Post AwjzLYFDbXVgMYlSi0 by brouhaha@mastodon.social
(DIR) More posts by brouhaha@mastodon.social
(DIR) Post #AwjzLVWTiuX9vDohhA by brouhaha@mastodon.social
2025-08-01T20:45:29Z
0 likes, 0 repeats
I've been trying to track down a nasty memory corruption problem in my C++ program, which manifests right after I do an emplace_back() to a std::vector. My debugging efforts this far have mostly focused on the constructors involved in creating the element for the vector, but I haven't found anything wrong there.I'm away from my computer right now, but I think I may have identified the problem. That emplace_back() occurs in a range-based for loop that is iterating over the vector.1/
(DIR) Post #AwjzLWfjRzWvUD9bl2 by brouhaha@mastodon.social
2025-08-01T20:59:39Z
0 likes, 0 repeats
When the vector grows past its previous capacity, it has to be reallocated, which invalidates the iterator, but the loop is still trying to use it. In this case, I think changing the range-based for loop to an ordinary for loop with a numeric index will solve the problem.2/
(DIR) Post #AwjzLXWCIvUu6vhVXE by brouhaha@mastodon.social
2025-08-01T21:00:26Z
0 likes, 0 repeats
It's another case where C++ gave me enough rope to shoot myself in the foot.:-)3/
(DIR) Post #AwjzLYFDbXVgMYlSi0 by brouhaha@mastodon.social
2025-08-01T21:07:37Z
0 likes, 0 repeats
I should look into whether any of the open source static analysis tools would have detected this. Valgrind detects it at runtime, but I didn't understand what was causing the issue. Poking at it with gdb didn't help me much either, because I was looking in the wrong places.4/
(DIR) Post #AwjzLYtdBHpuNtfjhQ by cross@discuss.systems
2025-08-01T21:25:45Z
0 likes, 0 repeats
@brouhaha it looks like, for this specific case, Address Sanitizer finds an issue pretty quickly.```term% cat foo.cc#include <iostream>#include <vector>intmain(){ std::vector<int> v{0, 1, 2, 3, 4}; for (const auto elem: v) std::cout << "elem= " << elem << std::endl; for (auto it = v.cbegin(); it != v.cend(); ++it) { if (*it % 2) v.emplace_back(1); } for (const auto elem: v) std::cout << "elem= " << elem << std::endl; return 0;}term% clang++ -std=c++23 -fsanitize=address,undefined -Wall -Wextra -o foo foo.ccterm% ./foofoo(22239,0x20c2e5f00) malloc: nano zone abandoned due to inability to reserve vm space.elem= 0elem= 1elem= 2elem= 3elem= 4===================================================================22239==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000001c98 at pc 0x000102264f94 bp 0x00016db9a650 sp 0x00016db9a648READ of size 4 at 0x603000001c98 thread T0 #0 0x000102264f90 in main+0x6b0 (foo:arm64+0x100000f90) [snip] #9 0x000102266308 in int& std::__1::vector<int, std::__1::allocator<int>>::emplace_back<int>(int&&)+0x224 (foo:arm64+0x100002308) #10 0x000102265028 in main+0x748 (foo:arm64+0x100001028) #11 0x00019de36b94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)previously allocated by thread T0 here: [snip] #8 0x000102266928 in std::__1::vector<int, std::__1::allocator<int>>::vector[abi:ne190102](std::initializer_list<int>)+0x328 (foo:arm64+0x100002928) #9 0x0001022655a4 in std::__1::vector<int, std::__1::allocator<int>>::vector[abi:ne190102](std::initializer_list<int>)+0x60 (foo:arm64+0x1000015a4) #10 0x000102264b54 in main+0x274 (foo:arm64+0x100000b54) #11 0x00019de36b94 in start+0x17b8 (dyld:arm64e+0xfffffffffff3ab94)SUMMARY: AddressSanitizer: heap-use-after-free (foo:arm64+0x100000f90) in main+0x6b0[snip]Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb==22239==ABORTINGterm% clang++ --versionApple clang version 17.0.0 (clang-1700.0.13.5)Target: arm64-apple-darwin24.5.0Thread model: posixInstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/binterm% ```
(DIR) Post #AwjzLZYOjiRiQKkIF6 by brainwagon@mastodon.social
2025-08-01T21:49:23Z
0 likes, 0 repeats
@cross @brouhaha These kind of error messages are one of the reasons that I really hate C++ (not the only reason, but a good reason). Debuggers like this aren't even "source level debuggers": they seldom print any message that can quickly be associated with the source code responsible for them. WTF does "nano zone abandoned due to inability to reserve vm space" even mean?
(DIR) Post #AwjzLa8Cab5ODNUt3A by Armadillosoft@mastodon.social
2025-08-01T21:58:59Z
0 likes, 0 repeats
@brainwagon @cross @brouhaha {{{ hugs }}}Best I usually hope for in any compiler error message (admittedly, I have spent my time with lots of very old ones) is a general idea of where in the code the problem might lie.
(DIR) Post #AwjzLafWahjzsj5UzQ by brouhaha@mastodon.social
2025-08-01T22:03:02Z
0 likes, 0 repeats
@Armadillosoft @brainwagon @cross Agreed, but modern programming languages, like C++ but also some others, have made that much more difficult to achieve in the general case. I haven't spent a great deal of time pondering it, but my gut feeling is that raising the semantic level of programming languages might inevitably have this effect.Certainly I find that debugging C++ code is on average substantially more difficult than debugging a similar amount of C code.1/
(DIR) Post #AwjzLbIAH2eJoZAMDY by cy@fedicy.us.to
2025-08-01T23:13:26Z
0 likes, 0 repeats
What I do is program in C.#include <stdio.h>#define ELEMS(v) (sizeof(v)/sizeof(*v))static voidprint_elems(int*const v, int nv) {int idx;for(idx=0;idx<nv;++idx) {printf("elem= %d\n", v[idx]);}}#define print_elems(v) print_elems(v, ELEMS(v))intmain() {int v[] = {0, 1, 2, 3, 4};print_elems(v);int nextra = 0;int idx;for(idx=0;idx<ELEMS(v);++idx) {if(v[idx] % 2) {++nextra;}}int vv[nextra];for(idx=0;idx<nextra;++idx) {vv[idx] = 1;}puts("---");print_elems(v);print_elems(vv);}$ tcc -run /tmp/derp.celem= 0elem= 1elem= 2elem= 3elem= 4---elem= 0elem= 1elem= 2elem= 3elem= 4elem= 1elem= 1CC: @Armadillosoft@mastodon.social @brainwagon@mastodon.social @cross@discuss.systems
(DIR) Post #AwjzLcjqtaOIIj7z0q by brouhaha@mastodon.social
2025-08-01T21:11:24Z
0 likes, 0 repeats
When I get home, I'll verify whether this is the problem, although it is definitely _a_ problem. My takeaway from the experience is:1) be more careful with iterators, watching for circumstances in which they may be invalidated2) add invalidated iterators to my set of things to check for on mysterious failures5/
(DIR) Post #AwjzLffhzRrJOkDDTE by brouhaha@mastodon.social
2025-08-01T22:05:50Z
0 likes, 0 repeats
@Armadillosoft @brainwagon @cross On the other hand, debugging a C++ program might not be harder than debugging a C program that has the same level of functionality. In that case, the C program is usually larger (in source code form) than the C++ program, due to the lower semantic level.