fs/ocfs2/xattr.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
Add comprehensive validation of inline xattr metadata in
ocfs2_xattr_ibody_list() to prevent out-of-bounds access and
use-after-free bugs when processing corrupted inline xattrs.
The patch adds two critical validations:
1. Validates i_xattr_inline_size before use:
- Ensures it does not exceed block size
- Ensures it is at least large enough for xattr header
- Prevents pointer arithmetic with corrupted size values that could
point outside the inode block
2. Validates xattr entry count (xh_count):
- Calculates maximum entries that can fit in the inline space
- Rejects counts that exceed this limit
- Prevents out-of-bounds array access in subsequent code
Without these checks, a corrupted filesystem with invalid inline xattr
metadata can cause the code to access memory beyond the allocated space.
For example:
- A corrupted i_xattr_inline_size of 0 would cause header pointer
calculation to point past the end of the block
- A corrupted xh_count of 22 with inline_size of 256 would cause
array access 7 entries beyond the 15 that actually fit (the syzbot
reproducer used xh_count of 20041), leading to use-after-free when
accessing freed memory pages
The validation uses the correct inline_size (from di->i_xattr_inline_size)
rather than block size, ensuring accurate bounds checking for inline
xattrs specifically.
Reported-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=ab0ad25088673470d2d9
Tested-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
Suggested-by: Heming Zhao <heming.zhao@suse.com>
Link: https://lore.kernel.org/all/20251111073831.2027072-1-kartikey406@gmail.com/ [v1]
Link: https://lore.kernel.org/all/20251117063217.5690-1-kartikey406@gmail.com/ [v2]
Link: https://lore.kernel.org/all/20251117114224.12948-1-kartikey406@gmail.com/ [v3]
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
Changes in v4:
- Corrected commit message example: max entries is 15, not 7
(pointed out by Heming Zhao)
Changes in v3:
- Moved validation from ocfs2_xattr_list_entries() to
ocfs2_xattr_ibody_list() to use correct inline size calculation
(suggested by Heming Zhao)
- Added validation of i_xattr_inline_size before use
- Added validation of xattr entry count against inline space
- Changed return value to -EFSCORRUPTED for consistency
---
fs/ocfs2/xattr.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index d70a20d29e3e..98fd4f3f2d2d 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -971,13 +971,39 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
struct ocfs2_xattr_header *header = NULL;
struct ocfs2_inode_info *oi = OCFS2_I(inode);
int ret = 0;
+ u16 xattr_count;
+ size_t max_entries;
+ u16 inline_size;
if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
return ret;
+ inline_size = le16_to_cpu(di->i_xattr_inline_size);
+
+ /* Validate inline size is reasonable */
+ if (inline_size > inode->i_sb->s_blocksize ||
+ inline_size < sizeof(struct ocfs2_xattr_header)) {
+ ocfs2_error(inode->i_sb,
+ "Invalid xattr inline size %u in inode %llu\n",
+ inline_size,
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
+ return -EFSCORRUPTED;
+ }
+
header = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ ((void *)di + inode->i_sb->s_blocksize - inline_size);
+
+ xattr_count = le16_to_cpu(header->xh_count);
+ max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
+ sizeof(struct ocfs2_xattr_entry);
+
+ if (xattr_count > max_entries) {
+ ocfs2_error(inode->i_sb,
+ "xattr entry count %u exceeds maximum %zu in inode %llu\n",
+ xattr_count, max_entries,
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
+ return -EFSCORRUPTED;
+ }
ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
--
2.43.0
On Thu, Nov 20, 2025 at 09:41:45AM +0530, Deepanshu Kartikey wrote:
> Add comprehensive validation of inline xattr metadata in
> ocfs2_xattr_ibody_list() to prevent out-of-bounds access and
> use-after-free bugs when processing corrupted inline xattrs.
>
> The patch adds two critical validations:
>
> 1. Validates i_xattr_inline_size before use:
> - Ensures it does not exceed block size
> - Ensures it is at least large enough for xattr header
> - Prevents pointer arithmetic with corrupted size values that could
> point outside the inode block
>
> 2. Validates xattr entry count (xh_count):
> - Calculates maximum entries that can fit in the inline space
> - Rejects counts that exceed this limit
> - Prevents out-of-bounds array access in subsequent code
>
> Without these checks, a corrupted filesystem with invalid inline xattr
> metadata can cause the code to access memory beyond the allocated space.
> For example:
> - A corrupted i_xattr_inline_size of 0 would cause header pointer
> calculation to point past the end of the block
> - A corrupted xh_count of 22 with inline_size of 256 would cause
> array access 7 entries beyond the 15 that actually fit (the syzbot
> reproducer used xh_count of 20041), leading to use-after-free when
> accessing freed memory pages
>
> The validation uses the correct inline_size (from di->i_xattr_inline_size)
> rather than block size, ensuring accurate bounds checking for inline
> xattrs specifically.
>
> Reported-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=ab0ad25088673470d2d9
> Tested-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
> Suggested-by: Heming Zhao <heming.zhao@suse.com>
> Link: https://lore.kernel.org/all/20251111073831.2027072-1-kartikey406@gmail.com/ [v1]
> Link: https://lore.kernel.org/all/20251117063217.5690-1-kartikey406@gmail.com/ [v2]
> Link: https://lore.kernel.org/all/20251117114224.12948-1-kartikey406@gmail.com/ [v3]
> Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
LGTM
Reviewed-by: Heming Zhao <heming.zhao@suse.com>
> ---
> Changes in v4:
> - Corrected commit message example: max entries is 15, not 7
> (pointed out by Heming Zhao)
>
> Changes in v3:
> - Moved validation from ocfs2_xattr_list_entries() to
> ocfs2_xattr_ibody_list() to use correct inline size calculation
> (suggested by Heming Zhao)
> - Added validation of i_xattr_inline_size before use
> - Added validation of xattr entry count against inline space
> - Changed return value to -EFSCORRUPTED for consistency
> ---
> fs/ocfs2/xattr.c | 30 ++++++++++++++++++++++++++++--
> 1 file changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index d70a20d29e3e..98fd4f3f2d2d 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -971,13 +971,39 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
> struct ocfs2_xattr_header *header = NULL;
> struct ocfs2_inode_info *oi = OCFS2_I(inode);
> int ret = 0;
> + u16 xattr_count;
> + size_t max_entries;
> + u16 inline_size;
>
> if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
> return ret;
>
> + inline_size = le16_to_cpu(di->i_xattr_inline_size);
> +
> + /* Validate inline size is reasonable */
> + if (inline_size > inode->i_sb->s_blocksize ||
> + inline_size < sizeof(struct ocfs2_xattr_header)) {
> + ocfs2_error(inode->i_sb,
> + "Invalid xattr inline size %u in inode %llu\n",
> + inline_size,
> + (unsigned long long)OCFS2_I(inode)->ip_blkno);
> + return -EFSCORRUPTED;
> + }
> +
> header = (struct ocfs2_xattr_header *)
> - ((void *)di + inode->i_sb->s_blocksize -
> - le16_to_cpu(di->i_xattr_inline_size));
> + ((void *)di + inode->i_sb->s_blocksize - inline_size);
> +
> + xattr_count = le16_to_cpu(header->xh_count);
> + max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
> + sizeof(struct ocfs2_xattr_entry);
> +
> + if (xattr_count > max_entries) {
> + ocfs2_error(inode->i_sb,
> + "xattr entry count %u exceeds maximum %zu in inode %llu\n",
> + xattr_count, max_entries,
> + (unsigned long long)OCFS2_I(inode)->ip_blkno);
> + return -EFSCORRUPTED;
> + }
>
> ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
>
> --
> 2.43.0
>
On 2025/11/20 14:49, Heming Zhao wrote:
> On Thu, Nov 20, 2025 at 09:41:45AM +0530, Deepanshu Kartikey wrote:
>> Add comprehensive validation of inline xattr metadata in
>> ocfs2_xattr_ibody_list() to prevent out-of-bounds access and
>> use-after-free bugs when processing corrupted inline xattrs.
>>
>> The patch adds two critical validations:
>>
>> 1. Validates i_xattr_inline_size before use:
>> - Ensures it does not exceed block size
>> - Ensures it is at least large enough for xattr header
>> - Prevents pointer arithmetic with corrupted size values that could
>> point outside the inode block
>>
>> 2. Validates xattr entry count (xh_count):
>> - Calculates maximum entries that can fit in the inline space
>> - Rejects counts that exceed this limit
>> - Prevents out-of-bounds array access in subsequent code
>>
>> Without these checks, a corrupted filesystem with invalid inline xattr
>> metadata can cause the code to access memory beyond the allocated space.
>> For example:
>> - A corrupted i_xattr_inline_size of 0 would cause header pointer
>> calculation to point past the end of the block
>> - A corrupted xh_count of 22 with inline_size of 256 would cause
>> array access 7 entries beyond the 15 that actually fit (the syzbot
>> reproducer used xh_count of 20041), leading to use-after-free when
>> accessing freed memory pages
>>
>> The validation uses the correct inline_size (from di->i_xattr_inline_size)
>> rather than block size, ensuring accurate bounds checking for inline
>> xattrs specifically.
>>
>> Reported-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
>> Closes: https://syzkaller.appspot.com/bug?extid=ab0ad25088673470d2d9
>> Tested-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
>> Suggested-by: Heming Zhao <heming.zhao@suse.com>
>> Link: https://lore.kernel.org/all/20251111073831.2027072-1-kartikey406@gmail.com/ [v1]
>> Link: https://lore.kernel.org/all/20251117063217.5690-1-kartikey406@gmail.com/ [v2]
>> Link: https://lore.kernel.org/all/20251117114224.12948-1-kartikey406@gmail.com/ [v3]
>> Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
>
> LGTM
> Reviewed-by: Heming Zhao <heming.zhao@suse.com>
Acked-by: Joseph Qi <joseph.qi@linux.alibaba.com>
>
>> ---
>> Changes in v4:
>> - Corrected commit message example: max entries is 15, not 7
>> (pointed out by Heming Zhao)
>>
>> Changes in v3:
>> - Moved validation from ocfs2_xattr_list_entries() to
>> ocfs2_xattr_ibody_list() to use correct inline size calculation
>> (suggested by Heming Zhao)
>> - Added validation of i_xattr_inline_size before use
>> - Added validation of xattr entry count against inline space
>> - Changed return value to -EFSCORRUPTED for consistency
>> ---
>> fs/ocfs2/xattr.c | 30 ++++++++++++++++++++++++++++--
>> 1 file changed, 28 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
>> index d70a20d29e3e..98fd4f3f2d2d 100644
>> --- a/fs/ocfs2/xattr.c
>> +++ b/fs/ocfs2/xattr.c
>> @@ -971,13 +971,39 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
>> struct ocfs2_xattr_header *header = NULL;
>> struct ocfs2_inode_info *oi = OCFS2_I(inode);
>> int ret = 0;
>> + u16 xattr_count;
>> + size_t max_entries;
>> + u16 inline_size;
>>
>> if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
>> return ret;
>>
>> + inline_size = le16_to_cpu(di->i_xattr_inline_size);
>> +
>> + /* Validate inline size is reasonable */
>> + if (inline_size > inode->i_sb->s_blocksize ||
>> + inline_size < sizeof(struct ocfs2_xattr_header)) {
>> + ocfs2_error(inode->i_sb,
>> + "Invalid xattr inline size %u in inode %llu\n",
>> + inline_size,
>> + (unsigned long long)OCFS2_I(inode)->ip_blkno);
>> + return -EFSCORRUPTED;
>> + }
>> +
>> header = (struct ocfs2_xattr_header *)
>> - ((void *)di + inode->i_sb->s_blocksize -
>> - le16_to_cpu(di->i_xattr_inline_size));
>> + ((void *)di + inode->i_sb->s_blocksize - inline_size);
>> +
>> + xattr_count = le16_to_cpu(header->xh_count);
>> + max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
>> + sizeof(struct ocfs2_xattr_entry);
>> +
>> + if (xattr_count > max_entries) {
>> + ocfs2_error(inode->i_sb,
>> + "xattr entry count %u exceeds maximum %zu in inode %llu\n",
>> + xattr_count, max_entries,
>> + (unsigned long long)OCFS2_I(inode)->ip_blkno);
>> + return -EFSCORRUPTED;
>> + }
>>
>> ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
>>
>> --
>> 2.43.0
>>
On Thu, Nov 20, 2025 at 12:19 PM Heming Zhao <heming.zhao@suse.com> wrote:
>
> On Thu, Nov 20, 2025 at 09:41:45AM +0530, Deepanshu Kartikey wrote:
> > Add comprehensive validation of inline xattr metadata in
> > ocfs2_xattr_ibody_list() to prevent out-of-bounds access and
> > use-after-free bugs when processing corrupted inline xattrs.
> >
> > The patch adds two critical validations:
> >
> > 1. Validates i_xattr_inline_size before use:
> > - Ensures it does not exceed block size
> > - Ensures it is at least large enough for xattr header
> > - Prevents pointer arithmetic with corrupted size values that could
> > point outside the inode block
> >
> > 2. Validates xattr entry count (xh_count):
> > - Calculates maximum entries that can fit in the inline space
> > - Rejects counts that exceed this limit
> > - Prevents out-of-bounds array access in subsequent code
> >
> > Without these checks, a corrupted filesystem with invalid inline xattr
> > metadata can cause the code to access memory beyond the allocated space.
> > For example:
> > - A corrupted i_xattr_inline_size of 0 would cause header pointer
> > calculation to point past the end of the block
> > - A corrupted xh_count of 22 with inline_size of 256 would cause
> > array access 7 entries beyond the 15 that actually fit (the syzbot
> > reproducer used xh_count of 20041), leading to use-after-free when
> > accessing freed memory pages
> >
> > The validation uses the correct inline_size (from di->i_xattr_inline_size)
> > rather than block size, ensuring accurate bounds checking for inline
> > xattrs specifically.
> >
> > Reported-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
> > Closes: https://syzkaller.appspot.com/bug?extid=ab0ad25088673470d2d9
> > Tested-by: syzbot+ab0ad25088673470d2d9@syzkaller.appspotmail.com
> > Suggested-by: Heming Zhao <heming.zhao@suse.com>
> > Link: https://lore.kernel.org/all/20251111073831.2027072-1-kartikey406@gmail.com/ [v1]
> > Link: https://lore.kernel.org/all/20251117063217.5690-1-kartikey406@gmail.com/ [v2]
> > Link: https://lore.kernel.org/all/20251117114224.12948-1-kartikey406@gmail.com/ [v3]
> > Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
>
> LGTM
> Reviewed-by: Heming Zhao <heming.zhao@suse.com>
>
> > ---
> > Changes in v4:
> > - Corrected commit message example: max entries is 15, not 7
> > (pointed out by Heming Zhao)
> >
> > Changes in v3:
> > - Moved validation from ocfs2_xattr_list_entries() to
> > ocfs2_xattr_ibody_list() to use correct inline size calculation
> > (suggested by Heming Zhao)
> > - Added validation of i_xattr_inline_size before use
> > - Added validation of xattr entry count against inline space
> > - Changed return value to -EFSCORRUPTED for consistency
> > ---
> > fs/ocfs2/xattr.c | 30 ++++++++++++++++++++++++++++--
> > 1 file changed, 28 insertions(+), 2 deletions(-)
> >
> > diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> > index d70a20d29e3e..98fd4f3f2d2d 100644
> > --- a/fs/ocfs2/xattr.c
> > +++ b/fs/ocfs2/xattr.c
> > @@ -971,13 +971,39 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
> > struct ocfs2_xattr_header *header = NULL;
> > struct ocfs2_inode_info *oi = OCFS2_I(inode);
> > int ret = 0;
> > + u16 xattr_count;
> > + size_t max_entries;
> > + u16 inline_size;
> >
> > if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
> > return ret;
> >
> > + inline_size = le16_to_cpu(di->i_xattr_inline_size);
> > +
> > + /* Validate inline size is reasonable */
> > + if (inline_size > inode->i_sb->s_blocksize ||
> > + inline_size < sizeof(struct ocfs2_xattr_header)) {
> > + ocfs2_error(inode->i_sb,
> > + "Invalid xattr inline size %u in inode %llu\n",
> > + inline_size,
> > + (unsigned long long)OCFS2_I(inode)->ip_blkno);
> > + return -EFSCORRUPTED;
> > + }
> > +
> > header = (struct ocfs2_xattr_header *)
> > - ((void *)di + inode->i_sb->s_blocksize -
> > - le16_to_cpu(di->i_xattr_inline_size));
> > + ((void *)di + inode->i_sb->s_blocksize - inline_size);
> > +
> > + xattr_count = le16_to_cpu(header->xh_count);
> > + max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
> > + sizeof(struct ocfs2_xattr_entry);
> > +
> > + if (xattr_count > max_entries) {
> > + ocfs2_error(inode->i_sb,
> > + "xattr entry count %u exceeds maximum %zu in inode %llu\n",
> > + xattr_count, max_entries,
> > + (unsigned long long)OCFS2_I(inode)->ip_blkno);
> > + return -EFSCORRUPTED;
> > + }
> >
> > ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
> >
> > --
> > 2.43.0
> >
Hi Joseph, Mark, Joel,
Gentle ping on this patch. It has been reviewed and approved by Heming:
Reviewed-by: Heming Zhao <heming.zhao@suse.com>
This patch fixes a syzbot-reported use-after-free bug in OCFS2 inline
xattr handling. The fix adds validation of i_xattr_inline_size and
xh_count before use to prevent out-of-bounds access.
Should I resend with the Reviewed-by tag included, or is there anything
else needed for this to be picked up?
Thanks,
Deepanshu
Link: https://lore.kernel.org/all/20251120041145.33176-1-kartikey406@gmail.com/T/
[v4]
© 2016 - 2025 Red Hat, Inc.