Skip to content

Commit d7d02f7

Browse files
author
Chandan Babu R
committed
Merge tag 'improve-attr-validation-6.10_2024-04-23' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.10-mergeC
xfs: improve extended attribute validation Prior to introducing parent pointer extended attributes, let's spend some time cleaning up the attr code and strengthening the validation that it performs on attrs coming in from the disk. Signed-off-by: Darrick J. Wong <[email protected]> Signed-off-by: Chandan Babu R <[email protected]> * tag 'improve-attr-validation-6.10_2024-04-23' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: enforce one namespace per attribute xfs: refactor name/value iovec validation in xlog_recover_attri_commit_pass2 xfs: refactor name/length checks in xfs_attri_validate xfs: use local variables for name and value length in _attri_commit_pass2 xfs: always set args->value in xfs_attri_item_recover xfs: validate recovered name buffers when recovering xattr items xfs: use helpers to extract xattr op from opflags xfs: restructure xfs_attr_complete_op a bit xfs: check shortform attr entry flags specifically xfs: fix missing check for invalid attr flags xfs: check opcode and iovec count match in xlog_recover_attri_commit_pass2 xfs: use an XFS_OPSTATE_ flag for detecting if logged xattrs are available xfs: require XFS_SB_FEAT_INCOMPAT_LOG_XATTRS for attr log intent item recovery xfs: attr fork iext must be loaded before calling xfs_attr_is_leaf
2 parents 1321890 + ea0b3e8 commit d7d02f7

File tree

11 files changed

+299
-72
lines changed

11 files changed

+299
-72
lines changed

