Skip to content

Fuzzing fixes#63

Open
bobsayshilol wants to merge 10 commits into
enzo1982:mainfrom
bobsayshilol:fuzzing-fixes
Open

Fuzzing fixes#63
bobsayshilol wants to merge 10 commits into
enzo1982:mainfrom
bobsayshilol:fuzzing-fixes

Conversation

@bobsayshilol

Copy link
Copy Markdown

This PR fixes a few issues found via fuzzing.

See individual commits for more details.

Sanitizer output below:

f9e7fae - Avoid leaking memory if |new MP4BytesProperty()| throws
==59260==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 136 byte(s) in 1 object(s) allocated from:
    #0 0x00000050ce91 in operator new(unsigned long) (/mp4v2/fuzz2/runall+0x50ce91) (BuildId: 1e8723124762f77071337829ebe7a5e68909a39e)
    #1 0x00000054bb44 in mp4v2::impl::MP4Atom::factory(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*, char const*) /mp4v2/mp4v2/src/mp4atom.cpp:1047:12
    #2 0x00000054859d in mp4v2::impl::MP4Atom::CreateAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*, char const*) /mp4v2/mp4v2/src/mp4atom.cpp:81:21
    #3 0x00000054f0b3 in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:187:22
    #4 0x000000551c2d in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:448:31
    #5 0x000000550e62 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:254:9
    #6 0x00000063fea2 in mp4v2::impl::MP4DrefAtom::Read() /mp4v2/mp4v2/src/atom_dref.cpp:47:14
    #7 0x00000054f711 in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:215:16
    #8 0x000000551c2d in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:448:31
    #9 0x000000550e62 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:254:9
    #10 0x00000055d4bb in mp4v2::impl::MP4File::ReadFromFile() /mp4v2/mp4v2/src/mp4file.cpp:458:18
    #11 0x00000055e9cf in mp4v2::impl::MP4File::Modify(char const*, MP4IOCallbacks_s const*, void*) /mp4v2/mp4v2/src/mp4file.cpp:173:5
    #12 0x00000052beba in MP4ModifyCallbacks /mp4v2/mp4v2/src/mp4.cpp:282:20
caa7244 - Fix an OOM and stack overflow
<snipped>
    #230 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #231 0x00000054f6ba in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #232 0x0000005517bd in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #233 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #234 0x00000054f6ba in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #235 0x0000005517bd in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #236 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #237 0x00000054f6ba in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #238 0x0000005517bd in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #239 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #240 0x00000054f6ba in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #241 0x0000005517bd in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #242 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #243 0x00000054f6ba in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #244 0x0000005517bd in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #245 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #246 0x00000054f6ba in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #247 0x0000005517bd in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #248 0x0000005509f2 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9

SUMMARY: AddressSanitizer: stack-overflow (/mp4v2/fuzz2/runall+0x4d6ed5) (BuildId: 43ad6afac5b018ce49d33f33f99dd3c7ac03f564) in __asan::GetCurrentThread()
==62224==ABORTING

Also causes an OOM with a different input due to an infinite number of children being added to a single atom (compared to "atom with a single child" being a child of itself in the above stack-overflow)

e8746b4 - Fix crash on nullptr
==66105==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00000053bbae bp 0x7ffd413318b0 sp 0x7ffd41331860 T0)
==66105==The signal is caused by a READ memory access.
==66105==Hint: address points to the zero page.
    #0 0x00000053bbae in MP4FreeH264SeqPictHeaders /mp4v2/mp4v2/src/mp4.cpp:2621:26
da83dca - Fix read/write out of bounds due to off-by-one error
==66929==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7da3675e2500 at pc 0x0000004aebb7 bp 0x7ffd3ae62220 sp 0x7ffd3ae619b0
WRITE of size 39 at 0x7da3675e2500 thread T0
    #0 0x0000004aebb6 in strncat (/mp4v2/fuzz2/runall+0x4aebb6) (BuildId: 395862754a7e6d10e15bb7ab46fa9a289fe723f0)
    #1 0x0000005a9f45 in MP4Info /mp4v2/mp4v2/src/mp4info.cpp:597:21

