A SIGSEGV crash due to memory read/write from an unmapped virtual address was observed in Oracle Java Runtime Environment version 8u40 (latest at the time of this writing) while fuzz-testing the processing of TrueType font files, implemented in a proprietary t2k library. It manifests itself in the form of the following crash:
--- cut --
$ java -cp . DisplaySfntFont sample.ttf
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f8845ca6cd2, pid=17161, tid=140225357510400
#
# JRE version: Java(TM) SE Runtime Environment (8.0_40-b25) (build 1.8.0_40-b25)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.40-b25 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C [libt2k.so+0x29cd2] sc_mark+0xb62
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# hs_err_pid17161.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Aborted (core dumped)
--- cut ---
The full native stack-trace is as follows:
--- cut ---
C [libt2k.so+0x29cd2] sc_mark+0xb62
C [libt2k.so+0x3007b] sc_ScanChar2+0x11db
C [libt2k.so+0x16237] fs_ContourScan3+0xa7
C [libt2k.so+0x51b8f] MakeBWBits+0x13f
C [libt2k.so+0x4929c] T2K_RenderGlyphInternal+0x178c
C [libt2k.so+0x4965f] T2K_RenderGlyph+0x1df
C [libt2k.so+0x34d3f] Java_sun_font_T2KFontScaler_getGlyphImageNative+0x11f
--- cut ---
The crash reliably reproduces on both Windows and Linux platforms. The immediate cause of the crash appears to be an attempt to dereference a pointer fetched from outside of a heap allocation. Specifically, a 64-bit (8-byte) pointer is read from the boundary of a heap allocation, where x bytes are still within the buffer, and the remaining 8-x bytes reside outside of it. This seems to be caused by the fact that the faulty loop doesn't account for the allocation size not being a multiplicity of 8 (or 4 on 32-bit platforms), and only checks a "pointer < end_of_allocation" condition, which consequently leads to wrongly reading a pointer partially outside of the valid buffer.
The condition is illustrated below in the Valgrind log:
--- cut ---
==18143== Invalid read of size 8
==18143== at 0x2AE76E49: sc_mark (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE7D07A: sc_ScanChar2 (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE63236: fs_ContourScan3 (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9EB8E: MakeBWBits (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9629B: T2K_RenderGlyphInternal (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9665E: T2K_RenderGlyph (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE81D3E: Java_sun_font_T2KFontScaler_getGlyphImageNative (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x7774993: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== Address 0x27ab8aa8 is 24 bytes inside a block of size 26 alloc'd
==18143== at 0x40307C4: malloc (valgrind/coregrind/m_replacemalloc/vg_replace_malloc.c:270)
==18143== by 0x2AEA5C1D: tsi_AllocMem (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9EC45: MakeBWBits (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9629B: T2K_RenderGlyphInternal (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9665E: T2K_RenderGlyph (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE81D3E: Java_sun_font_T2KFontScaler_getGlyphImageNative (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x7774993: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776682F: ???
==18143==
==18143== Invalid read of size 2
==18143== at 0x2AE76E4C: sc_mark (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE7D07A: sc_ScanChar2 (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE63236: fs_ContourScan3 (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9EB8E: MakeBWBits (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9629B: T2K_RenderGlyphInternal (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE9665E: T2K_RenderGlyph (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x2AE81D3E: Java_sun_font_T2KFontScaler_getGlyphImageNative (in jre1.8.0_40/lib/amd64/libt2k.so)
==18143== by 0x7774993: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== by 0x776675F: ???
==18143== Address 0xf060 is not stack'd, malloc'd or (recently) free'd
--- cut ---
The mutated testcases minimize to a 1-byte difference, which is an immediate operand of a PUSH[] instruction invoked prior to a CALL[] instruction. However, it is not clear which TrueType instruction is the actual trigger for the crash.
Attached with this report are three mutated testcases together with original files used to create them, and a simple Java program used to reproduce the vulnerability by loading TrueType/OpenType fonts specified through a command-line parameter.
This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.