Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzing fixes #251

Merged
merged 14 commits into from Nov 16, 2020
Merged

Fuzzing fixes #251

merged 14 commits into from Nov 16, 2020

Conversation

blitz
Copy link
Contributor

@blitz blitz commented Nov 15, 2020

I've been running patchelf against the afl fuzzer, because we've seen patchelf crash in our production environment. This MR contains test cases and fixes for the first round of issues.

I've been operating with this assumption:

  • patchelf should never trigger assertions or crash for malformed ELF files.

I've structured this MR in this way:

  • commit to introduce test case for issue X
  • commit to fix issue X
  • commit to introduce test case for issue Y
  • commit to fix issue Y
  • ...

There are a few trivial fixes that come from code reading that currently have no corresponding test case, because the fuzzer couldn't construct one. If this is an issue, I can probably manually construct an offending file.

@tfc @domenkozar

Parsing this file results in patchelf triggering an assertion:

patchelf: patchelf.cc:431: ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, unsigned long, unsigned long, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, unsigned short>::ElfFile(FileContents) [Elf_Ehdr = Elf64_Ehdr, Elf_Phdr = Elf64_Phdr, Elf_Shdr = Elf64_Shdr, Elf_Addr = unsigned long, Elf_Off = unsigned long, Elf_Dyn = Elf64_Dyn, Elf_Sym = Elf64_Sym, Elf_Verneed = Elf64_Verneed, Elf_Versym = unsigned short]: Assertion `shstrtabIndex < shdrs.size()' failed.
Program received signal SIGABRT, Aborted.
Parsing this file results in patchelf triggering an assertion:

patchelf: patchelf.cc:384: void checkPointer(const FileContents &, void *, unsigned int): Assertion `q >= contents->data() && q + size <= contents->data() + contents->size()' failed.
Aborted (core dumped)
Parsing this file results in patchelf triggering an assertion:

patchelf: patchelf.cc:439: ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, unsigned long, unsigned long, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, unsigned short>::ElfFile(FileContents) [Elf_Ehdr = Elf64_Ehdr, Elf_Phdr = Elf64_Phdr, Elf_Shdr = Elf64_Shdr, Elf_Addr = unsigned long, Elf_Off = unsigned long, Elf_Dyn = Elf64_Dyn, Elf_Sym = Elf64_Sym, Elf_Verneed = Elf64_Verneed, Elf_Versym = unsigned short]: Assertion `shstrtabSize > 0' failed.
Aborted (core dumped)
Parsing this file results in patchelf triggering an assertion:

patchelf: patchelf.cc:442: ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, unsigned long, unsigned long, Elf64_Dyn, Elf64_Sym, Elf64_Verneed, unsigned short>::ElfFile(FileContents) [Elf_Ehdr = Elf64_Ehdr, Elf_Phdr = Elf64_Phdr, Elf_Shdr = Elf64_Shdr, Elf_Addr = unsigned long, Elf_Off = unsigned long, Elf_Dyn = Elf64_Dyn, Elf_Sym = Elf64_Sym, Elf_Verneed = Elf64_Verneed, Elf_Versym = unsigned short]: Assertion `shstrtab[shstrtabSize - 1] == 0' failed.
Aborted (core dumped)
Parsing this file results in patchelf segfaulting at:

    at /nix/store/h31cy7jm6g7cfqbhc5pm4rf9c53i3qfb-gcc-9.3.0/include/c++/9.3.0/bits/char_traits.h:335
    at /nix/store/h31cy7jm6g7cfqbhc5pm4rf9c53i3qfb-gcc-9.3.0/include/c++/9.3.0/bits/basic_string.h:527
    this=0x7fffffff8028, fileContents=...) at patchelf.cc:449
... otherwise strlen() (in the std::string constructor) will be called
with an out-of-bounds pointer.
Parsing this file results in patchelf segfaulting at:

Program received signal SIGSEGV, Segmentation fault.
std::vector<Elf32_Phdr, std::allocator<Elf32_Phdr> >::_M_realloc_insert<Elf32_Phdr const&> (this=0x7fffffff80a8, __position=..., __args=...)
    at /nix/store/h31cy7jm6g7cfqbhc5pm4rf9c53i3qfb-gcc-9.3.0/include/c++/9.3.0/bits/vector.tcc:449
449		  _Alloc_traits::construct(this->_M_impl,
(gdb) bt
    at /nix/store/h31cy7jm6g7cfqbhc5pm4rf9c53i3qfb-gcc-9.3.0/include/c++/9.3.0/bits/vector.tcc:449
    at /nix/store/h31cy7jm6g7cfqbhc5pm4rf9c53i3qfb-gcc-9.3.0/include/c++/9.3.0/bits/stl_vector.h:1195
    this=0x7fffffff8088, fileContents=...) at patchelf.cc:421
sectionsByOldIndex was resized to hdr->e_shnum instead of
rdi(hdr->e_shnum). This omitted the endianness conversion and probably
only worked by accident, because the 16-bit LE->BE conversion results
in integers that are larger as long as there no more than 255 section
headers.

This would be a good usecase for std::transform, but I'm unsure
whether we want to bump requirements to build patchelf to C++ 2017.
This is similar to the earlier fix for phdr offsets, but the fuzzer
didn't find this.
@blitz
Copy link
Contributor Author

blitz commented Nov 15, 2020

With this patch of fixes, AFL now runs for 3h without finding anything. This is a big step up from before. :)

@edolstra edolstra merged commit 57ad111 into NixOS:master Nov 16, 2020
@edolstra
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants