Skip to content

Commit 6d200bd

Browse files
author
Darrick J. Wong
committed
Merge tag 'make-attr-fork-permanent-5.20_2022-07-14' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-5.20-mergeB
xfs: make attr forks permanent This series fixes a use-after-free bug that syzbot uncovered. The UAF itself is a result of a race condition between getxattr and removexattr because callers to getxattr do not necessarily take any sort of locks before calling into the filesystem. Although the race condition itself can be fixed through clever use of a memory barrier, further consideration of the use cases of extended attributes shows that most files always have at least one attribute, so we might as well make them permanent. v2: Minor tweaks suggested by Dave, and convert some more macros to helper functions. Signed-off-by: Darrick J. Wong <[email protected]> * tag 'make-attr-fork-permanent-5.20_2022-07-14' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: replace inode fork size macros with functions xfs: replace XFS_IFORK_Q with a proper predicate function xfs: use XFS_IFORK_Q to determine the presence of an xattr fork xfs: make inode attribute forks a permanent part of struct xfs_inode xfs: convert XFS_IFORK_PTR to a static inline helper
2 parents 35c5a09 + c01147d commit 6d200bd

35 files changed

+285
-247
lines changed

fs/xfs/libxfs/xfs_attr.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,10 @@ int
6767
xfs_inode_hasattr(
6868
struct xfs_inode *ip)
6969
{
70-
if (!XFS_IFORK_Q(ip))
70+
if (!xfs_inode_has_attr_fork(ip))
7171
return 0;
72-
if (!ip->i_afp)
73-
return 0;
74-
if (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS &&
75-
ip->i_afp->if_nextents == 0)
72+
if (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
73+
ip->i_af.if_nextents == 0)
7674
return 0;
7775
return 1;
7876
}
@@ -85,7 +83,7 @@ bool
8583
xfs_attr_is_leaf(
8684
struct xfs_inode *ip)
8785
{
88-
struct xfs_ifork *ifp = ip->i_afp;
86+
struct xfs_ifork *ifp = &ip->i_af;
8987
struct xfs_iext_cursor icur;
9088
struct xfs_bmbt_irec imap;
9189

@@ -231,7 +229,7 @@ xfs_attr_get_ilocked(
231229
if (!xfs_inode_hasattr(args->dp))
232230
return -ENOATTR;
233231

234-
if (args->dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL)
232+
if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL)
235233
return xfs_attr_shortform_getvalue(args);
236234
if (xfs_attr_is_leaf(args->dp))
237235
return xfs_attr_leaf_get(args);
@@ -354,7 +352,7 @@ xfs_attr_try_sf_addname(
354352
/*
355353
* Build initial attribute list (if required).
356354
*/
357-
if (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS)
355+
if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
358356
xfs_attr_shortform_create(args);
359357

360358
error = xfs_attr_shortform_addname(args);
@@ -864,7 +862,7 @@ xfs_attr_lookup(
864862
if (!xfs_inode_hasattr(dp))
865863
return -ENOATTR;
866864

867-
if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL)
865+
if (dp->i_af.if_format == XFS_DINODE_FMT_LOCAL)
868866
return xfs_attr_sf_findname(args, NULL, NULL);
869867

870868
if (xfs_attr_is_leaf(dp)) {
@@ -1001,7 +999,7 @@ xfs_attr_set(
1001999
* If the inode doesn't have an attribute fork, add one.
10021000
* (inode must not be locked when we call this routine)
10031001
*/
1004-
if (XFS_IFORK_Q(dp) == 0) {
1002+
if (xfs_inode_has_attr_fork(dp) == 0) {
10051003
int sf_size = sizeof(struct xfs_attr_sf_hdr) +
10061004
xfs_attr_sf_entsize_byname(args->namelen,
10071005
args->valuelen);
@@ -1101,7 +1099,7 @@ static inline int xfs_attr_sf_totsize(struct xfs_inode *dp)
11011099
{
11021100
struct xfs_attr_shortform *sf;
11031101

1104-
sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data;
1102+
sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data;
11051103
return be16_to_cpu(sf->hdr.totsize);
11061104
}
11071105

fs/xfs/libxfs/xfs_attr.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,9 @@ static inline bool
560560
xfs_attr_is_shortform(
561561
struct xfs_inode *ip)
562562
{
563-
return ip->i_afp->if_format == XFS_DINODE_FMT_LOCAL ||
564-
(ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS &&
565-
ip->i_afp->if_nextents == 0);
563+
return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL ||
564+
(ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
565+
ip->i_af.if_nextents == 0);
566566
}
567567

568568
static inline enum xfs_delattr_state
@@ -573,10 +573,10 @@ xfs_attr_init_add_state(struct xfs_da_args *args)
573573
* next state, the attribute fork may be null. This can occur only occur
574574
* on a pure remove, but we grab the next state before we check if a
575575
* replace operation is being performed. If we are called from any other
576-
* context, i_afp is guaranteed to exist. Hence if the attr fork is
576+
* context, i_af is guaranteed to exist. Hence if the attr fork is
577577
* null, we were called from a pure remove operation and so we are done.
578578
*/
579-
if (!args->dp->i_afp)
579+
if (!xfs_inode_has_attr_fork(args->dp))
580580
return XFS_DAS_DONE;
581581

582582
args->op_flags |= XFS_DA_OP_ADDNAME;

fs/xfs/libxfs/xfs_attr_leaf.c

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ xfs_attr_shortform_bytesfit(
590590
* to real extents, or the delalloc conversion will take care of the
591591
* literal area rebalancing.
592592
*/
593-
if (bytes <= XFS_IFORK_ASIZE(dp))
593+
if (bytes <= xfs_inode_attr_fork_size(dp))
594594
return dp->i_forkoff;
595595

596596
/*
@@ -682,7 +682,7 @@ xfs_attr_shortform_create(
682682
struct xfs_da_args *args)
683683
{
684684
struct xfs_inode *dp = args->dp;
685-
struct xfs_ifork *ifp = dp->i_afp;
685+
struct xfs_ifork *ifp = &dp->i_af;
686686
struct xfs_attr_sf_hdr *hdr;
687687

688688
trace_xfs_attr_sf_create(args);
@@ -719,7 +719,7 @@ xfs_attr_sf_findname(
719719
int end;
720720
int i;
721721

722-
sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data;
722+
sf = (struct xfs_attr_shortform *)args->dp->i_af.if_u1.if_data;
723723
sfe = &sf->list[0];
724724
end = sf->hdr.count;
725725
for (i = 0; i < end; sfe = xfs_attr_sf_nextentry(sfe),
@@ -764,7 +764,7 @@ xfs_attr_shortform_add(
764764
mp = dp->i_mount;
765765
dp->i_forkoff = forkoff;
766766

767-
ifp = dp->i_afp;
767+
ifp = &dp->i_af;
768768
ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
769769
sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
770770
if (xfs_attr_sf_findname(args, &sfe, NULL) == -EEXIST)
@@ -797,11 +797,10 @@ xfs_attr_fork_remove(
797797
struct xfs_inode *ip,
798798
struct xfs_trans *tp)
799799
{
800-
ASSERT(ip->i_afp->if_nextents == 0);
800+
ASSERT(ip->i_af.if_nextents == 0);
801801

802-
xfs_idestroy_fork(ip->i_afp);
803-
kmem_cache_free(xfs_ifork_cache, ip->i_afp);
804-
ip->i_afp = NULL;
802+
xfs_idestroy_fork(&ip->i_af);
803+
xfs_ifork_zap_attr(ip);
805804
ip->i_forkoff = 0;
806805
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
807806
}
@@ -825,7 +824,7 @@ xfs_attr_sf_removename(
825824

826825
dp = args->dp;
827826
mp = dp->i_mount;
828-
sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data;
827+
sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data;
829828

830829
error = xfs_attr_sf_findname(args, &sfe, &base);
831830

@@ -889,7 +888,7 @@ xfs_attr_shortform_lookup(xfs_da_args_t *args)
889888

890889
trace_xfs_attr_sf_lookup(args);
891890

892-
ifp = args->dp->i_afp;
891+
ifp = &args->dp->i_af;
893892
ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
894893
sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
895894
sfe = &sf->list[0];
@@ -917,8 +916,8 @@ xfs_attr_shortform_getvalue(
917916
struct xfs_attr_sf_entry *sfe;
918917
int i;
919918

920-
ASSERT(args->dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL);
921-
sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data;
919+
ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL);
920+
sf = (struct xfs_attr_shortform *)args->dp->i_af.if_u1.if_data;
922921
sfe = &sf->list[0];
923922
for (i = 0; i < sf->hdr.count;
924923
sfe = xfs_attr_sf_nextentry(sfe), i++) {
@@ -948,7 +947,7 @@ xfs_attr_shortform_to_leaf(
948947
trace_xfs_attr_sf_to_leaf(args);
949948

950949
dp = args->dp;
951-
ifp = dp->i_afp;
950+
ifp = &dp->i_af;
952951
sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
953952
size = be16_to_cpu(sf->hdr.totsize);
954953
tmpbuffer = kmem_alloc(size, 0);
@@ -1055,8 +1054,8 @@ xfs_attr_shortform_verify(
10551054
int i;
10561055
int64_t size;
10571056

1058-
ASSERT(ip->i_afp->if_format == XFS_DINODE_FMT_LOCAL);
1059-
ifp = XFS_IFORK_PTR(ip, XFS_ATTR_FORK);
1057+
ASSERT(ip->i_af.if_format == XFS_DINODE_FMT_LOCAL);
1058+
ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK);
10601059
sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data;
10611060
size = ifp->if_bytes;
10621061

0 commit comments

Comments
 (0)