Changing resource pack that overrides a font crashes JVM. Not Minecraft crash.
I'm making a font resource pack for upcoming 1.20 version, so I tried to reload it to apply changes. And then BOOM! The JVM has crashed.
This occurs when triggering some event for a resource pack, such as changing the order, disabling, pressing F3+T to reload, ᅟor quitting Minecraft.
The font resource pack overrides 'default' font using a truetype font (.ttf) file.
Environment
OS:
Windows 11 , 64 bit Build 22621 (10.0.22621.1485)
OS uptime: 9 days 7:38 hours
Hyper-V role detected
CPU: total 8 (initial active 8) (4 cores per cpu, 2 threads per core) family 6 model 142 stepping 10 microcode 0xe0, cx8, cmov, fxsr, ht, mmx, 3dnowpref, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, lzcnt, tsc, tscinvbit, avx, avx2, aes, erms, clmul, bmi1, bmi2, adx, fma, vzeroupper, clflush, clflushopt, hv
Memory: 4k page, system-wide physical 16295M (3838M free)
TotalPageFile size 40871M (AvailPageFile size 22336M)
current process WorkingSet (physical memory assigned to process): 1339M, peak: 1365M
current process commit charge ("private bytes"): 1521M, peak: 1547M
vm_info: Java HotSpot(TM) 64-Bit Server VM (17.0.6+9-LTS-190) for windows-amd64 JRE (17.0.6+9-LTS-190), built on Dec 6 2022 15:53:54 by "mach5one" with MS VC++ 17.1 (VS2022)
Attachments
Comments 3
Code analysis (23w18a)
Yarn names are used here: 23w18+build.1 for 23w18a. The decompiled code shown below has been generated using Fabric’s fork of CFR.
FontManager#reload has this chunk of code, closing each loaded font storage, then closing each font:
this.fontStorages.values().forEach(FontStorage::close);
this.fontStorages.clear();
this.fonts.forEach(Font::close);
this.fonts.clear();Looking at {{FontStorage}}’s code, we find the following:
@Override
public void close() {
this.closeFonts();
this.closeGlyphAtlases();
}
private void closeFonts() {
for (Font font : this.fonts) {
font.close();
}
this.fonts.clear();
}
private void closeGlyphAtlases() {
for (GlyphAtlasTexture glyphAtlasTexture : this.glyphAtlases) {
glyphAtlasTexture.close();
}
this.glyphAtlases.clear();
}The problem is that the font storage objects in FontManager#fontStorages refer to the same fonts as the ones in FontManager#fonts. This causes some fonts to be closed more than once. BitmapFont}}’s implementation of {{Font#close protects against multiple calls, but TrueTypeFont}}’s implementation does not – calling {{close more than once on a TrueTypeFont causes a double free, resulting in a JVM crash.
Likewise, FontManager#close also causes the same type of double free.
A possible solution would be to have FontStorage not be responsible for closing its fonts. Alternatively, each subclass of Font could protect against multiple closes, but this is a hacky solution in my opinion.
Could you please attach a sample resource pack to this issue?