-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Description
When using a custom system font (Product Sans in my case), the game intermittently crashes with EXCEPTION_ACCESS_VIOLATION in lwjgl_stb.dll during font loading. The crash happens inside stbtt_PackFontRanges called from the Font constructor. It doesn't happen every launch, roughly maybe 1 in 3 times, which points to a GC timing issue rather than bad font data.
Root Cause Analysis
The crash originates in Font.java constructor, specifically at the stbtt_PackFontRanges call on line 62.
The LWJGL wrapper for this method works like this internally:
public static boolean stbtt_PackFontRanges(STBTTPackContext spc, ByteBuffer fontdata, int font_index, STBTTPackRange.Buffer ranges) {
return nstbtt_PackFontRanges(spc.address(), memAddress(fontdata), font_index, ranges.address(), ranges.remaining()) != 0;
}memAddress(fontdata) extracts the raw long pointer from the ByteBuffer. After that, only primitive long/int values are passed to the JNI native call. The JVM no longer sees any live object reference to the font data buffer during the native call, so it becomes eligible for garbage collection.
If G1 GC runs a concurrent cycle while STB is still reading font data through that pointer, the DirectByteBuffer's Cleaner frees the native memory, and STB hits an access violation trying to read from freed memory.
This is more likely to happen because:
- Between
stbtt_InitFont(line 34) andstbtt_PackFontRanges(line 62), there are ~13.create()calls that allocate DirectByteBuffers, which can trigger GC - The
stbtt_InitFonton line 34 also stores a pointer into the same buffer inside thefontInfostruct, butfontInfois allocated with.create()and also has no reachability guarantee
Additionally, stbtt_InitFont return value is not checked. If it ever fails, the subsequent stbtt_PackFontRanges call will use invalid font info and crash deterministically.
Suggested Fix
Add Reference.reachabilityFence(buffer) after all native calls that use the buffer to prevent premature GC:
STBTruetype.stbtt_PackFontRanges(packContext, buffer, 0, packRange);
STBTruetype.stbtt_PackEnd(packContext);
// ...
scale = STBTruetype.stbtt_ScaleForPixelHeight(fontInfo, height);
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer ascent = stack.mallocInt(1);
STBTruetype.stbtt_GetFontVMetrics(fontInfo, ascent, null, null);
this.ascent = ascent.get(0);
}
Reference.reachabilityFence(buffer);Or alternatively, store the buffer as a field in the Font class so it stays alive for the lifetime of the object.
Also worth adding:
- Check the return value of
stbtt_InitFontand handle failure gracefully - Free STB structs (
fontInfo,packContext,packRange,cdata) after use since they currently leak native memory on every font load
Crash Log
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffcf73fe131, pid=18072, tid=9192
#
# Problematic frame:
# C [lwjgl_stb.dll+0x2e131]
Current thread (0x0000025c309bd840): JavaThread "Render thread" [_thread_in_native, id=9192]
Native frames:
C [lwjgl_stb.dll+0x2e131]
Java frames:
j org.lwjgl.stb.STBTruetype.nstbtt_PackFontRanges(JJIJI)I+0
j org.lwjgl.stb.STBTruetype.stbtt_PackFontRanges(Lorg/lwjgl/stb/STBTTPackContext;Ljava/nio/ByteBuffer;ILorg/lwjgl/stb/STBTTPackRange$Buffer;)Z+42
j meteordevelopment.meteorclient.renderer.text.Font.<init>(Ljava/nio/ByteBuffer;I)V+292
j meteordevelopment.meteorclient.systems.hud.HudRenderer.loadFont(I)Lmeteordevelopment/meteorclient/systems/hud/HudRenderer$FontHolder;+36
j meteordevelopment.meteorclient.systems.hud.HudRenderer$$Lambda+0x00000008022c6b58.apply(Ljava/lang/Object;)Ljava/lang/Object;+7
j meteordevelopment.meteorclient.systems.hud.elements.ActiveModulesHud.tick(Lmeteordevelopment/meteorclient/systems/hud/HudRenderer;)V+153
j meteordevelopment.meteorclient.systems.hud.Hud.onTick(Lmeteordevelopment/meteorclient/events/world/TickEvent$Post;)V+84
siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0x0000025c53be6098
Registers:
RAX=0x0000000000000578, RBX=0x0000025c11394534, RCX=0x0000000000000000, RDX=0x0000025c53be5b20
Environment
- Meteor Client build 49 (1.21.11)
- OpenJDK 21.0.3 (Microsoft build), G1 GC
- JVM args:
-Xms512m -Xmx5192m - Windows 11, Intel i5-12600 (12 cores), Intel UHD 770
- Font: Product Sans Regular (system installed TTF, 109KB, valid header)