mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
xfs: fix ondisk symlink target validation in xrep_dinode_check_dfork
LOLLM noticed that online repair of a broken symlink file could fail
unnecessarily if a local-format symlink target isn't null terminated.
The ondisk target isn't required to be null terminated, but repair
enforces that anyway because it uses the validator for the incore
symlink target. (The incore buffer is always null-terminated). Fix
this by reverting the changes to xfs_symlink_shortform_verify and adding
an ondisk-specific helper in inode_repair.c.
Cc: stable@vger.kernel.org # v6.8
Fixes: e744cef206 ("xfs: zap broken inode forks")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Assisted-by: LOLLM # finding obvious bugs
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dgc@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
parent
a800b92634
commit
aafe21c23a
|
|
@ -683,9 +683,7 @@ xfs_ifork_verify_local_data(
|
|||
break;
|
||||
}
|
||||
case S_IFLNK: {
|
||||
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
|
||||
|
||||
fa = xfs_symlink_shortform_verify(ifp->if_data, ifp->if_bytes);
|
||||
fa = xfs_symlink_shortform_verify(ip);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -208,11 +208,15 @@ xfs_symlink_local_to_remote(
|
|||
*/
|
||||
xfs_failaddr_t
|
||||
xfs_symlink_shortform_verify(
|
||||
void *sfp,
|
||||
int64_t size)
|
||||
struct xfs_inode *ip)
|
||||
{
|
||||
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
|
||||
char *sfp = (char *)ifp->if_data;
|
||||
int size = ifp->if_bytes;
|
||||
char *endp = sfp + size;
|
||||
|
||||
ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL);
|
||||
|
||||
/*
|
||||
* Zero length symlinks should never occur in memory as they are
|
||||
* never allowed to exist on disk.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ bool xfs_symlink_hdr_ok(xfs_ino_t ino, uint32_t offset,
|
|||
void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
|
||||
struct xfs_inode *ip, struct xfs_ifork *ifp,
|
||||
void *priv);
|
||||
xfs_failaddr_t xfs_symlink_shortform_verify(void *sfp, int64_t size);
|
||||
xfs_failaddr_t xfs_symlink_shortform_verify(struct xfs_inode *ip);
|
||||
int xfs_symlink_remote_read(struct xfs_inode *ip, char *link);
|
||||
int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip,
|
||||
xfs_ino_t owner, const char *target_path, int pathlen,
|
||||
|
|
|
|||
|
|
@ -1024,6 +1024,30 @@ xrep_dinode_bad_metabt_fork(
|
|||
return false;
|
||||
}
|
||||
|
||||
static xfs_failaddr_t
|
||||
xrep_symlink_shortform_verify(
|
||||
void *sfp,
|
||||
int64_t size)
|
||||
{
|
||||
/*
|
||||
* Zero length symlinks should never occur in memory as they are
|
||||
* never allowed to exist on disk.
|
||||
*/
|
||||
if (!size)
|
||||
return __this_address;
|
||||
|
||||
/* No negative sizes or overly long symlink targets. */
|
||||
if (size < 0 || size > XFS_SYMLINK_MAXLEN)
|
||||
return __this_address;
|
||||
|
||||
/* No NULLs in the target either. */
|
||||
if (memchr(sfp, 0, size))
|
||||
return __this_address;
|
||||
|
||||
/* ondisk symlink target isn't null terminated, unlike incore */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the data fork for things that will fail the ifork verifiers or the
|
||||
* ifork formatters.
|
||||
|
|
@ -1099,7 +1123,7 @@ xrep_dinode_check_dfork(
|
|||
return true;
|
||||
/* symlink structure must pass verification. */
|
||||
if (S_ISLNK(mode) &&
|
||||
xfs_symlink_shortform_verify(dfork_ptr, data_size) != NULL)
|
||||
xrep_symlink_shortform_verify(dfork_ptr, data_size) != NULL)
|
||||
return true;
|
||||
break;
|
||||
case XFS_DINODE_FMT_EXTENTS:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user