Avoid infinite loop when getting annotation field name
- aims to fix issue #12963; - use a Set to track already visited objects; - remove the loop limit in getInheritableProperty and use a RefSet too.
This commit is contained in:
parent
f892c00275
commit
0fc8267576
@ -39,7 +39,15 @@ import {
|
||||
createDefaultAppearance,
|
||||
parseDefaultAppearance,
|
||||
} from "./default_appearance.js";
|
||||
import { Dict, isDict, isName, isRef, isStream, Name } from "./primitives.js";
|
||||
import {
|
||||
Dict,
|
||||
isDict,
|
||||
isName,
|
||||
isRef,
|
||||
isStream,
|
||||
Name,
|
||||
RefSet,
|
||||
} from "./primitives.js";
|
||||
import { ColorSpace } from "./colorspace.js";
|
||||
import { OperatorList } from "./operator_list.js";
|
||||
import { StringStream } from "./stream.js";
|
||||
@ -1066,14 +1074,27 @@ class WidgetAnnotation extends Annotation {
|
||||
}
|
||||
|
||||
let loopDict = dict;
|
||||
const visited = new RefSet();
|
||||
if (dict.objId) {
|
||||
visited.put(dict.objId);
|
||||
}
|
||||
while (loopDict.has("Parent")) {
|
||||
loopDict = loopDict.get("Parent");
|
||||
if (!isDict(loopDict)) {
|
||||
if (
|
||||
!(loopDict instanceof Dict) ||
|
||||
(loopDict.objId && visited.has(loopDict.objId))
|
||||
) {
|
||||
// Even though it is not allowed according to the PDF specification,
|
||||
// bad PDF generators may provide a `Parent` entry that is not a
|
||||
// dictionary, but `null` for example (issue 8143).
|
||||
//
|
||||
// If parent has been already visited, it means that we're
|
||||
// in an infinite loop.
|
||||
break;
|
||||
}
|
||||
if (loopDict.objId) {
|
||||
visited.put(loopDict.objId);
|
||||
}
|
||||
|
||||
if (loopDict.has("T")) {
|
||||
fieldName.unshift(stringToPDFString(loopDict.get("T")));
|
||||
|
@ -19,7 +19,6 @@ import {
|
||||
bytesToString,
|
||||
objectSize,
|
||||
stringToPDFString,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { Dict, isName, isRef, isStream, RefSet } from "./primitives.js";
|
||||
|
||||
@ -72,8 +71,7 @@ class XRefParseException extends BaseException {}
|
||||
*
|
||||
* If the key is not found in the tree, `undefined` is returned. Otherwise,
|
||||
* the value for the key is returned or, if `stopWhenFound` is `false`, a list
|
||||
* of values is returned. To avoid infinite loops, the traversal is stopped when
|
||||
* the loop limit is reached.
|
||||
* of values is returned.
|
||||
*
|
||||
* @param {Dict} dict - Dictionary from where to start the traversal.
|
||||
* @param {string} key - The key of the property to find the value for.
|
||||
@ -90,11 +88,13 @@ function getInheritableProperty({
|
||||
getArray = false,
|
||||
stopWhenFound = true,
|
||||
}) {
|
||||
const LOOP_LIMIT = 100;
|
||||
let loopCount = 0;
|
||||
let values;
|
||||
const visited = new RefSet();
|
||||
|
||||
while (dict) {
|
||||
while (dict instanceof Dict && !(dict.objId && visited.has(dict.objId))) {
|
||||
if (dict.objId) {
|
||||
visited.put(dict.objId);
|
||||
}
|
||||
const value = getArray ? dict.getArray(key) : dict.get(key);
|
||||
if (value !== undefined) {
|
||||
if (stopWhenFound) {
|
||||
@ -105,10 +105,6 @@ function getInheritableProperty({
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
if (++loopCount > LOOP_LIMIT) {
|
||||
warn(`getInheritableProperty: maximum loop count exceeded for "${key}"`);
|
||||
break;
|
||||
}
|
||||
dict = dict.get("Parent");
|
||||
}
|
||||
return values;
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -76,6 +76,7 @@
|
||||
!issue8798r.pdf
|
||||
!issue8823.pdf
|
||||
!issue9084.pdf
|
||||
!issue12963.pdf
|
||||
!issue9105_reduced.pdf
|
||||
!issue9252.pdf
|
||||
!issue9262_reduced.pdf
|
||||
|
BIN
test/pdfs/issue12963.pdf
Normal file
BIN
test/pdfs/issue12963.pdf
Normal file
Binary file not shown.
@ -2445,6 +2445,15 @@
|
||||
"lastPage": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue12963",
|
||||
"file": "pdfs/issue12963.pdf",
|
||||
"md5": "2590047a2ef0113e698d888c29918008",
|
||||
"rounds": 1,
|
||||
"firstPage": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"annotations": true
|
||||
},
|
||||
{ "id": "multiple-filters-length-zero",
|
||||
"file": "pdfs/multiple-filters-length-zero.pdf",
|
||||
"md5": "c273c3a6fb79cbf3034fe1b62b204728",
|
||||
|
@ -123,30 +123,6 @@ describe("core_utils", function () {
|
||||
["qux2", "quux"],
|
||||
]);
|
||||
});
|
||||
|
||||
it("stops searching when the loop limit is reached", function () {
|
||||
const dict = new Dict();
|
||||
let currentDict = dict;
|
||||
let parentDict = null;
|
||||
// Exceed the loop limit of 100.
|
||||
for (let i = 0; i < 150; i++) {
|
||||
parentDict = new Dict();
|
||||
currentDict.set("Parent", parentDict);
|
||||
currentDict = parentDict;
|
||||
}
|
||||
parentDict.set("foo", "bar"); // Never found because of loop limit.
|
||||
expect(getInheritableProperty({ dict, key: "foo" })).toEqual(undefined);
|
||||
|
||||
dict.set("foo", "baz");
|
||||
expect(
|
||||
getInheritableProperty({
|
||||
dict,
|
||||
key: "foo",
|
||||
getArray: false,
|
||||
stopWhenFound: false,
|
||||
})
|
||||
).toEqual(["baz"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("toRomanNumerals", function () {
|
||||
|
Loading…
Reference in New Issue
Block a user