Coverage Report

Created: 2026-10-13 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/Parser.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Parser.cc
4
//
5
// Copyright 1996-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <stddef.h>
12
#include <string.h>
13
#include "gmempp.h"
14
#include "Object.h"
15
#include "Array.h"
16
#include "Dict.h"
17
#include "Decrypt.h"
18
#include "Parser.h"
19
#include "XRef.h"
20
#include "Error.h"
21
22
339k
Parser::Parser(XRef *xrefA, Lexer *lexerA, GBool allowStreamsA) {
23
339k
  xref = xrefA;
24
339k
  lexer = lexerA;
25
339k
  inlineImg = 0;
26
339k
  allowStreams = allowStreamsA;
27
339k
  lexer->getObj(&buf1);
28
339k
  lexer->getObj(&buf2);
29
339k
}
30
31
339k
Parser::~Parser() {
32
339k
  buf1.free();
33
339k
  buf2.free();
34
339k
  delete lexer;
35
339k
}
36
37
Object *Parser::getObj(Object *obj, GBool simpleOnly,
38
           Guchar *fileKey,
39
           CryptAlgorithm encAlgorithm, int keyLength,
40
145M
           int objNum, int objGen, int recursion) {
41
145M
  char *key;
42
145M
  Stream *str;
43
145M
  Object obj2;
44
145M
  int num;
45
145M
  DecryptStream *decrypt;
46
145M
  GString *s, *s2;
47
145M
  int c;
48
49
  // refill buffer after inline image data
50
145M
  if (inlineImg == 2) {
51
57.8k
    buf1.free();
52
57.8k
    buf2.free();
53
57.8k
    lexer->getObj(&buf1);
54
57.8k
    lexer->getObj(&buf2);
55
57.8k
    inlineImg = 0;
56
57.8k
  }
57
58
  // array
59
145M
  if (!simpleOnly && recursion < objectRecursionLimit && buf1.isCmd("[")) {
60
650k
    shift();
61
650k
    obj->initArray(xref);
62
19.9M
    while (!buf1.isCmd("]") && !buf1.isEOF())
63
19.3M
      obj->arrayAdd(getObj(&obj2, gFalse, fileKey, encAlgorithm, keyLength,
64
19.3M
         objNum, objGen, recursion + 1));
65
650k
    if (buf1.isEOF())
66
388k
      error(errSyntaxError, getPos(), "End of file inside array");
67
650k
    shift();
68
69
  // dictionary or stream
70
144M
  } else if (!simpleOnly && recursion < objectRecursionLimit &&
71
8.82M
       buf1.isCmd("<<")) {
72
528k
    shift();
73
528k
    obj->initDict(xref);
74
15.7M
    while (!buf1.isCmd(">>") && !buf1.isEOF()) {
75
15.2M
      if (!buf1.isName()) {
76
12.9M
  error(errSyntaxError, getPos(),
77
12.9M
        "Dictionary key must be a name object");
78
12.9M
  shift();
79
12.9M
      } else {
80
2.26M
  key = copyString(buf1.getName());
81
2.26M
  shift();
82
2.26M
  if (buf1.isEOF() || buf1.isError()) {
83
38.7k
    gfree(key);
84
38.7k
    break;
85
38.7k
  }
86
2.22M
  obj->dictAdd(key, getObj(&obj2, gFalse,
87
2.22M
         fileKey, encAlgorithm, keyLength,
88
2.22M
         objNum, objGen, recursion + 1));
89
2.22M
      }
90
15.2M
    }
91
528k
    if (buf1.isEOF())
92
33.3k
      error(errSyntaxError, getPos(), "End of file inside dictionary");
93
    // stream objects are not allowed inside content streams or
94
    // object streams
95
528k
    if (allowStreams && buf2.isCmd("stream")) {
96
94.3k
      if ((str = makeStream(obj, fileKey, encAlgorithm, keyLength,
97
94.3k
          objNum, objGen, recursion + 1))) {
98
86.7k
  obj->initStream(str);
99
86.7k
      } else {
100
7.67k
  obj->free();
101
7.67k
  obj->initError();
102
7.67k
      }
103
434k
    } else {
104
434k
      shift();
105
434k
    }
106
107
  // indirect reference or integer
108
144M
  } else if (buf1.isInt()) {
109
3.75M
    num = buf1.getInt();
110
3.75M
    shift();
111
3.75M
    if (buf1.isInt() && buf2.isCmd("R")) {
112
429k
      int gen = buf1.getInt();
113
429k
      if (num >= 0 && gen >= 0) {
114
426k
  obj->initRef(num, gen);
115
426k
      } else {
116
2.96k
  error(errSyntaxError, getPos(),
117
2.96k
        "Negative number or generation in indirect reference");
118
2.96k
  obj->initError();
119
2.96k
      }
120
429k
      shift();
121
429k
      shift();
122
3.32M
    } else {
123
3.32M
      obj->initInt(num);
124
3.32M
    }
125
126
  // string
127
140M
  } else if (buf1.isString() && fileKey) {
128
20.7k
    s = buf1.getString();
129
20.7k
    s2 = new GString();
130
20.7k
    obj2.initNull();
131
20.7k
    decrypt = new DecryptStream(new MemStream(s->getCString(), 0,
132
20.7k
                s->getLength(), &obj2),
133
20.7k
        fileKey, encAlgorithm, keyLength,
134
20.7k
        objNum, objGen);
135
20.7k
    decrypt->reset();
136
834k
    while ((c = decrypt->getChar()) != EOF) {
137
813k
      s2->append((char)c);
138
813k
    }
139
20.7k
    delete decrypt;
140
20.7k
    obj->initString(s2);
141
20.7k
    shift();
142
143
  // simple object
144
140M
  } else {
145
140M
    buf1.copy(obj);
146
140M
    shift();
147
140M
  }
148
149
145M
  return obj;
150
145M
}
151
152
Stream *Parser::makeStream(Object *dict, Guchar *fileKey,
153
         CryptAlgorithm encAlgorithm, int keyLength,
154
94.3k
         int objNum, int objGen, int recursion) {
155
  // get stream start position
156
94.3k
  lexer->skipToNextLine();
157
94.3k
  Stream *curStr = lexer->getStream();
158
94.3k
  if (!curStr) {
159
1.96k
    return NULL;
160
1.96k
  }
161
92.4k
  GFileOffset pos = curStr->getPos();
162
163
92.4k
  GBool haveLength = gFalse;
164
92.4k
  GFileOffset length = 0;
165
92.4k
  GFileOffset endPos;
166
167
  // check for length in damaged file
168
92.4k
  if (xref && xref->getStreamEnd(pos, &endPos)) {
169
41.1k
    length = endPos - pos;
170
41.1k
    haveLength = gTrue;
171
172
  // get length from the stream object
173
51.3k
  } else {
174
51.3k
    Object obj;
175
51.3k
    dict->dictLookup("Length", &obj, recursion);
176
51.3k
    if (obj.isInt()) {
177
34.7k
      length = (GFileOffset)(Guint)obj.getInt();
178
34.7k
      haveLength = gTrue;
179
34.7k
    } else {
180
16.5k
      error(errSyntaxError, getPos(),
181
16.5k
      "Missing or invalid 'Length' attribute in stream");
182
16.5k
    }
183
51.3k
    obj.free();
184
51.3k
  }
185
186
  // in badly damaged PDF files, we can run off the end of the input
187
  // stream immediately after the "stream" token
188
92.4k
  if (!lexer->getStream()) {
189
0
    return NULL;
190
0
  }
191
192
  // copy the base stream (Lexer will free stream objects when it gets
193
  // to end of stream -- which can happen in the shift() calls below)
194
92.4k
  BaseStream *baseStr =
195
92.4k
      (BaseStream *)lexer->getStream()->getBaseStream()->copy();
196
197
  // 'Length' attribute is missing -- search for 'endstream'
198
92.4k
  if (!haveLength) {
199
16.5k
    GBool foundEndstream = gFalse;
200
16.5k
    char endstreamBuf[8];
201
16.5k
    if ((curStr = lexer->getStream())) {
202
16.5k
      int c;
203
5.23M
      while ((c = curStr->getChar()) != EOF) {
204
5.22M
  if (c == 'e' &&
205
219k
      curStr->getBlock(endstreamBuf, 8) == 8 &&
206
218k
      !memcmp(endstreamBuf, "ndstream", 8)) {
207
10.8k
    length = curStr->getPos() - 9 - pos;
208
10.8k
    foundEndstream = gTrue;
209
10.8k
    break;
210
10.8k
  }
211
5.22M
      }
212
16.5k
    }
213
16.5k
    if (!foundEndstream) {
214
5.70k
      error(errSyntaxError, getPos(), "Couldn't find 'endstream' for stream");
215
5.70k
      delete baseStr;
216
5.70k
      return NULL;
217
5.70k
    }
218
16.5k
  }
219
220
  // make new base stream
221
86.7k
  Stream *str = baseStr->makeSubStream(pos, gTrue, length, dict);
222
223
  // look for the 'endstream' marker
224
86.7k
  if (haveLength) {
225
    // skip over stream data
226
75.8k
    lexer->setPos(pos + length);
227
228
    // check for 'endstream'
229
    // NB: we never reuse the Parser object to parse objects after a
230
    // stream, and we could (if the PDF file is damaged) be in the
231
    // middle of binary data at this point, so we check the stream
232
    // data directly for 'endstream', rather than calling shift() to
233
    // parse objects
234
75.8k
    GBool foundEndstream = gFalse;
235
75.8k
    char endstreamBuf[8];
236
75.8k
    if ((curStr = lexer->getStream())) {
237
      // skip up to 100 whitespace chars
238
75.8k
      int c;
239
185k
      for (int i = 0; i < 100; ++i) {
240
184k
  c = curStr->getChar();
241
184k
  if (!Lexer::isSpace(c)) {
242
74.8k
    break;
243
74.8k
  }
244
184k
      }
245
75.8k
      if (c == 'e') {
246
44.2k
  if (curStr->getBlock(endstreamBuf, 8) == 8 &&
247
44.1k
      !memcmp(endstreamBuf, "ndstream", 8)) {
248
41.2k
    foundEndstream = gTrue;
249
41.2k
  }
250
44.2k
      }
251
75.8k
    }
252
75.8k
    if (!foundEndstream) {
253
34.6k
      error(errSyntaxError, getPos(), "Missing 'endstream'");
254
      // kludge for broken PDF files: just add 5k to the length, and
255
      // hope it's enough
256
      // (dict is now owned by str, so we need to copy it before deleting str)
257
34.6k
      Object obj;
258
34.6k
      dict->copy(&obj);
259
34.6k
      delete str;
260
34.6k
      length += 5000;
261
34.6k
      str = baseStr->makeSubStream(pos, gTrue, length, &obj);
262
34.6k
    }
263
75.8k
  }
264
265
  // free the copied base stream
266
86.7k
  delete baseStr;
267
268
  // handle decryption
269
86.7k
  if (fileKey) {
270
    // the 'Crypt' filter is used to mark unencrypted metadata streams
271
    //~ this should also check for an empty DecodeParams entry
272
5.02k
    GBool encrypted = gTrue;
273
5.02k
    Object obj;
274
5.02k
    dict->dictLookup("Filter", &obj, recursion);
275
5.02k
    if (obj.isName("Crypt")) {
276
51
      encrypted = gFalse;
277
4.97k
    } else if (obj.isArray() && obj.arrayGetLength() >= 1) {
278
632
      Object obj2;
279
632
      if (obj.arrayGet(0, &obj2)->isName("Crypt")) {
280
0
  encrypted = gFalse;
281
0
      }
282
632
      obj2.free();
283
632
    }
284
5.02k
    obj.free();
285
5.02k
    if (encrypted) {
286
4.97k
      str = new DecryptStream(str, fileKey, encAlgorithm, keyLength,
287
4.97k
            objNum, objGen);
288
4.97k
    }
289
5.02k
  }
290
291
  // get filters
292
86.7k
  str = str->addFilters(dict, recursion);
293
294
86.7k
  return str;
295
92.4k
}
296
297
162M
void Parser::shift() {
298
162M
  if (inlineImg > 0) {
299
528k
    if (inlineImg < 2) {
300
293k
      ++inlineImg;
301
293k
    } else {
302
      // in a damaged content stream, if 'ID' shows up in the middle
303
      // of a dictionary, we need to reset
304
235k
      inlineImg = 0;
305
235k
    }
306
161M
  } else if (buf2.isCmd("ID")) {
307
293k
    lexer->skipChar();    // skip char after 'ID' command
308
293k
    inlineImg = 1;
309
293k
  }
310
162M
  buf1.free();
311
162M
  buf1 = buf2;
312
162M
  if (inlineImg > 0)    // don't buffer inline image data
313
586k
    buf2.initNull();
314
161M
  else
315
161M
    lexer->getObj(&buf2);
316
162M
}