xfs: add parent pointer validator functions

The attr name of a parent pointer is a string, and the attr value of a
parent pointer is (more or less) a file handle.  So we need to modify
attr_namecheck to verify the parent pointer name, and add a
xfs_parent_valuecheck function to sanitize the handle.  At the same
time, we need to validate attr values during log recovery if the xattr
is really a parent pointer.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move functions to xfs_parent.c, adjust for new disk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Allison Henderson 2024-04-22 09:47:44 -07:00 committed by Darrick J. Wong
parent 297da63379
commit a08d672963
5 changed files with 123 additions and 0 deletions

View file

@ -42,6 +42,7 @@ xfs-y += $(addprefix libxfs/, \
xfs_inode_buf.o \
xfs_log_rlimit.o \
xfs_ag_resv.o \
xfs_parent.o \
xfs_rmap.o \
xfs_rmap_btree.o \
xfs_refcount.o \

View file

@ -26,6 +26,7 @@
#include "xfs_trace.h"
#include "xfs_attr_item.h"
#include "xfs_xattr.h"
#include "xfs_parent.h"
struct kmem_cache *xfs_attr_intent_cache;
@ -1568,6 +1569,10 @@ xfs_attr_namecheck(
if (length >= MAXNAMELEN)
return false;
/* Parent pointers have their own validation. */
if (attr_flags & XFS_ATTR_PARENT)
return xfs_parent_namecheck(attr_flags, name, length);
/* There shouldn't be any nulls here */
return !memchr(name, 0, length);
}

View file

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022-2024 Oracle.
* All rights reserved.
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_format.h"
#include "xfs_da_format.h"
#include "xfs_log_format.h"
#include "xfs_shared.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_inode.h"
#include "xfs_error.h"
#include "xfs_trace.h"
#include "xfs_trans.h"
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_dir2.h"
#include "xfs_dir2_priv.h"
#include "xfs_attr_sf.h"
#include "xfs_bmap.h"
#include "xfs_defer.h"
#include "xfs_log.h"
#include "xfs_xattr.h"
#include "xfs_parent.h"
#include "xfs_trans_space.h"
/*
* Parent pointer attribute handling.
*
* Because the attribute name is a filename component, it will never be longer
* than 255 bytes and must not contain nulls or slashes. These are roughly the
* same constraints that apply to attribute names.
*
* The attribute value must always be a struct xfs_parent_rec. This means the
* attribute will never be in remote format because 12 bytes is nowhere near
* xfs_attr_leaf_entsize_local_max() (~75% of block size).
*
* Creating a new parent attribute will always create a new attribute - there
* should never, ever be an existing attribute in the tree for a new inode.
* ENOSPC behavior is problematic - creating the inode without the parent
* pointer is effectively a corruption, so we allow parent attribute creation
* to dip into the reserve block pool to avoid unexpected ENOSPC errors from
* occurring.
*/
/* Return true if parent pointer attr name is valid. */
bool
xfs_parent_namecheck(
unsigned int attr_flags,
const void *name,
size_t length)
{
/*
* Parent pointers always use logged operations, so there should never
* be incomplete xattrs.
*/
if (attr_flags & XFS_ATTR_INCOMPLETE)
return false;
return xfs_dir2_namecheck(name, length);
}
/* Return true if parent pointer attr value is valid. */
bool
xfs_parent_valuecheck(
struct xfs_mount *mp,
const void *value,
size_t valuelen)
{
const struct xfs_parent_rec *rec = value;
if (!xfs_has_parent(mp))
return false;
/* The xattr value must be a parent record. */
if (valuelen != sizeof(struct xfs_parent_rec))
return false;
/* The parent record must be local. */
if (value == NULL)
return false;
/* The parent inumber must be valid. */
if (!xfs_verify_dir_ino(mp, be64_to_cpu(rec->p_ino)))
return false;
return true;
}

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022-2024 Oracle.
* All Rights Reserved.
*/
#ifndef __XFS_PARENT_H__
#define __XFS_PARENT_H__
/* Metadata validators */
bool xfs_parent_namecheck(unsigned int attr_flags, const void *name,
size_t length);
bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
size_t valuelen);
#endif /* __XFS_PARENT_H__ */

View file

@ -27,6 +27,7 @@
#include "xfs_error.h"
#include "xfs_log_priv.h"
#include "xfs_log_recover.h"
#include "xfs_parent.h"
struct kmem_cache *xfs_attri_cache;
struct kmem_cache *xfs_attrd_cache;
@ -973,6 +974,15 @@ xfs_attri_validate_value_iovec(
return NULL;
}
if ((attri_formatp->alfi_attr_filter & XFS_ATTR_PARENT) &&
!xfs_parent_valuecheck(mp, iovec->i_addr, value_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, sizeof(*attri_formatp));
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
iovec->i_addr, iovec->i_len);
return NULL;
}
return iovec->i_addr;
}