Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

f2fs: sanity check of xattr entry size

There is a security report where f2fs_getxattr() has a hole to expose wrong
memory region when the image is malformed like this.

f2fs_getxattr: entry->e_name_len: 4, size: 12288, buffer_size: 16384, len: 4

Cc: <stable@vger.kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

+13 -5
+13 -5
fs/f2fs/xattr.c
··· 288 288 static int lookup_all_xattrs(struct inode *inode, struct page *ipage, 289 289 unsigned int index, unsigned int len, 290 290 const char *name, struct f2fs_xattr_entry **xe, 291 - void **base_addr) 291 + void **base_addr, int *base_size) 292 292 { 293 293 void *cur_addr, *txattr_addr, *last_addr = NULL; 294 294 nid_t xnid = F2FS_I(inode)->i_xattr_nid; ··· 299 299 if (!size && !inline_size) 300 300 return -ENODATA; 301 301 302 - txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), 303 - inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS); 302 + *base_size = inline_size + size + XATTR_PADDING_SIZE; 303 + txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), *base_size, GFP_NOFS); 304 304 if (!txattr_addr) 305 305 return -ENOMEM; 306 306 ··· 312 312 313 313 *xe = __find_inline_xattr(inode, txattr_addr, &last_addr, 314 314 index, len, name); 315 - if (*xe) 315 + if (*xe) { 316 + *base_size = inline_size; 316 317 goto check; 318 + } 317 319 } 318 320 319 321 /* read from xattr node block */ ··· 476 474 int error = 0; 477 475 unsigned int size, len; 478 476 void *base_addr = NULL; 477 + int base_size; 479 478 480 479 if (name == NULL) 481 480 return -EINVAL; ··· 487 484 488 485 down_read(&F2FS_I(inode)->i_xattr_sem); 489 486 error = lookup_all_xattrs(inode, ipage, index, len, name, 490 - &entry, &base_addr); 487 + &entry, &base_addr, &base_size); 491 488 up_read(&F2FS_I(inode)->i_xattr_sem); 492 489 if (error) 493 490 return error; ··· 501 498 502 499 if (buffer) { 503 500 char *pval = entry->e_name + entry->e_name_len; 501 + 502 + if (base_size - (pval - (char *)base_addr) < size) { 503 + error = -ERANGE; 504 + goto out; 505 + } 504 506 memcpy(buffer, pval, size); 505 507 } 506 508 error = size;