Merge pull request #10010 from Snuffleupagus/issue-10004
Attempt to find truncated endstream commands, in the fallback code-path, in `Parser.makeStream` (issue 10004)
This commit is contained in:
commit
66bd088948
@ -18,8 +18,8 @@ import {
|
|||||||
PredictorStream, RunLengthStream
|
PredictorStream, RunLengthStream
|
||||||
} from './stream';
|
} from './stream';
|
||||||
import {
|
import {
|
||||||
assert, FormatError, info, isNum, isSpace, isString, MissingDataException,
|
assert, bytesToString, FormatError, info, isNum, isSpace, isString,
|
||||||
StreamType, warn
|
MissingDataException, StreamType, warn
|
||||||
} from '../shared/util';
|
} from '../shared/util';
|
||||||
import {
|
import {
|
||||||
Cmd, Dict, EOF, isCmd, isDict, isEOF, isName, Name, Ref
|
Cmd, Dict, EOF, isCmd, isDict, isEOF, isName, Name, Ref
|
||||||
@ -471,13 +471,45 @@ var Parser = (function ParserClosure() {
|
|||||||
|
|
||||||
return imageStream;
|
return imageStream;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_findStreamLength(startPos, signature) {
|
||||||
|
const { stream, } = this.lexer;
|
||||||
|
stream.pos = startPos;
|
||||||
|
|
||||||
|
const SCAN_BLOCK_LENGTH = 2048;
|
||||||
|
const signatureLength = signature.length;
|
||||||
|
|
||||||
|
while (stream.pos < stream.end) {
|
||||||
|
const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);
|
||||||
|
const scanLength = scanBytes.length - signatureLength;
|
||||||
|
|
||||||
|
if (scanLength <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let pos = 0;
|
||||||
|
while (pos < scanLength) {
|
||||||
|
let j = 0;
|
||||||
|
while (j < signatureLength && scanBytes[pos + j] === signature[j]) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (j >= signatureLength) { // `signature` found.
|
||||||
|
stream.pos += pos;
|
||||||
|
return (stream.pos - startPos);
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
stream.pos += scanLength;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
makeStream: function Parser_makeStream(dict, cipherTransform) {
|
makeStream: function Parser_makeStream(dict, cipherTransform) {
|
||||||
var lexer = this.lexer;
|
var lexer = this.lexer;
|
||||||
var stream = lexer.stream;
|
var stream = lexer.stream;
|
||||||
|
|
||||||
// get stream start position
|
// get stream start position
|
||||||
lexer.skipToNextLine();
|
lexer.skipToNextLine();
|
||||||
var pos = stream.pos - 1;
|
const startPos = stream.pos - 1;
|
||||||
|
|
||||||
// get length
|
// get length
|
||||||
var length = dict.get('Length');
|
var length = dict.get('Length');
|
||||||
@ -487,52 +519,49 @@ var Parser = (function ParserClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skip over the stream data
|
// skip over the stream data
|
||||||
stream.pos = pos + length;
|
stream.pos = startPos + length;
|
||||||
lexer.nextChar();
|
lexer.nextChar();
|
||||||
|
|
||||||
// Shift '>>' and check whether the new object marks the end of the stream
|
// Shift '>>' and check whether the new object marks the end of the stream
|
||||||
if (this.tryShift() && isCmd(this.buf2, 'endstream')) {
|
if (this.tryShift() && isCmd(this.buf2, 'endstream')) {
|
||||||
this.shift(); // 'stream'
|
this.shift(); // 'stream'
|
||||||
} else {
|
} else {
|
||||||
// bad stream length, scanning for endstream
|
// Bad stream length, scanning for endstream command.
|
||||||
stream.pos = pos;
|
const ENDSTREAM_SIGNATURE = new Uint8Array([
|
||||||
var SCAN_BLOCK_SIZE = 2048;
|
0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D]);
|
||||||
var ENDSTREAM_SIGNATURE_LENGTH = 9;
|
let actualLength = this._findStreamLength(startPos,
|
||||||
var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65,
|
ENDSTREAM_SIGNATURE);
|
||||||
0x61, 0x6D];
|
if (actualLength < 0) {
|
||||||
var skipped = 0, found = false, i, j;
|
// Only allow limited truncation of the endstream signature,
|
||||||
while (stream.pos < stream.end) {
|
// to prevent false positives.
|
||||||
var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE);
|
const MAX_TRUNCATION = 1;
|
||||||
var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH;
|
// Check if the PDF generator included truncated endstream commands,
|
||||||
if (scanLength <= 0) {
|
// such as e.g. "endstrea" (fixes issue10004.pdf).
|
||||||
break;
|
for (let i = 1; i <= MAX_TRUNCATION; i++) {
|
||||||
}
|
const end = ENDSTREAM_SIGNATURE.length - i;
|
||||||
found = false;
|
const TRUNCATED_SIGNATURE = ENDSTREAM_SIGNATURE.slice(0, end);
|
||||||
i = 0;
|
|
||||||
while (i < scanLength) {
|
let maybeLength = this._findStreamLength(startPos,
|
||||||
j = 0;
|
TRUNCATED_SIGNATURE);
|
||||||
while (j < ENDSTREAM_SIGNATURE_LENGTH &&
|
if (maybeLength >= 0) {
|
||||||
scanBytes[i + j] === ENDSTREAM_SIGNATURE[j]) {
|
// Ensure that the byte immediately following the truncated
|
||||||
j++;
|
// endstream command is a space, to prevent false positives.
|
||||||
}
|
const lastByte = stream.peekBytes(end + 1)[end];
|
||||||
if (j >= ENDSTREAM_SIGNATURE_LENGTH) {
|
if (!isSpace(lastByte)) {
|
||||||
found = true;
|
break;
|
||||||
|
}
|
||||||
|
info(`Found "${bytesToString(TRUNCATED_SIGNATURE)}" when ` +
|
||||||
|
'searching for endstream command.');
|
||||||
|
actualLength = maybeLength;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
if (found) {
|
|
||||||
skipped += i;
|
if (actualLength < 0) {
|
||||||
stream.pos += i;
|
throw new FormatError('Missing endstream command.');
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
skipped += scanLength;
|
|
||||||
stream.pos += scanLength;
|
|
||||||
}
|
}
|
||||||
if (!found) {
|
length = actualLength;
|
||||||
throw new FormatError('Missing endstream');
|
|
||||||
}
|
|
||||||
length = skipped;
|
|
||||||
|
|
||||||
lexer.nextChar();
|
lexer.nextChar();
|
||||||
this.shift();
|
this.shift();
|
||||||
@ -540,7 +569,7 @@ var Parser = (function ParserClosure() {
|
|||||||
}
|
}
|
||||||
this.shift(); // 'endstream'
|
this.shift(); // 'endstream'
|
||||||
|
|
||||||
stream = stream.makeSubStream(pos, length, dict);
|
stream = stream.makeSubStream(startPos, length, dict);
|
||||||
if (cipherTransform) {
|
if (cipherTransform) {
|
||||||
stream = cipherTransform.createStream(stream, length);
|
stream = cipherTransform.createStream(stream, length);
|
||||||
}
|
}
|
||||||
|
1
test/pdfs/issue10004.pdf.link
Normal file
1
test/pdfs/issue10004.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/mozilla/pdf.js/files/2315390/2371410.pdf
|
@ -726,6 +726,13 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue10004",
|
||||||
|
"file": "pdfs/issue10004.pdf",
|
||||||
|
"md5": "64d1853060cefe3be50e5c4617dd0505",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "load"
|
||||||
|
},
|
||||||
{ "id": "issue7507",
|
{ "id": "issue7507",
|
||||||
"file": "pdfs/issue7507.pdf",
|
"file": "pdfs/issue7507.pdf",
|
||||||
"md5": "f7aeaafe0c89b94436e94eaa63307303",
|
"md5": "f7aeaafe0c89b94436e94eaa63307303",
|
||||||
|
Loading…
Reference in New Issue
Block a user