[Sanitizers] Fix read buffer overrun in scanning loader commands
authorMariusz Borsa <m_borsa@apple.com>
Sat, 4 Feb 2023 01:54:10 +0000 (17:54 -0800)
committerMariusz Borsa <m_borsa@apple.com>
Sat, 4 Feb 2023 02:43:33 +0000 (18:43 -0800)
commitabbd4da2043856f443e3d1c8d2c7627cac93a6ac
tree2dbdefb1ab2dd9e78b65644b45f806b7c5cafb9f
parentd62cdfadc05436cc4ea0bb6e1875a4c621f33a14
[Sanitizers] Fix read buffer overrun in scanning loader commands

The fix only affects Darwin, but to write the test I had to modify
the MemoryMappingLayout class which is used by all OSes,
to allow for mocking of image header (this change should be NFC). Hence no [Darwin] in the subject
so I can get more eyes on it.

While looking for a memory gap to put the shadow area into, the sanitizer code
scans through the loaded images, and for each image it scans through its
loader command to determine the occupied memory ranges.

While doing so, if the 'segment load' (kLCSegment) loader comand is encountered, the command scanning function
returns success (true), but does not decrement the command list iterator counter.
The result is that the function is called again and again, with the iterator counter
now being too high. The command scanner keeps updating the loader command pointer,
by using the command size field.

If the loop counter is too high, the command pointer
lands into unintended area ( beyond <header addr>+sizeof(mac_header64)+header->sizeofcmds ),
and result depends on the random content found there.

The random content interpreted as loader command might contain a large integer value in the
cmdsize field - this value is added to the current loader command pointer,
which might now point to an inaccessible memory address. It can occasionally result
in a crash if it happens to run beyond the mapped memory segment.

Note that when the area after the loader command list
contains zeros or small integers only, the loop will end normally and the problem
will go unnoticed. So it happened until now since having a some big value
after the header area, falling into command size field is a pretty rare situation.

The fix makes sure that the iterator counter gets updated when the segment load (kLCSegment)
loader command is found too, and in the same code location so the updates will always go together.

Undo the changes in the sanitizer_procmaps_mac.cpp to see the test failing.

rdar://101161047
rdar://102819707

Differential Revision: https://reviews.llvm.org/D142164
compiler-rt/lib/sanitizer_common/sanitizer_procmaps.h
compiler-rt/lib/sanitizer_common/sanitizer_procmaps_mac.cpp
compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt
compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_mac_test.cpp [new file with mode: 0644]