0x7da3675e2500 is located 0 bytes after 4096-byte region [0x7da3675e1500,0x7da3675e2500)
allocated by thread T0 here:
    #0 0x0000004c8ba8 in malloc (/mp4v2/fuzz2/runall+0x4c8ba8) (BuildId: 395862754a7e6d10e15bb7ab46fa9a289fe723f0)
    #1 0x00000052a453 in mp4v2::impl::MP4Malloc(unsigned long) /mp4v2/mp4v2/src/mp4util.h:68:15
    #2 0x0000005a9e9c in mp4v2::impl::MP4Calloc(unsigned long) /mp4v2/mp4v2/src/mp4util.h:77:19
    #3 0x0000005a9e9c in MP4Info /mp4v2/mp4v2/src/mp4info.cpp:587:31
20c06a4 - Fix a couple more leaks
Indirect leak of 48 byte(s) in 1 object(s) allocated from:
    #0 0x00000050ce91 in operator new(unsigned long) (/mp4v2/fuzz2/runall+0x50ce91) (BuildId: d405b1fc53665aa73d5907a25ac687bdf95f1c69)
    #1 0x000000658409 in mp4v2::impl::MP4StandardAtom::MP4StandardAtom(mp4v2::impl::MP4File&, char const*) /mp4v2/mp4v2/src/atom_standard.cpp:42:13
    #2 0x00000054bb78 in mp4v2::impl::MP4Atom::factory(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*, char const*) /mp4v2/mp4v2/src/mp4atom.cpp:1046:16
    #3 0x00000054859d in mp4v2::impl::MP4Atom::CreateAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*, char const*) /mp4v2/mp4v2/src/mp4atom.cpp:81:21
    #4 0x00000054f0d7 in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:187:22

Indirect leak of 40 byte(s) in 10 object(s) allocated from:
    #0 0x0000004c8fb0 in realloc (/mp4v2/fuzz2/runall+0x4c8fb0) (BuildId: d405b1fc53665aa73d5907a25ac687bdf95f1c69)
    #1 0x000000546509 in mp4v2::impl::MP4Realloc(void*, unsigned int) /mp4v2/mp4v2/src/mp4util.h:97:18
    #2 0x00000055925f in mp4v2::impl::MP4Array<unsigned int>::Resize(unsigned int) /mp4v2/mp4v2/src/mp4array.h:92:29
    #3 0x0000005b1ac1 in mp4v2::impl::MP4BytesProperty::SetCount(unsigned int) /mp4v2/mp4v2/src/mp4property.cpp:558:18
    #4 0x0000005b161a in mp4v2::impl::MP4BytesProperty::MP4BytesProperty(mp4v2::impl::MP4Atom&, char const*, unsigned int, unsigned int) /mp4v2/mp4v2/src/mp4property.cpp:536:5
    #5 0x0000005550b2 in mp4v2::impl::MP4Atom::AddReserved(mp4v2::impl::MP4Atom&, char const*, unsigned int) /mp4v2/mp4v2/src/mp4atom.cpp:609:39
    #6 0x00000063a272 in mp4v2::impl::MP4ChplAtom::MP4ChplAtom(mp4v2::impl::MP4File&) /mp4v2/mp4v2/src/atom_chpl.cpp:38:5
    #7 0x00000054a04d in mp4v2::impl::MP4Atom::factory(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*, char const*) /mp4v2/mp4v2/src/mp4atom.cpp:864:28

<snipped>

SUMMARY: AddressSanitizer: 104866 byte(s) leaked in 1808 allocation(s).
ba33c0d - Plug another leak
==76950==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 290 byte(s) in 1 object(s) allocated from:
    #0 0x0000004c8ba8 in malloc (/mp4v2/fuzz2/runall+0x4c8ba8) (BuildId: 032263d14ed6d41baee575bf881322906efee340)
    #1 0x00000052a453 in mp4v2::impl::MP4Malloc(unsigned long) /mp4v2/mp4v2/src/mp4util.h:68:15
    #2 0x00000056ce01 in mp4v2::impl::MP4BytesProperty::GetValue(unsigned char**, unsigned int*, unsigned int) /mp4v2/mp4v2/src/mp4property.h:479:30
    #3 0x00000056480a in mp4v2::impl::MP4File::GetBytesProperty(char const*, unsigned char**, unsigned int*) /mp4v2/mp4v2/src/mp4file.cpp:1007:37
    #4 0x00000056480a in mp4v2::impl::MP4File::FinishWrite(unsigned int) /mp4v2/mp4v2/src/mp4file.cpp:573:9
    #5 0x0000005675f1 in mp4v2::impl::MP4File::Close(unsigned int) /mp4v2/mp4v2/src/mp4file.cpp:714:9
    #6 0x00000052c373 in MP4Close /mp4v2/mp4v2/src/mp4.cpp:336:15

