aboutsummaryrefslogtreecommitdiff
path: root/toys/posix
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2020-11-12 13:51:22 -0800
committerRob Landley <rob@landley.net>2020-11-12 18:36:37 -0600
commit0c567294d476ae6cab863e774844cd94e51b88e2 (patch)
tree660297ee772a79333955873f25d2e71ca4120eee /toys/posix
parent693eaf6c22a8a64645af6732731d6ef2a0432838 (diff)
downloadtoybox-0c567294d476ae6cab863e774844cd94e51b88e2.tar.gz
file: harden against invalid input.
I promised months ago I'd fix this, and there was a (not visible to the public but filed by a member of the public) bug filed against Android in the meantime, but judged No Security Impact because "toybox is not a security boundary". Anyway, it seemed high time I learned about fuzzing command-line tools with AFL++, so here we are. With these patches (and starting from the ELF files in test/files/elf), toybox file survived ~24hours against AFL++. Amusingly it corrupted the ELF files hard enough that it also managed to find a bug in the code for MS-DOS executables, which is the motivation for the final hunk in this patch. Bug: http://b/159065007 Test: ~/AFLplusplus/afl-fuzz -i tests/files/elf -o fuzz-out -- ./file @@
Diffstat (limited to 'toys/posix')
-rw-r--r--toys/posix/file.c26
1 files changed, 16 insertions, 10 deletions
diff --git a/toys/posix/file.c b/toys/posix/file.c
index 4ff2cca7..ecb3fc43 100644
--- a/toys/posix/file.c
+++ b/toys/posix/file.c
@@ -37,7 +37,7 @@ static void do_elf_file(int fd)
phentsize, phnum, shsize, shnum;
int64_t (*elf_int)(void *ptr, unsigned size);
char *map = 0;
- off_t phoff, shoff;
+ long phoff, shoff;
printf("ELF ");
elf_int = (endian==2) ? peek_be : peek_le;
@@ -107,11 +107,11 @@ static void do_elf_file(int fd)
// We need to read the phdrs for dynamic vs static.
// (Note: fields got reordered for 64 bit)
- if (phoff+phnum*phentsize>TT.len) goto bad;
+ if (phoff<0 || phoff>TT.len || phnum*phentsize>TT.len-phoff) goto bad;
for (i = 0; i<phnum; i++) {
char *phdr = map+phoff+i*phentsize;
int p_type = elf_int(phdr, 4);
- long long p_offset, p_filesz;
+ unsigned long long p_offset, p_filesz;
if (p_type==2 /*PT_DYNAMIC*/) dynamic = 1;
if (p_type!=3 /*PT_INTERP*/ && p_type!=4 /*PT_NOTE*/) continue;
@@ -121,7 +121,7 @@ static void do_elf_file(int fd)
p_filesz = elf_int(phdr+16*j, 4*j);
if (p_type==3 /*PT_INTERP*/) {
- if (p_offset+p_filesz>TT.len) goto bad;
+ if (p_filesz>TT.len || p_offset>TT.len-p_filesz) goto bad;
printf(", dynamic (%.*s)", (int)p_filesz, map+p_offset);
}
}
@@ -131,12 +131,17 @@ static void do_elf_file(int fd)
// Notes are in program headers *and* section headers, but some files don't
// contain program headers, so we prefer to check here.
// (Note: fields got reordered for 64 bit)
- if (shoff+i*shnum>TT.len) goto bad;
+ if (shoff<0 || shoff>TT.len || shnum*shsize>TT.len-shoff) goto bad;
for (i = 0; i<shnum; i++) {
char *shdr = map+shoff+i*shsize;
- int sh_type = elf_int(shdr+4, 4);
- long sh_offset = elf_int(shdr+8+8*(bits+1), 4*(bits+1));
- int sh_size = elf_int(shdr+8+12*(bits+1), 4);
+ unsigned long sh_offset;
+ int sh_type, sh_size;
+
+ if (shdr>map+TT.len-(8+4*(bits+1))) goto bad;
+ sh_type = elf_int(shdr+4, 4);
+ sh_offset = elf_int(shdr+8+8*(bits+1), 4*(bits+1));
+ sh_size = elf_int(shdr+8+12*(bits+1), 4);
+ if (sh_offset>TT.len || sh_size>TT.len-sh_offset) goto bad;
if (sh_type == 2 /*SHT_SYMTAB*/) {
stripped = 0;
@@ -151,12 +156,13 @@ static void do_elf_file(int fd)
while (sh_size >= 3*4) { // Don't try to read a truncated entry.
unsigned n_namesz, n_descsz, n_type, notesz;
- if (sh_offset+sh_size>TT.len) goto bad;
+ if (note>map+TT.len-3*4) goto bad;
n_namesz = elf_int(note, 4);
n_descsz = elf_int(note+4, 4);
n_type = elf_int(note+8, 4);
notesz = 3*4 + ((n_namesz+3)&~3) + ((n_descsz+3)&~3);
+ if (notesz<n_namesz || notesz<n_descsz) goto bad;
// Does the claimed size of this note actually fit in the section?
if (notesz > sh_size) goto bad;
@@ -191,7 +197,7 @@ bad:
static void do_regular_file(int fd, char *name)
{
char *s;
- int len, magic;
+ unsigned len, magic;
// zero through elf shnum, just in case
memset(toybuf, 0, 80);