mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-10-04 06:18:00 +02:00
Merge pull request #22037 from calixteman/cff-index-offsets
Bound CFF INDEX and FDArray parsing
This commit is contained in:
+23
-5
@@ -35,6 +35,9 @@ import { MathClamp } from "../shared/math_clamp.js";
|
||||
// Maximum subroutine call depth of type 2 charstrings. Matches OTS.
|
||||
const MAX_SUBR_NESTING = 10;
|
||||
|
||||
// CFF FDSelect uses Card8 indices, so only 256 font DICTs are addressable.
|
||||
const MAX_FD_ARRAY_COUNT = 256;
|
||||
|
||||
function looksLikeUnsigned16BitNegative(coord) {
|
||||
return coord > 0x7fff && coord <= 0xffff;
|
||||
}
|
||||
@@ -325,7 +328,12 @@ class CFFParser {
|
||||
let charset, encoding;
|
||||
if (cff.isCIDFont) {
|
||||
const fdArrayIndex = this.parseIndex(topDict.getByName("FDArray")).obj;
|
||||
for (let i = 0, ii = fdArrayIndex.count; i < ii; ++i) {
|
||||
let fdArrayCount = fdArrayIndex.count;
|
||||
if (fdArrayCount > MAX_FD_ARRAY_COUNT) {
|
||||
warn(`CFFParser.parse: too many FDArray entries (${fdArrayCount}).`);
|
||||
fdArrayCount = MAX_FD_ARRAY_COUNT;
|
||||
}
|
||||
for (let i = 0; i < fdArrayCount; ++i) {
|
||||
const dictRaw = fdArrayIndex.get(i);
|
||||
const fontDict = this.createDict(
|
||||
CFFTopDict,
|
||||
@@ -489,16 +497,22 @@ class CFFParser {
|
||||
|
||||
if (count !== 0) {
|
||||
const offsetSize = bytes[pos++];
|
||||
// add 1 for offset to determine size of last object
|
||||
if (offsetSize < 1 || offsetSize > 4) {
|
||||
throw new FormatError(`Invalid CFF INDEX offset size: ${offsetSize}`);
|
||||
}
|
||||
// Offsets are relative to the byte before the object data.
|
||||
const startPos = pos + (count + 1) * offsetSize - 1;
|
||||
const bytesLength = bytes.length;
|
||||
// Clamp offsets to prevent out-of-bounds or overlapping entries.
|
||||
let prevOffset = startPos;
|
||||
|
||||
for (i = 0, ii = count + 1; i < ii; ++i) {
|
||||
let offset = 0;
|
||||
for (let j = 0; j < offsetSize; ++j) {
|
||||
offset <<= 8;
|
||||
offset += bytes[pos++];
|
||||
offset = (offset << 8) | bytes[pos++];
|
||||
}
|
||||
offsets.push(startPos + offset);
|
||||
prevOffset = MathClamp(startPos + offset, prevOffset, bytesLength);
|
||||
offsets.push(prevOffset);
|
||||
}
|
||||
end = offsets[count];
|
||||
}
|
||||
@@ -1086,6 +1100,10 @@ class CFFParser {
|
||||
}
|
||||
const fdIndex = bytes[pos++];
|
||||
const next = (bytes[pos] << 8) | bytes[pos + 1];
|
||||
// Reject ranges that would expand FDSelect past the glyph count.
|
||||
if (next - first > length - fdSelect.length) {
|
||||
throw new FormatError("parseFDSelect: Invalid font data.");
|
||||
}
|
||||
for (let j = first; j < next; ++j) {
|
||||
fdSelect.push(fdIndex);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
CFFTopDict,
|
||||
} from "../../src/core/cff_parser.js";
|
||||
import { DefaultFileReaderFactory, TEST_PDFS_PATH } from "./test_utils.js";
|
||||
import { FormatError } from "../../src/shared/util.js";
|
||||
import { PDFDocument } from "../../src/core/document.js";
|
||||
import { Ref } from "../../src/core/primitives.js";
|
||||
import { SEAC_ANALYSIS_ENABLED } from "../../src/core/fonts_utils.js";
|
||||
@@ -652,6 +653,86 @@ describe("CFFParser", function () {
|
||||
expect(fdSelect.format).toEqual(3);
|
||||
});
|
||||
|
||||
it("rejects fdselect format 3 ranges exceeding the glyph count", function () {
|
||||
// prettier-ignore
|
||||
const bytes = new Uint8Array([0x03, // format
|
||||
0x00, 0x03, // range count
|
||||
0x00, 0x00, // first gid
|
||||
0x00, // font dict 0 id
|
||||
0xff, 0xff, // next gid (too large)
|
||||
0x01, // font dict 1 id
|
||||
0x00, 0x00, // next gid (decreasing)
|
||||
0x02, // font dict 2 id
|
||||
0xff, 0xff // sentinel (exclusive end gid)
|
||||
]);
|
||||
parser.bytes = bytes.slice();
|
||||
|
||||
expect(() => parser.parseFDSelect(0, 4)).toThrowError(
|
||||
FormatError,
|
||||
"parseFDSelect: Invalid font data."
|
||||
);
|
||||
});
|
||||
|
||||
it("parses an index with invalid offsets", function () {
|
||||
// prettier-ignore
|
||||
const bytes = new Uint8Array([0x00, 0x04, // count
|
||||
0x01, // offsetSize
|
||||
0x01, // offset[0]
|
||||
0x03, // offset[1]
|
||||
0x01, // offset[2] (decreasing)
|
||||
0xc8, // offset[3] (out of bounds)
|
||||
0x02, // offset[4] (decreasing)
|
||||
0x0a, 0x0b, 0x0c, 0x0d]);
|
||||
parser.bytes = bytes;
|
||||
const { obj: index, endPos } = parser.parseIndex(0);
|
||||
|
||||
expect(index.count).toEqual(4);
|
||||
expect(index.get(0)).toEqual(new Uint8Array([0x0a, 0x0b]));
|
||||
expect(index.get(1)).toEqual(new Uint8Array([]));
|
||||
expect(index.get(2)).toEqual(new Uint8Array([0x0c, 0x0d]));
|
||||
expect(index.get(3)).toEqual(new Uint8Array([]));
|
||||
expect(endPos).toEqual(bytes.length);
|
||||
});
|
||||
|
||||
it("throws on an index with an invalid offset size", function () {
|
||||
// prettier-ignore
|
||||
parser.bytes = new Uint8Array([0x00, 0x01, // count
|
||||
0x05, // offsetSize (invalid)
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, // offset[0]
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, // offset[1]
|
||||
0x0a]);
|
||||
|
||||
expect(() => parser.parseIndex(0)).toThrowError(
|
||||
FormatError,
|
||||
"Invalid CFF INDEX offset size: 5"
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores unreachable FDArray entries", function () {
|
||||
cff.isCIDFont = true;
|
||||
cff.topDict.setByName("ROS", [0, 0, 0]);
|
||||
cff.topDict.setByName("FDSelect", 0);
|
||||
cff.topDict.setByName("FDArray", 0);
|
||||
|
||||
cff.fdArray = [];
|
||||
for (let i = 0; i < 300; i++) {
|
||||
const fdDict = new CFFTopDict(cff.strings);
|
||||
fdDict.setByName("Private", [0, 0]);
|
||||
fdDict.privateDict = new CFFPrivateDict(cff.strings);
|
||||
cff.fdArray.push(fdDict);
|
||||
}
|
||||
cff.fdSelect = new CFFFDSelect(0, Array(cff.charStrings.count).fill(0));
|
||||
const fontDataWithLargeFDArray = new CFFCompiler(cff).compile();
|
||||
|
||||
const reparsedCff = new CFFParser(
|
||||
new Stream(fontDataWithLargeFDArray),
|
||||
{},
|
||||
SEAC_ANALYSIS_ENABLED
|
||||
).parse();
|
||||
|
||||
expect(reparsedCff.fdArray.length).toEqual(256);
|
||||
});
|
||||
|
||||
// TODO fdArray
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user