erofs_extract_symlink() allocates a buffer using malloc(i_size + 1) where i_size is a 64-bit value read directly from the on-disk inode with no upper bound validation. When i_size equals UINT64_MAX, the addition wraps to zero, and malloc(0) returns a minimal allocation. The subsequent erofs_pread() writes i_size bytes into this tiny buffer.
Reported-by: Tristan <[email protected]> Closes: https://lore.kernel.org/r/CAA1XrhPMekMqAnRkC-jV9rTsO4LHjzh=kxn6zqkmgbrqfrn...@mail.gmail.com/5-symlink-integer-overflow.txt Signed-off-by: Gao Xiang <[email protected]> --- fsck/main.c | 5 +++-- include/erofs/defs.h | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/fsck/main.c b/fsck/main.c index 21ada195edab..f7f1387fe642 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -784,6 +784,7 @@ static inline int erofs_extract_symlink(struct erofs_inode *inode) { struct erofs_vfile vf; bool tryagain = true; + erofs_off_t bufsz; int ret; char *buf = NULL; @@ -794,8 +795,8 @@ static inline int erofs_extract_symlink(struct erofs_inode *inode) if (ret) return ret; - buf = malloc(inode->i_size + 1); - if (!buf) { + if (check_add_overflow(inode->i_size, (erofs_off_t)1, &bufsz) || + (buf = malloc(bufsz))) { ret = -ENOMEM; goto out; } diff --git a/include/erofs/defs.h b/include/erofs/defs.h index 8151b76ad0f5..fabd867d159c 100644 --- a/include/erofs/defs.h +++ b/include/erofs/defs.h @@ -392,6 +392,15 @@ unsigned long __roundup_pow_of_two(unsigned long n) #define __erofs_stringify_1(x...) #x #define __erofs_stringify(x...) __erofs_stringify_1(x) +#define check_add_overflow(a, b, d) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + typeof(d) __d = (d); \ + (void) (&__a == &__b); \ + (void) (&__a == __d); \ + __builtin_add_overflow(__a, __b, __d); \ +}) + #define check_sub_overflow(a, b, d) ({ \ typeof(a) __a = (a); \ typeof(b) __b = (b); \ -- 2.43.5