SUMMARY: AddressSanitizer: 290 byte(s) leaked in 1 allocation(s).
044f0fb - Guard against NULL deref
==77862==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000080 (pc 0x00000055e519 bp 0x7fff2e136f50 sp 0x7fff2e136f20 T0)
==77862==The signal is caused by a READ memory access.
==77862==Hint: address points to the zero page.
    #0 0x00000055e519 in mp4v2::impl::MP4Array<mp4v2::impl::MP4Atom*>::Size() /mp4v2/mp4v2/src/mp4array.h:49:16
    #1 0x00000055e519 in mp4v2::impl::MP4Atom::GetNumberOfChildAtoms() /mp4v2/mp4v2/src/mp4atom.h:171:30
    #2 0x00000055e519 in mp4v2::impl::MP4File::AddChildAtom(mp4v2::impl::MP4Atom*, char const*) /mp4v2/mp4v2/src/mp4file.cpp:756:41
    #3 0x00000055e519 in mp4v2::impl::MP4File::AddChildAtom(char const*, char const*) /mp4v2/mp4v2/src/mp4file.cpp:747:12
    #4 0x00000057177f in mp4v2::impl::MP4File::AddSystemsTrack(char const*, unsigned int) /mp4v2/mp4v2/src/mp4file.cpp:1259:11
    #5 0x00000052fd3b in MP4AddTrack /mp4v2/mp4v2/src/mp4.cpp:804:43
    #6 0x000000535476 in MP4CloneTrack /mp4v2/mp4v2/src/mp4.cpp:1819:26
03e9718 - Bounds check tag sizes before attempting to read them
==78773==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b7206fe41d1 at pc 0x00000060e29c bp 0x7fff4f620dd0 sp 0x7fff4f620dc8
READ of size 1 at 0x7b7206fe41d1 thread T0
    #0 0x00000060e29b in mp4v2::impl::itmf::Tags::fetchGenre(std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, MP4ItmfItem_s*, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, MP4ItmfItem_s*>>> const&, unsigned short&, unsigned short const*&) /mp4v2/mp4v2/src/itmf/Tags.cpp:427:21
    #1 0x00000060bd9e in mp4v2::impl::itmf::Tags::c_fetch(MP4Tags_s*&, void*) /mp4v2/mp4v2/src/itmf/Tags.cpp:95:5
    #2 0x00000051381a in MP4TagsFetch /mp4v2/mp4v2/src/cmeta.cpp:133:14

0x7b7206fe41d1 is located 0 bytes after 1-byte region [0x7b7206fe41d0,0x7b7206fe41d1)
allocated by thread T0 here:
    #0 0x0000004c8ba8 in malloc (/mp4v2/fuzz2/runall+0x4c8ba8) (BuildId: 66dfa78139d7115e3786a6b2a8e1129ec514bc36)
    #1 0x00000052a453 in mp4v2::impl::MP4Malloc(unsigned long) /mp4v2/mp4v2/src/mp4util.h:68:15
    #2 0x00000056ce11 in mp4v2::impl::MP4BytesProperty::GetValue(unsigned char**, unsigned int*, unsigned int) /mp4v2/mp4v2/src/mp4property.h:479:30
    #3 0x000000602aa2 in mp4v2::impl::itmf::(anonymous namespace)::__itemAtomToModel(mp4v2::impl::MP4ItemAtom&, MP4ItmfItem_s&) /mp4v2/mp4v2/src/itmf/generic.cpp:206:28
    #4 0x000000601c58 in mp4v2::impl::itmf::genericGetItems(mp4v2::impl::MP4File&) /mp4v2/mp4v2/src/itmf/generic.cpp:308:9
    #5 0x00000060b6d4 in mp4v2::impl::itmf::Tags::c_fetch(MP4Tags_s*&, void*) /mp4v2/mp4v2/src/itmf/Tags.cpp:72:33
    #6 0x00000051381a in MP4TagsFetch /mp4v2/mp4v2/src/cmeta.cpp:133:14
