Merge pull request #6724 from brendandahl/callsubr
Add validation for callsubr and callgsubr for type 2 charstrings.
This commit is contained in:
commit
62ade3e373
@ -80,6 +80,9 @@ var HINTING_ENABLED = false;
|
|||||||
// to control analysis of seac charstrings.
|
// to control analysis of seac charstrings.
|
||||||
var SEAC_ANALYSIS_ENABLED = false;
|
var SEAC_ANALYSIS_ENABLED = false;
|
||||||
|
|
||||||
|
// Maximum subroutine call depth of type 2 chartrings. Matches OTS.
|
||||||
|
var MAX_SUBR_NESTING = 10;
|
||||||
|
|
||||||
var FontFlags = {
|
var FontFlags = {
|
||||||
FixedPitch: 1,
|
FixedPitch: 1,
|
||||||
Serif: 2,
|
Serif: 2,
|
||||||
@ -6109,10 +6112,7 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
cff.isCIDFont = topDict.hasName('ROS');
|
cff.isCIDFont = topDict.hasName('ROS');
|
||||||
|
|
||||||
var charStringOffset = topDict.getByName('CharStrings');
|
var charStringOffset = topDict.getByName('CharStrings');
|
||||||
var charStringsAndSeacs = this.parseCharStrings(charStringOffset);
|
var charStringIndex = this.parseIndex(charStringOffset).obj;
|
||||||
cff.charStrings = charStringsAndSeacs.charStrings;
|
|
||||||
cff.seacs = charStringsAndSeacs.seacs;
|
|
||||||
cff.widths = charStringsAndSeacs.widths;
|
|
||||||
|
|
||||||
var fontMatrix = topDict.getByName('FontMatrix');
|
var fontMatrix = topDict.getByName('FontMatrix');
|
||||||
if (fontMatrix) {
|
if (fontMatrix) {
|
||||||
@ -6140,19 +6140,30 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
// cid fonts don't have an encoding
|
// cid fonts don't have an encoding
|
||||||
encoding = null;
|
encoding = null;
|
||||||
charset = this.parseCharsets(topDict.getByName('charset'),
|
charset = this.parseCharsets(topDict.getByName('charset'),
|
||||||
cff.charStrings.count, cff.strings, true);
|
charStringIndex.count, cff.strings, true);
|
||||||
cff.fdSelect = this.parseFDSelect(topDict.getByName('FDSelect'),
|
cff.fdSelect = this.parseFDSelect(topDict.getByName('FDSelect'),
|
||||||
cff.charStrings.count);
|
charStringIndex.count);
|
||||||
} else {
|
} else {
|
||||||
charset = this.parseCharsets(topDict.getByName('charset'),
|
charset = this.parseCharsets(topDict.getByName('charset'),
|
||||||
cff.charStrings.count, cff.strings, false);
|
charStringIndex.count, cff.strings, false);
|
||||||
encoding = this.parseEncoding(topDict.getByName('Encoding'),
|
encoding = this.parseEncoding(topDict.getByName('Encoding'),
|
||||||
properties,
|
properties,
|
||||||
cff.strings, charset.charset);
|
cff.strings, charset.charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
cff.charset = charset;
|
cff.charset = charset;
|
||||||
cff.encoding = encoding;
|
cff.encoding = encoding;
|
||||||
|
|
||||||
|
var charStringsAndSeacs = this.parseCharStrings(
|
||||||
|
charStringIndex,
|
||||||
|
topDict.privateDict.subrsIndex,
|
||||||
|
globalSubrIndex.obj,
|
||||||
|
cff.fdSelect,
|
||||||
|
cff.fdArray);
|
||||||
|
cff.charStrings = charStringsAndSeacs.charStrings;
|
||||||
|
cff.seacs = charStringsAndSeacs.seacs;
|
||||||
|
cff.widths = charStringsAndSeacs.widths;
|
||||||
|
|
||||||
return cff;
|
return cff;
|
||||||
},
|
},
|
||||||
parseHeader: function CFFParser_parseHeader() {
|
parseHeader: function CFFParser_parseHeader() {
|
||||||
@ -6327,22 +6338,17 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
}
|
}
|
||||||
return cffDict;
|
return cffDict;
|
||||||
},
|
},
|
||||||
parseCharStrings: function CFFParser_parseCharStrings(charStringOffset) {
|
parseCharString: function CFFParser_parseCharString(state, data,
|
||||||
var charStrings = this.parseIndex(charStringOffset).obj;
|
localSubrIndex,
|
||||||
var seacs = [];
|
globalSubrIndex) {
|
||||||
var widths = [];
|
if (state.callDepth > MAX_SUBR_NESTING) {
|
||||||
var count = charStrings.count;
|
return false;
|
||||||
for (var i = 0; i < count; i++) {
|
}
|
||||||
var charstring = charStrings.get(i);
|
var stackSize = state.stackSize;
|
||||||
|
var stack = state.stack;
|
||||||
|
|
||||||
var stackSize = 0;
|
|
||||||
var stack = [];
|
|
||||||
var undefStack = true;
|
|
||||||
var hints = 0;
|
|
||||||
var valid = true;
|
|
||||||
var data = charstring;
|
|
||||||
var length = data.length;
|
var length = data.length;
|
||||||
var firstStackClearing = true;
|
|
||||||
for (var j = 0; j < length;) {
|
for (var j = 0; j < length;) {
|
||||||
var value = data[j++];
|
var value = data[j++];
|
||||||
var validationCommand = null;
|
var validationCommand = null;
|
||||||
@ -6368,8 +6374,8 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
if (stackSize >= 4) {
|
if (stackSize >= 4) {
|
||||||
stackSize -= 4;
|
stackSize -= 4;
|
||||||
if (SEAC_ANALYSIS_ENABLED) {
|
if (SEAC_ANALYSIS_ENABLED) {
|
||||||
seacs[i] = stack.slice(stackSize, stackSize + 4);
|
state.seac = stack.slice(stackSize, stackSize + 4);
|
||||||
valid = false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validationCommand = CharstringValidationData[value];
|
validationCommand = CharstringValidationData[value];
|
||||||
@ -6388,28 +6394,65 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
j += 4;
|
j += 4;
|
||||||
stackSize++;
|
stackSize++;
|
||||||
} else if (value === 19 || value === 20) {
|
} else if (value === 19 || value === 20) {
|
||||||
hints += stackSize >> 1;
|
state.hints += stackSize >> 1;
|
||||||
j += (hints + 7) >> 3; // skipping right amount of hints flag data
|
// skipping right amount of hints flag data
|
||||||
|
j += (state.hints + 7) >> 3;
|
||||||
stackSize %= 2;
|
stackSize %= 2;
|
||||||
validationCommand = CharstringValidationData[value];
|
validationCommand = CharstringValidationData[value];
|
||||||
|
} else if (value === 10 || value === 29) {
|
||||||
|
var subrsIndex;
|
||||||
|
if (value === 10) {
|
||||||
|
subrsIndex = localSubrIndex;
|
||||||
|
} else {
|
||||||
|
subrsIndex = globalSubrIndex;
|
||||||
|
}
|
||||||
|
if (!subrsIndex) {
|
||||||
|
validationCommand = CharstringValidationData[value];
|
||||||
|
warn('Missing subrsIndex for ' + validationCommand.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var bias = 32768;
|
||||||
|
if (subrsIndex.count < 1240) {
|
||||||
|
bias = 107;
|
||||||
|
} else if (subrsIndex.count < 33900) {
|
||||||
|
bias = 1131;
|
||||||
|
}
|
||||||
|
var subrNumber = stack[--stackSize] + bias;
|
||||||
|
if (subrNumber < 0 || subrNumber >= subrsIndex.count) {
|
||||||
|
validationCommand = CharstringValidationData[value];
|
||||||
|
warn('Out of bounds subrIndex for ' + validationCommand.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state.stackSize = stackSize;
|
||||||
|
state.callDepth++;
|
||||||
|
var valid = this.parseCharString(state, subrsIndex.get(subrNumber),
|
||||||
|
localSubrIndex, globalSubrIndex);
|
||||||
|
if (!valid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state.callDepth--;
|
||||||
|
stackSize = state.stackSize;
|
||||||
|
continue;
|
||||||
|
} else if (value === 11) {
|
||||||
|
state.stackSize = stackSize;
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
validationCommand = CharstringValidationData[value];
|
validationCommand = CharstringValidationData[value];
|
||||||
}
|
}
|
||||||
if (validationCommand) {
|
if (validationCommand) {
|
||||||
if (validationCommand.stem) {
|
if (validationCommand.stem) {
|
||||||
hints += stackSize >> 1;
|
state.hints += stackSize >> 1;
|
||||||
}
|
}
|
||||||
if ('min' in validationCommand) {
|
if ('min' in validationCommand) {
|
||||||
if (!undefStack && stackSize < validationCommand.min) {
|
if (!state.undefStack && stackSize < validationCommand.min) {
|
||||||
warn('Not enough parameters for ' + validationCommand.id +
|
warn('Not enough parameters for ' + validationCommand.id +
|
||||||
'; actual: ' + stackSize +
|
'; actual: ' + stackSize +
|
||||||
', expected: ' + validationCommand.min);
|
', expected: ' + validationCommand.min);
|
||||||
valid = false;
|
return false;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (firstStackClearing && validationCommand.stackClearing) {
|
if (state.firstStackClearing && validationCommand.stackClearing) {
|
||||||
firstStackClearing = false;
|
state.firstStackClearing = false;
|
||||||
// the optional character width can be found before the first
|
// the optional character width can be found before the first
|
||||||
// stack-clearing command arguments
|
// stack-clearing command arguments
|
||||||
stackSize -= validationCommand.min;
|
stackSize -= validationCommand.min;
|
||||||
@ -6420,7 +6463,7 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
warn('Found too many parameters for stack-clearing command');
|
warn('Found too many parameters for stack-clearing command');
|
||||||
}
|
}
|
||||||
if (stackSize > 0 && stack[stackSize - 1] >= 0) {
|
if (stackSize > 0 && stack[stackSize - 1] >= 0) {
|
||||||
widths[i] = stack[stackSize - 1];
|
state.width = stack[stackSize - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ('stackDelta' in validationCommand) {
|
if ('stackDelta' in validationCommand) {
|
||||||
@ -6432,14 +6475,65 @@ var CFFParser = (function CFFParserClosure() {
|
|||||||
stackSize = 0;
|
stackSize = 0;
|
||||||
} else if (validationCommand.resetStack) {
|
} else if (validationCommand.resetStack) {
|
||||||
stackSize = 0;
|
stackSize = 0;
|
||||||
undefStack = false;
|
state.undefStack = false;
|
||||||
} else if (validationCommand.undefStack) {
|
} else if (validationCommand.undefStack) {
|
||||||
stackSize = 0;
|
stackSize = 0;
|
||||||
undefStack = true;
|
state.undefStack = true;
|
||||||
firstStackClearing = false;
|
state.firstStackClearing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
state.stackSize = stackSize;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
parseCharStrings: function CFFParser_parseCharStrings(charStrings,
|
||||||
|
localSubrIndex,
|
||||||
|
globalSubrIndex,
|
||||||
|
fdSelect,
|
||||||
|
fdArray) {
|
||||||
|
var seacs = [];
|
||||||
|
var widths = [];
|
||||||
|
var count = charStrings.count;
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var charstring = charStrings.get(i);
|
||||||
|
var state = {
|
||||||
|
callDepth: 0,
|
||||||
|
stackSize: 0,
|
||||||
|
stack: [],
|
||||||
|
undefStack: true,
|
||||||
|
hints: 0,
|
||||||
|
firstStackClearing: true,
|
||||||
|
seac: null,
|
||||||
|
width: null
|
||||||
|
};
|
||||||
|
var valid = true;
|
||||||
|
var localSubrToUse = null;
|
||||||
|
if (fdSelect && fdArray.length) {
|
||||||
|
var fdIndex = fdSelect.getFDIndex(i);
|
||||||
|
if (fdIndex === -1) {
|
||||||
|
warn('Glyph index is not in fd select.');
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (fdIndex >= fdArray.length) {
|
||||||
|
warn('Invalid fd index for glyph index.');
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
localSubrToUse = fdArray[fdIndex].privateDict.subrsIndex;
|
||||||
|
}
|
||||||
|
} else if (localSubrIndex) {
|
||||||
|
localSubrToUse = localSubrIndex;
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
valid = this.parseCharString(state, charstring, localSubrToUse,
|
||||||
|
globalSubrIndex);
|
||||||
|
}
|
||||||
|
if (state.width !== null) {
|
||||||
|
widths[i] = state.width;
|
||||||
|
}
|
||||||
|
if (state.seac !== null) {
|
||||||
|
seacs[i] = state.seac;
|
||||||
|
}
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
// resetting invalid charstring to single 'endchar'
|
// resetting invalid charstring to single 'endchar'
|
||||||
charStrings.set(i, new Uint8Array([14]));
|
charStrings.set(i, new Uint8Array([14]));
|
||||||
@ -6933,6 +7027,14 @@ var CFFFDSelect = (function CFFFDSelectClosure() {
|
|||||||
this.fdSelect = fdSelect;
|
this.fdSelect = fdSelect;
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
}
|
}
|
||||||
|
CFFFDSelect.prototype = {
|
||||||
|
getFDIndex: function CFFFDSelect_get(glyphIndex) {
|
||||||
|
if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return this.fdSelect[glyphIndex];
|
||||||
|
}
|
||||||
|
};
|
||||||
return CFFFDSelect;
|
return CFFFDSelect;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
2
test/pdfs/.gitignore
vendored
2
test/pdfs/.gitignore
vendored
@ -26,6 +26,7 @@
|
|||||||
!issue5564_reduced.pdf
|
!issue5564_reduced.pdf
|
||||||
!canvas.pdf
|
!canvas.pdf
|
||||||
!complex_ttf_font.pdf
|
!complex_ttf_font.pdf
|
||||||
|
!issue3694_reduced.pdf
|
||||||
!extgstate.pdf
|
!extgstate.pdf
|
||||||
!rotation.pdf
|
!rotation.pdf
|
||||||
!simpletype3font.pdf
|
!simpletype3font.pdf
|
||||||
@ -38,6 +39,7 @@
|
|||||||
!close-path-bug.pdf
|
!close-path-bug.pdf
|
||||||
!issue6019.pdf
|
!issue6019.pdf
|
||||||
!issue6621.pdf
|
!issue6621.pdf
|
||||||
|
!issue6286.pdf
|
||||||
!issue1055r.pdf
|
!issue1055r.pdf
|
||||||
!issue1293r.pdf
|
!issue1293r.pdf
|
||||||
!issue1655r.pdf
|
!issue1655r.pdf
|
||||||
|
BIN
test/pdfs/issue3694_reduced.pdf
Normal file
BIN
test/pdfs/issue3694_reduced.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue6286.pdf
Normal file
BIN
test/pdfs/issue6286.pdf
Normal file
Binary file not shown.
@ -376,6 +376,18 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue6286",
|
||||||
|
"file": "pdfs/issue6286.pdf",
|
||||||
|
"md5": "d13fd1b98fb1c9980356314fd1d3a91b",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "issue3694_reduced",
|
||||||
|
"file": "pdfs/issue_3694_reduced.pdf",
|
||||||
|
"md5": "c1438c7bad12d70c4cd684f8ce04448f",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "bug847420",
|
{ "id": "bug847420",
|
||||||
"file": "pdfs/bug847420.pdf",
|
"file": "pdfs/bug847420.pdf",
|
||||||
"md5": "0decd96fec4ef858c2c663a6de24e887",
|
"md5": "0decd96fec4ef858c2c663a6de24e887",
|
||||||
|
@ -101,7 +101,8 @@ describe('font', function() {
|
|||||||
14 // endchar
|
14 // endchar
|
||||||
]);
|
]);
|
||||||
parser.bytes = bytes;
|
parser.bytes = bytes;
|
||||||
var charStrings = parser.parseCharStrings(0).charStrings;
|
var charStringsIndex = parser.parseIndex(0).obj;
|
||||||
|
var charStrings = parser.parseCharStrings(charStringsIndex).charStrings;
|
||||||
expect(charStrings.count).toEqual(1);
|
expect(charStrings.count).toEqual(1);
|
||||||
// shoudn't be sanitized
|
// shoudn't be sanitized
|
||||||
expect(charStrings.get(0).length).toEqual(38);
|
expect(charStrings.get(0).length).toEqual(38);
|
||||||
@ -116,7 +117,8 @@ describe('font', function() {
|
|||||||
0, // offset[0]
|
0, // offset[0]
|
||||||
237, 247, 22, 247, 72, 204, 247, 86, 14]);
|
237, 247, 22, 247, 72, 204, 247, 86, 14]);
|
||||||
parser.bytes = bytes;
|
parser.bytes = bytes;
|
||||||
var result = parser.parseCharStrings(0);
|
var charStringsIndex = parser.parseIndex(0).obj;
|
||||||
|
var result = parser.parseCharStrings(charStringsIndex);
|
||||||
expect(result.charStrings.count).toEqual(1);
|
expect(result.charStrings.count).toEqual(1);
|
||||||
expect(result.charStrings.get(0).length).toEqual(1);
|
expect(result.charStrings.get(0).length).toEqual(1);
|
||||||
expect(result.seacs.length).toEqual(1);
|
expect(result.seacs.length).toEqual(1);
|
||||||
@ -139,7 +141,8 @@ describe('font', function() {
|
|||||||
0, // offset[0]
|
0, // offset[0]
|
||||||
237, 247, 22, 247, 72, 204, 247, 86, 14]);
|
237, 247, 22, 247, 72, 204, 247, 86, 14]);
|
||||||
parser.bytes = bytes;
|
parser.bytes = bytes;
|
||||||
var result = parser.parseCharStrings(0);
|
var charStringsIndex = parser.parseIndex(0).obj;
|
||||||
|
var result = parser.parseCharStrings(charStringsIndex);
|
||||||
expect(result.charStrings.count).toEqual(1);
|
expect(result.charStrings.count).toEqual(1);
|
||||||
expect(result.charStrings.get(0).length).toEqual(9);
|
expect(result.charStrings.get(0).length).toEqual(9);
|
||||||
expect(result.seacs.length).toEqual(0);
|
expect(result.seacs.length).toEqual(0);
|
||||||
@ -154,7 +157,8 @@ describe('font', function() {
|
|||||||
0, // offset[0]
|
0, // offset[0]
|
||||||
14]);
|
14]);
|
||||||
parser.bytes = bytes;
|
parser.bytes = bytes;
|
||||||
var result = parser.parseCharStrings(0);
|
var charStringsIndex = parser.parseIndex(0).obj;
|
||||||
|
var result = parser.parseCharStrings(charStringsIndex);
|
||||||
expect(result.charStrings.count).toEqual(1);
|
expect(result.charStrings.count).toEqual(1);
|
||||||
expect(result.charStrings.get(0)[0]).toEqual(14);
|
expect(result.charStrings.get(0)[0]).toEqual(14);
|
||||||
expect(result.seacs.length).toEqual(0);
|
expect(result.seacs.length).toEqual(0);
|
||||||
|
Loading…
Reference in New Issue
Block a user