Skip to content

Commit bd76a2b

Browse files
committed
ext4: improve xattr consistency checking and error reporting
jira VULN-65380 cve-pre CVE-2025-22121 commit-author Theodore Ts'o <tytso@mit.edu> commit 3478c83 Refactor the in-inode and xattr block consistency checking, and report more fine-grained reports of the consistency problems. Also add more consistency checks for ea_inode number. Reviewed-by: Andreas Dilger <adilger@dilger.ca> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Link: https://lore.kernel.org/r/20221214200818.870087-1-tytso@mit.edu Signed-off-by: Theodore Ts'o <tytso@mit.edu> (cherry picked from commit 3478c83) Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
1 parent 8542ab9 commit bd76a2b

File tree

1 file changed

+80
-46
lines changed

1 file changed

+80
-46
lines changed

fs/ext4/xattr.c

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -182,27 +182,73 @@ ext4_xattr_handler(int name_index)
182182
}
183183

184184
static int
185-
ext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end,
186-
void *value_start)
185+
check_xattrs(struct inode *inode, struct buffer_head *bh,
186+
struct ext4_xattr_entry *entry, void *end, void *value_start,
187+
const char *function, unsigned int line)
187188
{
188189
struct ext4_xattr_entry *e = entry;
190+
int err = -EFSCORRUPTED;
191+
char *err_str;
192+
193+
if (bh) {
194+
if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
195+
BHDR(bh)->h_blocks != cpu_to_le32(1)) {
196+
err_str = "invalid header";
197+
goto errout;
198+
}
199+
if (buffer_verified(bh))
200+
return 0;
201+
if (!ext4_xattr_block_csum_verify(inode, bh)) {
202+
err = -EFSBADCRC;
203+
err_str = "invalid checksum";
204+
goto errout;
205+
}
206+
} else {
207+
struct ext4_xattr_ibody_header *header = value_start;
208+
209+
header -= 1;
210+
if (end - (void *)header < sizeof(*header) + sizeof(u32)) {
211+
err_str = "in-inode xattr block too small";
212+
goto errout;
213+
}
214+
if (header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
215+
err_str = "bad magic number in in-inode xattr";
216+
goto errout;
217+
}
218+
}
189219

190220
/* Find the end of the names list */
191221
while (!IS_LAST_ENTRY(e)) {
192222
struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e);
193-
if ((void *)next >= end)
194-
return -EFSCORRUPTED;
195-
if (strnlen(e->e_name, e->e_name_len) != e->e_name_len)
196-
return -EFSCORRUPTED;
223+
if ((void *)next >= end) {
224+
err_str = "e_name out of bounds";
225+
goto errout;
226+
}
227+
if (strnlen(e->e_name, e->e_name_len) != e->e_name_len) {
228+
err_str = "bad e_name length";
229+
goto errout;
230+
}
197231
e = next;
198232
}
199233

200234
/* Check the values */
201235
while (!IS_LAST_ENTRY(entry)) {
202236
u32 size = le32_to_cpu(entry->e_value_size);
237+
unsigned long ea_ino = le32_to_cpu(entry->e_value_inum);
203238

204-
if (size > EXT4_XATTR_SIZE_MAX)
205-
return -EFSCORRUPTED;
239+
if (!ext4_has_feature_ea_inode(inode->i_sb) && ea_ino) {
240+
err_str = "ea_inode specified without ea_inode feature enabled";
241+
goto errout;
242+
}
243+
if (ea_ino && ((ea_ino == EXT4_ROOT_INO) ||
244+
!ext4_valid_inum(inode->i_sb, ea_ino))) {
245+
err_str = "invalid ea_ino";
246+
goto errout;
247+
}
248+
if (size > EXT4_XATTR_SIZE_MAX) {
249+
err_str = "e_value size too large";
250+
goto errout;
251+
}
206252

207253
if (size != 0 && entry->e_value_inum == 0) {
208254
u16 offs = le16_to_cpu(entry->e_value_offs);
@@ -214,66 +260,54 @@ ext4_xattr_check_entries(struct ext4_xattr_entry *entry, void *end,
214260
* the padded and unpadded sizes, since the size may
215261
* overflow to 0 when adding padding.
216262
*/
217-
if (offs > end - value_start)
218-
return -EFSCORRUPTED;
263+
if (offs > end - value_start) {
264+
err_str = "e_value out of bounds";
265+
goto errout;
266+
}
219267
value = value_start + offs;
220268
if (value < (void *)e + sizeof(u32) ||
221269
size > end - value ||
222-
EXT4_XATTR_SIZE(size) > end - value)
223-
return -EFSCORRUPTED;
270+
EXT4_XATTR_SIZE(size) > end - value) {
271+
err_str = "overlapping e_value ";
272+
goto errout;
273+
}
224274
}
225275
entry = EXT4_XATTR_NEXT(entry);
226276
}
227-
277+
if (bh)
278+
set_buffer_verified(bh);
228279
return 0;
280+
281+
errout:
282+
if (bh)
283+
__ext4_error_inode(inode, function, line, 0, -err,
284+
"corrupted xattr block %llu: %s",
285+
(unsigned long long) bh->b_blocknr,
286+
err_str);
287+
else
288+
__ext4_error_inode(inode, function, line, 0, -err,
289+
"corrupted in-inode xattr: %s", err_str);
290+
return err;
229291
}
230292

231293
static inline int
232294
__ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh,
233295
const char *function, unsigned int line)
234296
{
235-
int error = -EFSCORRUPTED;
236-
237-
if (BHDR(bh)->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC) ||
238-
BHDR(bh)->h_blocks != cpu_to_le32(1))
239-
goto errout;
240-
if (buffer_verified(bh))
241-
return 0;
242-
243-
error = -EFSBADCRC;
244-
if (!ext4_xattr_block_csum_verify(inode, bh))
245-
goto errout;
246-
error = ext4_xattr_check_entries(BFIRST(bh), bh->b_data + bh->b_size,
247-
bh->b_data);
248-
errout:
249-
if (error)
250-
__ext4_error_inode(inode, function, line, 0, -error,
251-
"corrupted xattr block %llu",
252-
(unsigned long long) bh->b_blocknr);
253-
else
254-
set_buffer_verified(bh);
255-
return error;
297+
return check_xattrs(inode, bh, BFIRST(bh), bh->b_data + bh->b_size,
298+
bh->b_data, function, line);
256299
}
257300

258301
#define ext4_xattr_check_block(inode, bh) \
259302
__ext4_xattr_check_block((inode), (bh), __func__, __LINE__)
260303

261304

262-
static int
305+
static inline int
263306
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
264307
void *end, const char *function, unsigned int line)
265308
{
266-
int error = -EFSCORRUPTED;
267-
268-
if (end - (void *)header < sizeof(*header) + sizeof(u32) ||
269-
(header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)))
270-
goto errout;
271-
error = ext4_xattr_check_entries(IFIRST(header), end, IFIRST(header));
272-
errout:
273-
if (error)
274-
__ext4_error_inode(inode, function, line, 0, -error,
275-
"corrupted in-inode xattr");
276-
return error;
309+
return check_xattrs(inode, NULL, IFIRST(header), end, IFIRST(header),
310+
function, line);
277311
}
278312

279313
#define xattr_check_inode(inode, header, end) \

0 commit comments

Comments
 (0)