1

The following snippet compiles and runs perfectly in all configurations, except when I strip the debug symbols, which makes it crash with a double free of the internal string pointer when t goes out of scope in bar(). malloc: *** error for object 0x11af79760: pointer being freed was not allocated

I can't find anything in any example, or documentation that suggests this is the wrong way to use ostringstream and ss.str().

std::wstring foo() {
    std::wostringstream ss;
    ss << L"this is a not too long string";
    return ss.str();
}

void bar() {
    // unrelated stuff

    auto t = foo();

    // unrelated stuff
}

This is on iOS, using Xcode 7.2, w. clang++ 7.0.2 (700.1.81), c++14 enabled and regardless of optimization level (assuming it's not completely optimized away).

This is in a rather large project, I've not been able to reproduce it in a minimal project so far. I can move the code around, and it still happens every time, which to me eliminates memory corruption. (Code also looks fine) The complexity of what's written to ss doesn't matter, I can even remove that line.

I can't get the same code to break on Mac using the same compiler and code, suggesting that perhaps the problem is related to arm in combination with symbol stripping, than the c++ implementation.

I haven't read up enough on the internals of what the symbol stripping does, apart from what I expect it to do, but it seems very odd to me that stripping symbols would affect anything.

I've tried all sorts of reasonable and unreasonable ways of wrapping the return with an extra string, and to manually access the internal rdbuf, but it doesn't matter (the library implementation does exactly that anyway).

The app immediately crashes when this happens, so instrumenting with Zombies hasn't provided anything useful so far. I've also tried compiling and running on multiple devices, with clean builds, and incremental.

I'm also terribly rusty when it comes to debugging C++ arm assembler (especially in Xcode), so I've not been able to locate anything odd there.

axl
  • 226
  • 1
  • 6
  • 1
    *This is in a rather large project, I've not been able to reproduce it in a minimal project so far* -- You probably corrupted memory somewhere else. You just found the "magic piece of code" that reveals the corruption. – PaulMcKenzie Jan 19 '16 at 10:08
  • Would stripping symbols change the binary layout in any way? I'm reasonably sure it's not memory corruption, as I can put this snippet of code anywhere in the project and it'll still fail. Not just the call, the whole block. Each instance will fail. True, the library call might have gotten corrupted, I've not read up on how code is protected on iDevices, but I still thing that's pretty far-fetched in this case. – axl Jan 19 '16 at 10:16
  • 1
    Stripping symbols changes the contents of memory. If you have any invalid memory accesses, symbol-stripping can affect what the invalid values you read are. Your first cause of action should be to check all "unrelated stuff" thoroughly, as undefined behaviour is related to pretty much everything. – molbdnilo Jan 19 '16 at 10:18
  • Do you have a reference to any documentation on the internals of symbol stripping? Why would the runtime part of the binary layout change? – axl Jan 19 '16 at 10:23
  • @axl I have no idea what you mean by the "runtime part of the binary layout", but the stripping internals are irrelevant because of these basic facts: 1) a binary with symbols and a binary without symbols have different contents, and 2) loading different data into memory results in memory containing different data. – molbdnilo Jan 19 '16 at 10:49
  • This may help you find the source of the problem: http://stackoverflow.com/questions/14045208/how-to-set-a-breakpoint-in-malloc-error-break-to-debug – Simon Kraemer Jan 19 '16 at 11:01
  • @molbdnilo, ok, I get that, but why would the loader not just skip the symbols section when loading the binary in the normal case? Since I have been moving the faulty function around to a lot of different memory locations and it still fails, I have a hard time seeing how it could be memory corruption, assuming that library code is write-protected on iOS. I agree that memory corruption sounds like the most plausible cause, I just cannot see where it comes from in this case. – axl Jan 19 '16 at 11:05
  • Thanks @SimonKraemer; the annoying thing is that the faulty free is the "correct" one, i.e. when the copy goes out of scope, so the faulty free happens first. I've been scratching my head if there's some form of optimization or faulty move-operation going on, but the code is literally identical to the standard library example code, and all sources say that `ss.str()` returns a copy, etc. – axl Jan 19 '16 at 11:08
  • I should add that the only reason I use this is that I want to use `std::put_time`, as it's the only time format function I can find that doesn't require me to manually pre-allocate a buffer like for `wcfstime()`. `put_time` has been eliminated as the source of the problem, btw. – axl Jan 19 '16 at 11:12
  • Have you tried something like this: `std::wstring foo() { std::wstring out; { std::wostringstream ss; ss << L"this is a not too long string"; out = ss.str(); } return out; }` – Simon Kraemer Jan 19 '16 at 11:49
  • I believe my colleague tried that with the same result. The shortest version we had was just `std:wostringstream ss; ss.str();` and that still failed. I guess I just have to bite the bullet and step through the assembly tomorrow. I obviously don't want to ship my product with 150 MiB of mostly useless symbol links. :) – axl Jan 19 '16 at 12:51
  • @axl: The interesting part would be if the version in my comment would crash when leaving the manual scope or when leaving the function scope. Sorry, I forgot to mention this. – Simon Kraemer Jan 19 '16 at 13:13
  • @SimonKraemer, the bad free still happens when `t` goes out of scope in `bar()`. Right now I've resorted to rewriting the code using `wcsftime()`, as I've run out of ideas. – axl Jan 20 '16 at 08:02
  • @axl That doesn't sound like the `wostringstream` causes the problem... – Simon Kraemer Jan 20 '16 at 08:23
  • @SimonKraemer, I agree that this makes little sense, but without using wostringstream all works fine. With even just a minimal usage I get the same problem, no matter where I put the code, or what build type I use (debug/release/etc). Symbol stripping is the triggering factor, the same code works in all permutations without it. – axl Jan 20 '16 at 09:58
  • I've tried various malloc/free patterns across modules to ensure there's no issue with different heaps (as you get between Windows DLLs for instance), that there's no malloc/free overrides anywhere, and I tried various modes of profiling through Instruments without getting any wiser, and at this point in time I don't really have more time to spend on this, unless I start to experience any other oddities elsewhere. I hate leaving a mystery unsolved, but right now I need to move on. :( – axl Jan 20 '16 at 09:58
  • @axl Understandable. Good luck ;-) – Simon Kraemer Jan 20 '16 at 10:01

0 Answers0