The h_addr_list/h_aliases/s_aliases copy loops in deepCopyHostent()/deepCopyServent() reserve N pointer slots for N entries and then write the terminator with *++q, one slot too far, so the array isn't terminated right after the last entry and that slot holds copied address/alias bytes used as a pointer.
ASan on a reduced reproducer, after a consumer walks the returned list:
AddressSanitizer: BUS, READ memory access in _platform_memmove
x1 = 0x0200000a0100000a (copied IPv4 bytes dereferenced as a pointer)
Reserve a slot for the terminator and write it with *q.
https://github.com/wxWidgets/wxWidgets/pull/26553
(1 file)
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
Thanks for the fix but this code is sufficiently non-trivial for me not to trust myself to apply it blindly. Could you please share the code you used to test this?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()
Sure. I pulled the addr_list block out of deepCopyHostent() verbatim into a standalone program, with a fake hostent for input and a consumer that walks the rebuilt list until NULL (the same way real callers do). The h_aliases and s_aliases lists have the identical shape, so this one case covers all three.
// c++ -std=c++17 -fsanitize=address -g repro.cpp -o repro && ./repro // add -DFIXED to build with the patch applied. #include <cstring> #include <cstdio> #include <netdb.h> int main() { // Fake resolver result: two 4-byte IPv4 addresses, NULL-terminated. static unsigned char a0[4] = {10, 0, 0, 1}; static unsigned char a1[4] = {10, 0, 0, 2}; static char *addrs[3] = { (char *)a0, (char *)a1, nullptr }; hostent src; std::memset(&src, 0, sizeof(src)); src.h_length = 4; src.h_addr_list = addrs; char buffer[256]; int pos = 0; int len = src.h_length; // --- verbatim from deepCopyHostent() --- char **p = src.h_addr_list, **q; char **h_addr_list = (char **)(buffer + pos); while(*(p++) != nullptr) pos += sizeof(char *); #ifdef FIXED pos += sizeof(char *); /* and one slot for the null terminator */ #endif for (p = src.h_addr_list, q = h_addr_list; *p != nullptr; p++, q++) { std::memcpy(buffer + pos, *p, len); *q = buffer + pos; pos += len; } #ifdef FIXED *q = nullptr; #else *++q = nullptr; #endif src.h_addr_list = h_addr_list; // --- end --- // Consumer: walk until NULL, like real callers do. int n = 0; for (char **a = src.h_addr_list; *a != nullptr; a++) { unsigned char out[4]; std::memcpy(out, *a, 4); // deref of the bogus pointer happens here n++; } std::printf("walked %d addresses (expected 2)\n", n); return 0; }
Without the patch the list isn't terminated right after the last entry, so the consumer reads a third "address" whose pointer is the two copied IPv4 bytes (0x0200000a0100000a) and faults dereferencing it:
AddressSanitizer: BUS ... READ memory access ... in _platform_memmove
x1 = 0x0200000a0100000a
#1 main repro.cpp ... in the consumer loop
With -DFIXED it prints walked 2 addresses (expected 2) and exits clean under ASan.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.![]()