Out-of-bounds write through LWJGL's MemoryUtil.memLengthUTF8
Background
There is a not yet report bug in LWJGL's MemoryUtil.memLengthUTF8 method which incorrectly calculates that a low surrogate followed by any character takes 4 bytes. This is incorrect because it actually can require up to 6 bytes when encoded in UTF-8. When the string is then encoded in the allocated ByteBuffer, it writes out of the bounds, and since it uses Unsafe this can likely be exploited or at least lead to JVM crashes.
I haven't reported the bug to LWJGL yet because I do not know how it would be handled and whether other people would notice that it is exploitable when they see the commit which fixes it.
However, I will report it as soon as there is a workaround in Minecraft.
Affected Minecraft methods
(Might be incomplete, but I did not find more)
- com.mojang.blaze3d.platform.ClipboardManager.setClipboard(long, ByteBuffer, String)
- com.mojang.blaze3d.platform.NativeImage.fromBase64(String)
Exploitation scope
Based on the affected methods, this could possible be exploited in the following scenarios:
- Clipboard:
- copy_to_clipboard clickEvent:
- Malicous op or even just Creative mode players on server
- Malicious downloaded worlds
- Writable book
- Malicious (block) entity data which user copies through F3 + I
- copy_to_clipboard clickEvent:
- Malicious base64 encoded server icon in server list
Proof of concept
- Place a command block and paste the command from Command.txt

- Activate the command block
- Click "Click" in chat
Either the JVM will crash, or the following exception will be thrown:
java.lang.IllegalArgumentException: Missing termination
at org.lwjgl.system.Checks.assertNT(Checks.java:108)
at org.lwjgl.system.Checks.checkNT1(Checks.java:139)
at org.lwjgl.glfw.GLFW.glfwSetClipboardString(GLFW.java:4361)
...
Workaround
Remove all unpaired low surrogates. Unpaired high surrogates are not a problem because when the string is encoded any char following a high surrogate is treated as low surrogate and therefore both will take up at most 4 bytes.
static CharSequence filterUnpairedLowSurrogates(CharSequence charSeq) { StringBuilder sb = new StringBuilder(charSeq.length()); boolean hasHighSurrogate = false; for (int i = 0; i < charSeq.length(); i++) { char c = charSeq.charAt(i); if (Character.isHighSurrogate(c)) { hasHighSurrogate = true; } // Skip unpaired low surrogates else if (!hasHighSurrogate && Character.isLowSurrogate(c)) { continue; } else { hasHighSurrogate = false; } sb.append(c); } return sb; }
Or build your own version of LWJGL, the attached MemoryUtil.diff
should solve the issue.
2019-12-08, 08:43 PM
2020-01-10, 09:13 PM
2020-01-10, 04:32 PM
1
2