3d78dcf - Plug a leak if an exception is thrown
==80384==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 2064 byte(s) in 2 object(s) allocated from:
    #0 0x0000004c8ba8 in malloc (/mp4v2/fuzz2/runall+0x4c8ba8) (BuildId: 4b9bb4e5a7a1341de3f0b8c0fe9c03de361ce290)
    #1 0x00000052a453 in mp4v2::impl::MP4Malloc(unsigned long) /mp4v2/mp4v2/src/mp4util.h:68:15
    #2 0x00000058605b in mp4v2::impl::MP4File::GetChapters(MP4Chapter_s**, unsigned int*, MP4ChapterType) /mp4v2/mp4v2/src/mp4file.cpp:2740:50
    #3 0x000000534bf9 in MP4GetChapters /mp4v2/mp4v2/src/mp4.cpp:1647:43
afa7205 - Fix crash when using libc++
==82447==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7fe1584dac1d bp 0x7ffdad8793d0 sp 0x7ffdad878b88 T0)
==82447==The signal is caused by a READ memory access.
==82447==Hint: address points to the zero page.
    #0 0x7fe1584dac1d in __strlen_avx2 (/lib64/libc.so.6+0x149c1d) (BuildId: ff0267465bc3d76e21003b3bc5598fd5ee63e261)
    #1 0x00000043d997 in strlen (/mp4v2/fuzz2/runall+0x43d997) (BuildId: b7760b32f97d9dbbe6e78660a8ea48def8a38991)
    #2 0x00000055161a in unsigned long std::__1::__constexpr_strlen[abi:ne210108]<char>(char const*) /usr/bin/../include/c++/v1/__string/constexpr_c_functions.h:63:10
    #3 0x00000055161a in std::__1::char_traits<char>::length[abi:ne210108](char const*) /usr/bin/../include/c++/v1/__string/char_traits.h:130:12
    #4 0x00000055161a in std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::operator<<[abi:ne210108]<std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*) /usr/bin/../include/c++/v1/__ostream/basic_ostream.h:438:53
    #5 0x00000055161a in mp4v2::impl::MP4Atom::ReadProperties(unsigned int, unsigned int) /mp4v2/mp4v2/src/mp4atom.cpp:403:85
    #6 0x000000550e4c in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:249:5
    #7 0x00000054f73a in mp4v2::impl::MP4Atom::ReadAtom(mp4v2::impl::MP4File&, mp4v2::impl::MP4Atom*) /mp4v2/mp4v2/src/mp4atom.cpp:214:16
    #8 0x000000551bed in mp4v2::impl::MP4Atom::ReadChildAtoms() /mp4v2/mp4v2/src/mp4atom.cpp:447:31
    #9 0x000000550e82 in mp4v2::impl::MP4Atom::Read() /mp4v2/mp4v2/src/mp4atom.cpp:253:9
    #10 0x00000055d3eb in mp4v2::impl::MP4File::ReadFromFile() /mp4v2/mp4v2/src/mp4file.cpp:458:18
    #11 0x00000055c609 in mp4v2::impl::MP4File::Read(char const*, MP4FileProvider_s const*, MP4IOCallbacks_s const*, void*) /mp4v2/mp4v2/src/mp4file.cpp:101:5
    #12 0x00000052b47c in MP4ReadCallbacks /mp4v2/mp4v2/src/mp4.cpp:131:16

See comment for why this change is necessary.
MP4File::GetTrackH264SeqPictHeaders() can return early if it fails to
parse or allocate, which means that MP4GetTrackH264SeqPictHeaders() can
return success and look like the pointers need freeing even though some
may still be nullptrs.

To fix this, guard the derefs.
We need an extra byte for the terminating NUL, otherwise the strncat()
can write past the end or the caller can read past the end of the
string.
DeleteChildAtom() doesn't delete the child atom, it only removes it.
Some uses of it are only for reparenting and not deleting, and it
seemed invasive to rename the symbol since it's widely used, so just
plug the leaks.
GetBytesProperty() allocates its return value, so remember to free it.

Also change the types to be consistent to avoid an unnecessary cast.
InsertChildAtom() does a NULL check of pParentAtom before it's used,
but that happens too late when called here. Add an ASSERT() to catch
that issue.
ASAN flags up that these read out of bounds on bad data.
MP4ConvertTime() can throw on bad data which ends up leaking |chapters|
since it's not owned by anything. Add some RAII to plug that leak.
It's UB to pass a nullptr into operator<<(ostream&, char*), which
happens in MP4Atom::ReadProperties()'s ostringstream on invalid data.

libstdc++ "implements" this UB by ignoring the nullptr along with
everything that comes after it, and libc++ crashes. Neither of these
are good, so return a placeholder if it's a nullptr.

Note that GetName() only appears to be used for logging messages so it
shouldn't affect behaviour.
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.

1 participant