Skip to content

Fix function parsing for clang debug builds of wrap files#6

Open
kieranclancy wants to merge 5 commits into
hedayat:masterfrom
kieranclancy:fix-read-functions-parsing
Open

Fix function parsing for clang debug builds of wrap files#6
kieranclancy wants to merge 5 commits into
hedayat:masterfrom
kieranclancy:fix-read-functions-parsing

Conversation

@kieranclancy

Copy link
Copy Markdown

First of all many thanks for your project. I've wanted something like this tool for a while and so was happy to stumble upon it.

Here's the issue I ran into:

When I use clang++ to build a wrap file, even a simple one such as:

extern "C" void f(void);
#include "powerfake.h"
WRAP_FUNCTION(f);

And compile with clang++ -c -g -o wrap.o wrap.cpp (clang 19.1.17 on Linux), the resulting object file contains binary data such as::

$ xxd wrap.o
...
00001160: c410 5dc3 0000 0000 506f 7765 7246 616b  ..].....PowerFak
00001170: 6557 7261 705f 5f70 666b 616c 6961 735f  eWrap__pfkalias_
00001180: 5f33 0066 0050 464b 5072 6f74 6f74 7970  _3.f.PFKPrototyp
00001190: 6553 7461 7274 3a20 5752 4150 5045 4420  eStart: WRAPPED 
000011a0: 7c20 6620 7c20 506f 7765 7246 616b 6557  | f | PowerFakeW
000011b0: 7261 705f 5f70 666b 616c 6961 735f 5f33  rap__pfkalias__3
000011c0: 207c 2000 6175 746f 2050 6f77 6572 4661   | .auto PowerFa
000011d0: 6b65 3a3a 696e 7465 726e 616c 3a3a 5479  ke::internal::Ty
000011e0: 7065 4e61 6d65 2829 205b 5420 3d20 766f  peName() [T = vo
000011f0: 6964 2028 2a29 2829 5d00 207c 2050 464b  id (*)()]. | PFK
00001200: 5072 6f74 6f74 7970 6545 6e64 0062 6173  PrototypeEnd.bas
00001210: 6963 5f73 7472 696e 673a 2063 6f6e 7374  ic_string: const
00001220: 7275 6374 696f 6e20 6672 6f6d 206e 756c  ruction from nul
00001230: 6c20 6973 206e 6f74 2076 616c 6964 0000  l is not valid..
00001240: 5046 4b50 726f 746f 7479 7065 5374 6172  PFKPrototypeStar
00001250: 743a 2057 5241 5050 4544 207c 2066 207c  t: WRAPPED | f |
00001260: 2050 6f77 6572 4661 6b65 5772 6170 5f5f   PowerFakeWrap__
00001270: 7066 6b61 6c69 6173 5f5f 3320 7c20 766f  pfkalias__3 | vo
00001280: 6964 2028 2a29 2829 207c 2050 464b 5072  id (*)() | PFKPr
00001290: 6f74 6f74 7970 6545 6e64 0000 0000 0000  ototypeEnd......
...

Note that the correct prototype string starts on 00001240, but there are fragments of the start marker at 00001185 and end marker at 000011fd that are misinterpreted by bind_fakes as a prototype string. This is because the current parsing functions do not check for nul characters between the markers, and as a result it passes the malformed data to various string parsing functions that in turn cause an (attempted) buffer overflow:

$ bind_fakes --standalone --output-prefix mylib.powerfake --symbol-files mylib.a --wrapper-files wrap.o
Exception: basic_string_view::substr: __pos (which is 18446744073709551615) > __size (which is 6)

This PR fixes the root cause in ReadFunctionsList where it does not check for nul characters between the start and end markers, and also adds some minimal return value checks to the other parsing functions so that they don't crash on bad data.

Unit tests are added to exercise these fixed code paths.

While I was there I also noticed a memcpy() of data from the same buffer into itself, which is undefined behaviour, so have changed this to a memmove().

I am happy to release these patches under the boost software license 1.0, if you should want to merge this into the project. I've tried to keep my coding style consistent with the surrounding code.

memcpy() is not safe to use on overlapping regions.
A number of string [r]find() operations were not being checked for
return values (especially == npos) and the resulting indices could have
resulted in buffer overflow or other errors. This became apparent when
trying to debug a crash when running bind_fakes on an object file
produced by clang++ with debug symbols enabled.

Adds checks for these return values and either returns an empty result
or at least a runtime exception where appropriate rather than a buffer
overflow.
The ReadFunctionsList did not check that the string between the start
and end markers was a complete string containing no NUL characters. This
meant, in particular with at least one version of clang, that fragments
of the start and end markers in the object file could be misinterpreted
and parsed as a prototype string, leading to a buffer overflow due to
subsequent mishandling of the malformed data between the two markers.

The fix here adds a function to check that the start and end markers do
not contain a NUL character between them.

--- BEGIN example wrap.cpp ---
extern "C" void f(void);
#include "powerfake.h"
WRAP_FUNCTION(f);
--- END example wrap.cpp ---

When compiled with `clang++ -c -g -o wrap.o wrap.cpp` the resulting
object file causes crashes in bind_fakes due to start/end fragments
being emitted in debug symbols.

(Tested with clang version 19.1.7)
Adds tests to exercise a number of cases where find() results on strings
were not previously being checked for invalid values and this could lead
to buffer overflows.
These tests include a test of a specific blob produced by clang++ that
had a fragment of the prototype start string and the prototype end
string with some malformed data between them.
@hedayat

hedayat commented Apr 19, 2026

Copy link
Copy Markdown
Owner

Thank you for this PR and sorry for the late reply. I'll hopefully look into it in a few days. :)

And happy to see that you found this project useful! 😊

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.

2 participants