From: Darrick J. Wong <djwong@xxxxxxxxxx> The VFS inc_nlink function does not explicitly check for integer overflows in the i_nlink field. Instead, it checks the link count against s_max_links in the vfs_{link,create,rename} functions. XFS sets the maximum link count to 2.1 billion, so integer overflows should not be a problem. However. It's possible that online repair could find that a file has more than four billion links, particularly if the link count got corrupted while creating hardlinks to the file. The di_nlinkv2 field is not large enough to store a value larger than 2^32, so we ought to define a magic pin value of ~0U which means that the inode never gets deleted. This will prevent a UAF error if the repair finds this situation and users begin deleting links to the file. Signed-off-by: Darrick J. Wong <djwong@xxxxxxxxxx> --- libxfs/util.c | 3 ++- libxfs/xfs_format.h | 6 ++++++ repair/incore_ino.c | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libxfs/util.c b/libxfs/util.c index 11978529ed6..03191ebcd08 100644 --- a/libxfs/util.c +++ b/libxfs/util.c @@ -252,7 +252,8 @@ libxfs_bumplink( xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); - inc_nlink(inode); + if (inode->i_nlink != XFS_NLINK_PINNED) + inc_nlink(inode); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); } diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h index 7861539ab8b..ec25010b577 100644 --- a/libxfs/xfs_format.h +++ b/libxfs/xfs_format.h @@ -912,6 +912,12 @@ static inline uint xfs_dinode_size(int version) */ #define XFS_MAXLINK ((1U << 31) - 1U) +/* + * Any file that hits the maximum ondisk link count should be pinned to avoid + * a use-after-free situation. + */ +#define XFS_NLINK_PINNED (~0U) + /* * Values for di_format * diff --git a/repair/incore_ino.c b/repair/incore_ino.c index 0dd7a2f060f..b0b41a2cc5c 100644 --- a/repair/incore_ino.c +++ b/repair/incore_ino.c @@ -108,7 +108,8 @@ void add_inode_ref(struct ino_tree_node *irec, int ino_offset) nlink_grow_16_to_32(irec); /*FALLTHRU*/ case sizeof(uint32_t): - irec->ino_un.ex_data->counted_nlinks.un32[ino_offset]++; + if (irec->ino_un.ex_data->counted_nlinks.un32[ino_offset] != XFS_NLINK_PINNED) + irec->ino_un.ex_data->counted_nlinks.un32[ino_offset]++; break; default: ASSERT(0);