fs/xfs/libxfs/xfs_attr.c

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ xfs_attr_is_leaf(
8787
struct xfs_iext_cursor icur;
8888
struct xfs_bmbt_irec imap;
8989

90+
ASSERT(!xfs_need_iread_extents(ifp));
91+
9092
if (ifp->if_nextents != 1 || ifp->if_format != XFS_DINODE_FMT_EXTENTS)
9193
return false;
9294

@@ -224,11 +226,21 @@ int
224226
xfs_attr_get_ilocked(
225227
struct xfs_da_args *args)
226228
{
229+
int error;
230+
227231
xfs_assert_ilocked(args->dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL);
228232

229233
if (!xfs_inode_hasattr(args->dp))
230234
return -ENOATTR;
231235

236+
/*
237+
* The incore attr fork iext tree must be loaded for xfs_attr_is_leaf
238+
* to work correctly.
239+
*/
240+
error = xfs_iread_extents(args->trans, args->dp, XFS_ATTR_FORK);
241+
if (error)
242+
return error;
243+
232244
if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL)
233245
return xfs_attr_shortform_getvalue(args);
234246
if (xfs_attr_is_leaf(args->dp))
@@ -420,14 +432,13 @@ xfs_attr_complete_op(
420432
enum xfs_delattr_state replace_state)
421433
{
422434
struct xfs_da_args *args = attr->xattri_da_args;
423-
bool do_replace = args->op_flags & XFS_DA_OP_REPLACE;
435+
436+
if (!(args->op_flags & XFS_DA_OP_REPLACE))
437+
replace_state = XFS_DAS_DONE;
424438

425439
args->op_flags &= ~XFS_DA_OP_REPLACE;
426440
args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
427-
if (do_replace)
428-
return replace_state;
429-
430-
return XFS_DAS_DONE;
441+
return replace_state;
431442
}
432443

433444
static int
@@ -870,6 +881,11 @@ xfs_attr_lookup(
870881
return -ENOATTR;
871882
}
872883

884+
/* Prerequisite for xfs_attr_is_leaf */
885+
error = xfs_iread_extents(args->trans, args->dp, XFS_ATTR_FORK);
886+
if (error)
887+
return error;
888+
873889
if (xfs_attr_is_leaf(dp)) {
874890
error = xfs_attr_leaf_hasname(args, &bp);
875891

@@ -1516,12 +1532,23 @@ xfs_attr_node_get(
15161532
return error;
15171533
}
15181534

1535+
/* Enforce that there is at most one namespace bit per attr. */
1536+
inline bool xfs_attr_check_namespace(unsigned int attr_flags)
1537+
{
1538+
return hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) < 2;
1539+
}
1540+
15191541
/* Returns true if the attribute entry name is valid. */
15201542
bool
15211543
xfs_attr_namecheck(
1544+
unsigned int attr_flags,
15221545
const void *name,
15231546
size_t length)
15241547
{
1548+
/* Only one namespace bit allowed. */
1549+
if (!xfs_attr_check_namespace(attr_flags))
1550+
return false;
1551+
15251552
/*
15261553
* MAXNAMELEN includes the trailing null, but (name/length) leave it
15271554
* out, so use >= for the length check.

fs/xfs/libxfs/xfs_attr.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,11 @@ struct xfs_attr_intent {
529529
struct xfs_bmbt_irec xattri_map;
530530
};
531531

532+
static inline unsigned int
533+
xfs_attr_intent_op(const struct xfs_attr_intent *attr)
534+
{
535+
return attr->xattri_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
536+
}
532537

533538
/*========================================================================
534539
* Function prototypes for the kernel.
@@ -555,7 +560,9 @@ enum xfs_attr_update {
555560
int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
556561
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
557562
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
558-
bool xfs_attr_namecheck(const void *name, size_t length);
563+
bool xfs_attr_check_namespace(unsigned int attr_flags);
564+
bool xfs_attr_namecheck(unsigned int attr_flags, const void *name,
565+
size_t length);
559566
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
560567
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
561568
unsigned int *total);

fs/xfs/libxfs/xfs_attr_leaf.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,11 @@ xfs_attr_shortform_to_leaf(
950950
nargs.hashval = xfs_da_hashname(sfe->nameval,
951951
sfe->namelen);
952952
nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK;
953+
if (!xfs_attr_check_namespace(sfe->flags)) {
954+
xfs_da_mark_sick(args);
955+
error = -EFSCORRUPTED;
956+
goto out;
957+
}
953958
error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
954959
ASSERT(error == -ENOATTR);
955960
error = xfs_attr3_leaf_add(bp, &nargs);
@@ -1063,7 +1068,7 @@ xfs_attr_shortform_verify(
10631068
* one namespace flag per xattr, so we can just count the
10641069
* bits (i.e. hweight) here.
10651070
*/
1066-
if (hweight8(sfep->flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
1071+
if (!xfs_attr_check_namespace(sfep->flags))
10671072
return __this_address;
10681073

10691074
sfep = next_sfep;

fs/xfs/libxfs/xfs_da_format.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,13 @@ struct xfs_attr3_leafblock {
719719
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
720720
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
721721
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
722+
722723
#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
723724

725+
#define XFS_ATTR_ONDISK_MASK (XFS_ATTR_NSP_ONDISK_MASK | \
726+
XFS_ATTR_LOCAL | \
727+
XFS_ATTR_INCOMPLETE)
728+
724729
#define XFS_ATTR_NAMESPACE_STR \
725730
{ XFS_ATTR_LOCAL, "local" }, \
726731
{ XFS_ATTR_ROOT, "root" }, \

fs/xfs/scrub/attr.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -192,20 +192,19 @@ xchk_xattr_actor(
192192
if (xchk_should_terminate(sc, &error))
193193
return error;
194194

195+
if (attr_flags & ~XFS_ATTR_ONDISK_MASK) {
196+
xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);
197+
return -ECANCELED;
198+
}
199+
195200
if (attr_flags & XFS_ATTR_INCOMPLETE) {
196201
/* Incomplete attr key, just mark the inode for preening. */
197202
xchk_ino_set_preen(sc, ip->i_ino);
198203
return 0;
199204
}
200205

201-
/* Only one namespace bit allowed. */
202-
if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1) {
203-
xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);
204-
return -ECANCELED;
205-
}
206-
207206
/* Does this name make sense? */
208-
if (!xfs_attr_namecheck(name, namelen)) {
207+
if (!xfs_attr_namecheck(attr_flags, name, namelen)) {
209208
xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, args.blkno);
210209
return -ECANCELED;
211210
}
@@ -481,7 +480,6 @@ xchk_xattr_rec(
481480
xfs_dahash_t hash;
482481
int nameidx;
483482
int hdrsize;
484-
unsigned int badflags;
485483
int error;
486484

487485
ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
@@ -511,10 +509,15 @@ xchk_xattr_rec(
511509

512510
/* Retrieve the entry and check it. */
513511
hash = be32_to_cpu(ent->hashval);
514-
badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE |
515-
XFS_ATTR_INCOMPLETE);
516-
if ((ent->flags & badflags) != 0)
512+
if (ent->flags & ~XFS_ATTR_ONDISK_MASK) {
513+
xchk_da_set_corrupt(ds, level);
514+
return 0;
515+
}
516+
if (!xfs_attr_check_namespace(ent->flags)) {
517517
xchk_da_set_corrupt(ds, level);
518+
return 0;
519+
}
520+
518521
if (ent->flags & XFS_ATTR_LOCAL) {
519522
lentry = (struct xfs_attr_leaf_name_local *)
520523
(((char *)bp->b_addr) + nameidx);
@@ -574,6 +577,15 @@ xchk_xattr_check_sf(
574577
break;
575578
}
576579

580+
/*
581+
* Shortform entries do not set LOCAL or INCOMPLETE, so the
582+
* only valid flag bits here are for namespaces.
583+
*/
584+
if (sfe->flags & ~XFS_ATTR_NSP_ONDISK_MASK) {
585+
xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
586+
break;
587+
}
588+
577589
if (!xchk_xattr_set_map(sc, ab->usedmap,
578590
(char *)sfe - (char *)sf,
579591
sizeof(struct xfs_attr_sf_entry))) {

fs/xfs/scrub/attr_repair.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,10 @@ xrep_xattr_want_salvage(
123123
return false;
124124
if (namelen > XATTR_NAME_MAX || namelen <= 0)
125125
return false;
126-
if (!xfs_attr_namecheck(name, namelen))
126+
if (!xfs_attr_namecheck(attr_flags, name, namelen))
127127
return false;
128128
if (valuelen > XATTR_SIZE_MAX || valuelen < 0)
129129
return false;
130-
if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
131-
return false;
132130
return true;
133131
}
134132

0 commit comments

Comments
 (0)