Skip to content

Commit 7b7820b

Browse files
author
Darrick J. Wong
committed
xfs: don't expose internal symlink metadata buffers to the vfs
Ian Kent reported that for inline symlinks, it's possible for vfs_readlink to hang on to the target buffer returned by _vn_get_link_inline long after it's been freed by xfs inode reclaim. This is a layering violation -- we should never expose XFS internals to the VFS. When the symlink has a remote target, we allocate a separate buffer, copy the internal information, and let the VFS manage the new buffer's lifetime. Let's adapt the inline code paths to do this too. It's less efficient, but fixes the layering violation and avoids the need to adapt the if_data lifetime to rcu rules. Clearly I don't care about readlink benchmarks. As a side note, this fixes the minor locking violation where we can access the inode data fork without taking any locks; proper locking (and eliminating the possibility of having to switch inode_operations on a live inode) is essential to online repair coordinating repairs correctly. Reported-by: Ian Kent <[email protected]> Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Dave Chinner <[email protected]>
1 parent 59d7fab commit 7b7820b

File tree

2 files changed

+20
-43
lines changed

2 files changed

+20
-43
lines changed

fs/xfs/xfs_iops.c

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -511,27 +511,6 @@ xfs_vn_get_link(
511511
return ERR_PTR(error);
512512
}
513513

514-
STATIC const char *
515-
xfs_vn_get_link_inline(
516-
struct dentry *dentry,
517-
struct inode *inode,
518-
struct delayed_call *done)
519-
{
520-
struct xfs_inode *ip = XFS_I(inode);
521-
char *link;
522-
523-
ASSERT(ip->i_df.if_format == XFS_DINODE_FMT_LOCAL);
524-
525-
/*
526-
* The VFS crashes on a NULL pointer, so return -EFSCORRUPTED if
527-
* if_data is junk.
528-
*/
529-
link = ip->i_df.if_u1.if_data;
530-
if (XFS_IS_CORRUPT(ip->i_mount, !link))
531-
return ERR_PTR(-EFSCORRUPTED);
532-
return link;
533-
}
534-
535514
static uint32_t
536515
xfs_stat_blksize(
537516
struct xfs_inode *ip)
@@ -1250,14 +1229,6 @@ static const struct inode_operations xfs_symlink_inode_operations = {
12501229
.update_time = xfs_vn_update_time,
12511230
};
12521231

1253-
static const struct inode_operations xfs_inline_symlink_inode_operations = {
1254-
.get_link = xfs_vn_get_link_inline,
1255-
.getattr = xfs_vn_getattr,
1256-
.setattr = xfs_vn_setattr,
1257-
.listxattr = xfs_vn_listxattr,
1258-
.update_time = xfs_vn_update_time,
1259-
};
1260-
12611232
/* Figure out if this file actually supports DAX. */
12621233
static bool
12631234
xfs_inode_supports_dax(
@@ -1408,10 +1379,7 @@ xfs_setup_iops(
14081379
inode->i_fop = &xfs_dir_file_operations;
14091380
break;
14101381
case S_IFLNK:
1411-
if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL)
1412-
inode->i_op = &xfs_inline_symlink_inode_operations;
1413-
else
1414-
inode->i_op = &xfs_symlink_inode_operations;
1382+
inode->i_op = &xfs_symlink_inode_operations;
14151383
break;
14161384
default:
14171385
inode->i_op = &xfs_inode_operations;

fs/xfs/xfs_symlink.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "xfs_trace.h"
2323
#include "xfs_trans.h"
2424
#include "xfs_ialloc.h"
25+
#include "xfs_error.h"
2526

2627
/* ----- Kernel only functions below ----- */
2728
int
@@ -96,17 +97,15 @@ xfs_readlink_bmap_ilocked(
9697

9798
int
9899
xfs_readlink(
99-
struct xfs_inode *ip,
100-
char *link)
100+
struct xfs_inode *ip,
101+
char *link)
101102
{
102-
struct xfs_mount *mp = ip->i_mount;
103-
xfs_fsize_t pathlen;
104-
int error = 0;
103+
struct xfs_mount *mp = ip->i_mount;
104+
xfs_fsize_t pathlen;
105+
int error = -EFSCORRUPTED;
105106

106107
trace_xfs_readlink(ip);
107108

108-
ASSERT(ip->i_df.if_format != XFS_DINODE_FMT_LOCAL);
109-
110109
if (xfs_is_shutdown(mp))
111110
return -EIO;
112111

@@ -121,12 +120,22 @@ xfs_readlink(
121120
__func__, (unsigned long long) ip->i_ino,
122121
(long long) pathlen);
123122
ASSERT(0);
124-
error = -EFSCORRUPTED;
125123
goto out;
126124
}
127125

128-
129-
error = xfs_readlink_bmap_ilocked(ip, link);
126+
if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
127+
/*
128+
* The VFS crashes on a NULL pointer, so return -EFSCORRUPTED
129+
* if if_data is junk.
130+
*/
131+
if (XFS_IS_CORRUPT(ip->i_mount, !ip->i_df.if_u1.if_data))
132+
goto out;
133+
134+
memcpy(link, ip->i_df.if_u1.if_data, pathlen + 1);
135+
error = 0;
136+
} else {
137+
error = xfs_readlink_bmap_ilocked(ip, link);
138+
}
130139

131140
out:
132141
xfs_iunlock(ip, XFS_ILOCK_SHARED);

0 commit comments

Comments
 (0)