[Bug] Cannot free Yoga Node on Mac OS

Started by orange451, January 25, 2018, 23:24:43

Previous topic - Next topic

orange451

Trying to use Yoga with lwjgl. If I run this code:
long a =  Yoga.YGNodeNew();
Yoga.YGNodeFree(a);


The 2nd line (freeing the memory) crashes the JVM.

Here's a copy of the crash report:
https://pastebin.com/yMKngwC2

spasi

Does it work if you run it with -Dorg.lwjgl.system.allocator=system?

Also, how do you launch the application? (IDE? terminal?)

orange451

Adding
-Dorg.lwjgl.system.allocator=system
fixes the problem

Thanks.

CoDi

This doesn't fix the problem, just hides it.

I believe this is a bug in Yoga:

https://github.com/facebook/yoga/blob/master/yoga/Yoga.cpp#L218
https://github.com/facebook/yoga/blob/master/yoga/Yoga.cpp#L259

Matching new and free is undefined behavior, as far as I remember. The MacOS system allocator may just get over it silently.

spasi

Good catch CoDi! Indeed, using delete instead of free fixes the crash for me.

But now I'm curious why using -Dorg.lwjgl.system.allocator=system makes a difference. Yoga does not use the LWJGL allocator (which is jemalloc on macOS by default), in any way.

CoDi

Well, it looks like jemalloc "simply" hooks into the stdlib functions, so the free() call goes through jemalloc. I have no idea how the default operator new is wired internally to do the actual allocation.

orange451

So to clarify, what is the "correct" way of handling this?

spasi

Quote from: orange451 on January 27, 2018, 02:07:39So to clarify, what is the "correct" way of handling this?

The next 3.1.6 snapshot (build 12) will have a modified Yoga with delete instead of free, so hopefully you won't have to do anything.

spasi

Quote from: CoDi on January 27, 2018, 01:05:01Well, it looks like jemalloc "simply" hooks into the stdlib functions, so the free() call goes through jemalloc. I have no idea how the default operator new is wired internally to do the actual allocation.

I've had a better look at this, because the jemalloc build that comes with LWJGL is built with `--disable-zone-allocator`, which means it does not replace the default macOS allocator.

Turns out it does override C++ new/delete with jemalloc_cpp.cpp. So with the way jemalloc is built, YGNodeNew goes to je_malloc via the overridden new, but YGNodeFree goes to the default zone allocator's free. Which tries to free a pointer it doesn't know about and crashes.

I couldn't believe you can override new/delete by loading a shared library without something like LD_PRELOAD, but apparently it's actually possible on macOS.

Build 12 will include both the Yoga fix and a jemalloc built with `--disable-cxx --disable-zone-allocator` (i.e. neither the default zone allocator, nor new/delete will be overridden).