Coverage Report

Created: 2026-08-11 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/fofi/FoFiIdentifier.cc
Line
Count
Source
1
//========================================================================
2
//
3
// FoFiIdentifier.cc
4
//
5
// Copyright 2009 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
//========================================================================
10
//
11
// Modified under the Poppler project - http://poppler.freedesktop.org
12
//
13
// All changes made under the Poppler project to this file are licensed
14
// under GPL version 2 or later
15
//
16
// Copyright (C) 2013 Christoph Duelli <duelli@melosgmbh.de>
17
// Copyright (C) 2018, 2021, 2025 Albert Astals Cid <aacid@kde.org>
18
// Copyright (C) 2019 Christian Persch <chpe@src.gnome.org>
19
// Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com>
20
// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
21
//
22
// To see a description of the changes please see the Changelog file that
23
// came with your tarball or type make ChangeLog if you are building from git
24
//
25
//========================================================================
26
27
#include <cstdio>
28
#include <cstring>
29
#include <climits>
30
#include "goo/gfile.h"
31
#include "goo/GooCheckedOps.h"
32
#include "FoFiIdentifier.h"
33
34
//------------------------------------------------------------------------
35
36
namespace { // do not pollute global namespace
37
38
class Reader
39
{
40
public:
41
2.99k
    Reader() = default;
42
    Reader(const Reader &) = delete;
43
    Reader &operator=(const Reader &other) = delete;
44
45
2.99k
    virtual ~Reader() = default;
46
47
    // Read one byte.  Returns -1 if past EOF.
48
    virtual int getByte(int pos) = 0;
49
50
    // Read a big-endian unsigned 16-bit integer.  Fills in *val and
51
    // returns true if successful.
52
    virtual bool getU16BE(int pos, int *val) = 0;
53
54
    // Read a big-endian unsigned 32-bit integer.  Fills in *val and
55
    // returns true if successful.
56
    virtual bool getU32BE(int pos, unsigned int *val) = 0;
57
58
    // Read a little-endian unsigned 32-bit integer.  Fills in *val and
59
    // returns true if successful.
60
    virtual bool getU32LE(int pos, unsigned int *val) = 0;
61
62
    // Read a big-endian unsigned <size>-byte integer, where 1 <= size
63
    // <= 4.  Fills in *val and returns true if successful.
64
    virtual bool getUVarBE(int pos, int size, unsigned int *val) = 0;
65
66
    // Compare against a string.  Returns true if equal.
67
    virtual bool cmp(int pos, const char *s) = 0;
68
};
69
70
//------------------------------------------------------------------------
71
72
class FileReader : public Reader
73
{
74
    class PrivateTag
75
    {
76
    };
77
78
public:
79
    static std::unique_ptr<FileReader> make(const char *fileName);
80
    ~FileReader() override;
81
    int getByte(int pos) override;
82
    bool getU16BE(int pos, int *val) override;
83
    bool getU32BE(int pos, unsigned int *val) override;
84
    bool getU32LE(int pos, unsigned int *val) override;
85
    bool getUVarBE(int pos, int size, unsigned int *val) override;
86
    bool cmp(int pos, const char *s) override;
87
88
    explicit FileReader(FILE *fA);
89
90
private:
91
    bool fillBuf(int pos, int len);
92
93
    FILE *f;
94
    char buf[1024];
95
    int bufPos, bufLen;
96
};
97
98
std::unique_ptr<FileReader> FileReader::make(const char *fileName)
99
0
{
100
0
    FILE *fA;
101
102
0
    if (!(fA = openFile(fileName, "rb"))) {
103
0
        return nullptr;
104
0
    }
105
0
    return std::make_unique<FileReader>(fA);
106
0
}
107
108
FileReader::FileReader(FILE *fA)
109
0
{
110
0
    f = fA;
111
0
    bufPos = 0;
112
0
    bufLen = 0;
113
0
}
114
115
FileReader::~FileReader()
116
0
{
117
0
    fclose(f);
118
0
}
119
120
int FileReader::getByte(int pos)
121
0
{
122
0
    if (!fillBuf(pos, 1)) {
123
0
        return -1;
124
0
    }
125
0
    return buf[pos - bufPos] & 0xff;
126
0
}
127
128
bool FileReader::getU16BE(int pos, int *val)
129
0
{
130
0
    if (!fillBuf(pos, 2)) {
131
0
        return false;
132
0
    }
133
0
    *val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff);
134
0
    return true;
135
0
}
136
137
bool FileReader::getU32BE(int pos, unsigned int *val)
138
0
{
139
0
    if (!fillBuf(pos, 4)) {
140
0
        return false;
141
0
    }
142
0
    *val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff);
143
0
    return true;
144
0
}
145
146
bool FileReader::getU32LE(int pos, unsigned int *val)
147
0
{
148
0
    if (!fillBuf(pos, 4)) {
149
0
        return false;
150
0
    }
151
0
    *val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24);
152
0
    return true;
153
0
}
154
155
bool FileReader::getUVarBE(int pos, int size, unsigned int *val)
156
0
{
157
0
    int i;
158
159
0
    if (size < 1 || size > 4 || !fillBuf(pos, size)) {
160
0
        return false;
161
0
    }
162
0
    *val = 0;
163
0
    for (i = 0; i < size; ++i) {
164
0
        *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
165
0
    }
166
0
    return true;
167
0
}
168
169
bool FileReader::cmp(int pos, const char *s)
170
0
{
171
0
    int n;
172
173
0
    n = static_cast<int>(strlen(s));
174
0
    if (!fillBuf(pos, n)) {
175
0
        return false;
176
0
    }
177
0
    return !memcmp(buf - bufPos + pos, s, n);
178
0
}
179
180
bool FileReader::fillBuf(int pos, int len)
181
0
{
182
0
    constexpr int bufSize = sizeof(buf);
183
0
    if (pos < 0 || len < 0 || len > bufSize || pos > INT_MAX - bufSize) {
184
0
        return false;
185
0
    }
186
0
    if (pos >= bufPos && pos + len <= bufPos + bufLen) {
187
0
        return true;
188
0
    }
189
0
    if (fseek(f, pos, SEEK_SET)) {
190
0
        return false;
191
0
    }
192
0
    bufPos = pos;
193
0
    bufLen = static_cast<int>(fread(buf, 1, sizeof(buf), f));
194
0
    return bufLen >= len;
195
0
}
196
197
//------------------------------------------------------------------------
198
199
class StreamReader : public Reader
200
{
201
    class PrivateTag
202
    {
203
    };
204
205
public:
206
    static std::unique_ptr<StreamReader> make(int (*getCharA)(void *data), void *dataA);
207
    ~StreamReader() override;
208
    int getByte(int pos) override;
209
    bool getU16BE(int pos, int *val) override;
210
    bool getU32BE(int pos, unsigned int *val) override;
211
    bool getU32LE(int pos, unsigned int *val) override;
212
    bool getUVarBE(int pos, int size, unsigned int *val) override;
213
    bool cmp(int pos, const char *s) override;
214
215
    StreamReader(int (*getCharA)(void *data), void *dataA, PrivateTag /*unused*/ = {});
216
217
private:
218
    bool fillBuf(int pos, int len);
219
220
    int (*getChar)(void *data);
221
    void *data;
222
    int streamPos;
223
    char buf[1024];
224
    int bufPos, bufLen;
225
};
226
227
std::unique_ptr<StreamReader> StreamReader::make(int (*getCharA)(void *data), void *dataA)
228
2.99k
{
229
2.99k
    return std::make_unique<StreamReader>(getCharA, dataA);
230
2.99k
}
231
232
StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA, PrivateTag /*unused*/)
233
2.99k
{
234
2.99k
    getChar = getCharA;
235
2.99k
    data = dataA;
236
2.99k
    streamPos = 0;
237
2.99k
    bufPos = 0;
238
2.99k
    bufLen = 0;
239
2.99k
}
240
241
StreamReader::~StreamReader() = default;
242
243
int StreamReader::getByte(int pos)
244
21.9k
{
245
21.9k
    if (!fillBuf(pos, 1)) {
246
1.46k
        return -1;
247
1.46k
    }
248
20.4k
    return buf[pos - bufPos] & 0xff;
249
21.9k
}
250
251
bool StreamReader::getU16BE(int pos, int *val)
252
483
{
253
483
    if (!fillBuf(pos, 2)) {
254
69
        return false;
255
69
    }
256
414
    *val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff);
257
414
    return true;
258
483
}
259
260
bool StreamReader::getU32BE(int pos, unsigned int *val)
261
0
{
262
0
    if (!fillBuf(pos, 4)) {
263
0
        return false;
264
0
    }
265
0
    *val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff);
266
0
    return true;
267
0
}
268
269
bool StreamReader::getU32LE(int pos, unsigned int *val)
270
6
{
271
6
    if (!fillBuf(pos, 4)) {
272
3
        return false;
273
3
    }
274
3
    *val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24);
275
3
    return true;
276
6
}
277
278
bool StreamReader::getUVarBE(int pos, int size, unsigned int *val)
279
390
{
280
390
    int i;
281
282
390
    if (size < 1 || size > 4 || !fillBuf(pos, size)) {
283
14
        return false;
284
14
    }
285
376
    *val = 0;
286
1.26k
    for (i = 0; i < size; ++i) {
287
885
        *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
288
885
    }
289
376
    return true;
290
390
}
291
292
bool StreamReader::cmp(int pos, const char *s)
293
5.99k
{
294
5.99k
    const int n = static_cast<int>(strlen(s));
295
5.99k
    if (!fillBuf(pos, n)) {
296
629
        return false;
297
629
    }
298
5.36k
    const int posDiff = pos - bufPos;
299
5.36k
    return !memcmp(buf + posDiff, s, n);
300
5.99k
}
301
302
bool StreamReader::fillBuf(int pos, int len)
303
28.8k
{
304
28.8k
    int c;
305
306
28.8k
    constexpr int bufSize = sizeof(buf);
307
28.8k
    if (pos < 0 || len < 0 || len > bufSize || pos > INT_MAX - bufSize) {
308
0
        return false;
309
0
    }
310
28.8k
    if (pos < bufPos) {
311
0
        return false;
312
0
    }
313
314
    // if requested region will not fit in the current buffer...
315
28.8k
    if (pos + len > bufPos + bufSize) {
316
317
        // if the start of the requested data is already in the buffer, move
318
        // it to the start of the buffer
319
95
        if (pos < bufPos + bufLen) {
320
0
            bufLen -= pos - bufPos;
321
0
            memmove(buf, buf + (pos - bufPos), bufLen);
322
0
            bufPos = pos;
323
324
            // otherwise discard data from the
325
            // stream until we get to the requested position
326
95
        } else {
327
95
            bufPos += bufLen;
328
95
            bufLen = 0;
329
100k
            while (bufPos < pos) {
330
100k
                if ((c = (*getChar)(data)) < 0) {
331
87
                    return false;
332
87
                }
333
100k
                ++bufPos;
334
100k
            }
335
95
        }
336
95
    }
337
338
    // read the rest of the requested data
339
84.5k
    while (bufPos + bufLen < pos + len) {
340
57.8k
        if ((c = (*getChar)(data)) < 0) {
341
2.09k
            return false;
342
2.09k
        }
343
55.7k
        buf[bufLen++] = static_cast<char>(c);
344
55.7k
    }
345
346
26.6k
    return true;
347
28.7k
}
348
349
}
350
351
//------------------------------------------------------------------------
352
353
static FoFiIdentifierType identify(Reader *reader);
354
static FoFiIdentifierType identifyOpenType(Reader *reader);
355
static FoFiIdentifierType identifyCFF(Reader *reader, int start);
356
357
FoFiIdentifierType FoFiIdentifier::identifyFile(const char *fileName)
358
0
{
359
360
0
    std::unique_ptr<FileReader> reader = FileReader::make(fileName);
361
0
    if (!reader) {
362
0
        return fofiIdError;
363
0
    }
364
0
    return identify(reader.get());
365
0
}
366
367
FoFiIdentifierType FoFiIdentifier::identifyStream(int (*getChar)(void *data), void *data)
368
2.99k
{
369
2.99k
    std::unique_ptr<StreamReader> reader = StreamReader::make(getChar, data);
370
371
2.99k
    if (!reader) {
372
0
        return fofiIdError;
373
0
    }
374
2.99k
    return identify(reader.get());
375
2.99k
}
376
377
static FoFiIdentifierType identify(Reader *reader)
378
2.99k
{
379
2.99k
    unsigned int n;
380
381
    //----- PFA
382
2.99k
    if (reader->cmp(0, "%!PS-AdobeFont-1") || reader->cmp(0, "%!FontType1")) {
383
0
        return fofiIdType1PFA;
384
0
    }
385
386
    //----- PFB
387
2.99k
    if (reader->getByte(0) == 0x80 && reader->getByte(1) == 0x01 && reader->getU32LE(2, &n)) {
388
3
        if ((n >= 16 && reader->cmp(6, "%!PS-AdobeFont-1")) || (n >= 11 && reader->cmp(6, "%!FontType1"))) {
389
0
            return fofiIdType1PFB;
390
0
        }
391
3
    }
392
393
    //----- TrueType
394
2.99k
    if ((reader->getByte(0) == 0x00 && reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00 && reader->getByte(3) == 0x00)
395
2.99k
        || (reader->getByte(0) == 0x74 && // 'true'
396
1.53k
            reader->getByte(1) == 0x72 && reader->getByte(2) == 0x75 && reader->getByte(3) == 0x65)) {
397
1.48k
        return fofiIdTrueType;
398
1.48k
    }
399
1.51k
    if (reader->getByte(0) == 0x74 && // 'ttcf'
400
57
        reader->getByte(1) == 0x74 && reader->getByte(2) == 0x63 && reader->getByte(3) == 0x66) {
401
0
        return fofiIdTrueTypeCollection;
402
0
    }
403
404
    //----- OpenType
405
1.51k
    if (reader->getByte(0) == 0x4f && // 'OTTO
406
56
        reader->getByte(1) == 0x54 && reader->getByte(2) == 0x54 && reader->getByte(3) == 0x4f) {
407
0
        return identifyOpenType(reader);
408
0
    }
409
410
    //----- CFF
411
1.51k
    if (reader->getByte(0) == 0x01 && reader->getByte(1) == 0x00) {
412
213
        return identifyCFF(reader, 0);
413
213
    }
414
    // some tools embed CFF fonts with an extra whitespace char at the
415
    // beginning
416
1.30k
    if (reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00) {
417
65
        return identifyCFF(reader, 1);
418
65
    }
419
420
1.23k
    return fofiIdUnknown;
421
1.30k
}
422
423
static FoFiIdentifierType identifyOpenType(Reader *reader)
424
0
{
425
0
    FoFiIdentifierType type;
426
0
    unsigned int offset;
427
0
    int nTables, i;
428
429
0
    if (!reader->getU16BE(4, &nTables)) {
430
0
        return fofiIdUnknown;
431
0
    }
432
0
    for (i = 0; i < nTables; ++i) {
433
0
        if (reader->cmp(12 + i * 16, "CFF ")) {
434
0
            if (reader->getU32BE(12 + i * 16 + 8, &offset) && offset < static_cast<unsigned int>(INT_MAX)) {
435
0
                type = identifyCFF(reader, static_cast<int>(offset));
436
0
                if (type == fofiIdCFF8Bit) {
437
0
                    type = fofiIdOpenTypeCFF8Bit;
438
0
                } else if (type == fofiIdCFFCID) {
439
0
                    type = fofiIdOpenTypeCFFCID;
440
0
                }
441
0
                return type;
442
0
            }
443
0
            return fofiIdUnknown;
444
0
        }
445
0
    }
446
0
    return fofiIdUnknown;
447
0
}
448
449
static FoFiIdentifierType identifyCFF(Reader *reader, int start)
450
278
{
451
278
    unsigned int offset0, offset1;
452
278
    int hdrSize, offSize0, offSize1, pos, endPos, b0, n, i;
453
454
    //----- read the header
455
278
    if (reader->getByte(start) != 0x01 || reader->getByte(start + 1) != 0x00) {
456
0
        return fofiIdUnknown;
457
0
    }
458
278
    if ((hdrSize = reader->getByte(start + 2)) < 0) {
459
7
        return fofiIdUnknown;
460
7
    }
461
271
    if ((offSize0 = reader->getByte(start + 3)) < 1 || offSize0 > 4) {
462
8
        return fofiIdUnknown;
463
8
    }
464
263
    pos = start + hdrSize;
465
263
    if (pos < 0) {
466
0
        return fofiIdUnknown;
467
0
    }
468
469
    //----- skip the name index
470
263
    if (!reader->getU16BE(pos, &n)) {
471
17
        return fofiIdUnknown;
472
17
    }
473
246
    if (n == 0) {
474
143
        pos += 2;
475
143
    } else {
476
103
        if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
477
10
            return fofiIdUnknown;
478
10
        }
479
93
        if (!reader->getUVarBE(pos + 3 + n * offSize1, offSize1, &offset1) || offset1 > static_cast<unsigned int>(INT_MAX)) {
480
16
            return fofiIdUnknown;
481
16
        }
482
77
        pos += 3 + (n + 1) * offSize1 + static_cast<int>(offset1) - 1;
483
77
    }
484
220
    if (pos < 0) {
485
0
        return fofiIdUnknown;
486
0
    }
487
488
    //----- parse the top dict index
489
220
    if (!reader->getU16BE(pos, &n) || n < 1) {
490
58
        return fofiIdUnknown;
491
58
    }
492
162
    if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
493
4
        return fofiIdUnknown;
494
4
    }
495
158
    if (!reader->getUVarBE(pos + 3, offSize1, &offset0) || offset0 > static_cast<unsigned int>(INT_MAX) || !reader->getUVarBE(pos + 3 + offSize1, offSize1, &offset1) || offset1 > static_cast<unsigned int>(INT_MAX) || offset0 > offset1) {
496
33
        return fofiIdUnknown;
497
33
    }
498
125
    if (checkedAdd(pos + 3 + (n + 1) * offSize1, static_cast<int>(offset0) - 1, &pos) || checkedAdd(pos + 3 + (n + 1) * offSize1, static_cast<int>(offset1) - 1, &endPos) || pos < 0 || endPos < 0 || pos > endPos) {
499
0
        return fofiIdUnknown;
500
0
    }
501
502
    //----- parse the top dict, look for ROS as first entry
503
    // for a CID font, the top dict starts with:
504
    //     <int> <int> <int> ROS
505
263
    for (i = 0; i < 3; ++i) {
506
237
        b0 = reader->getByte(pos++);
507
237
        if (b0 == 0x1c) {
508
0
            pos += 2;
509
237
        } else if (b0 == 0x1d) {
510
0
            pos += 4;
511
237
        } else if (b0 >= 0xf7 && b0 <= 0xfe) {
512
19
            pos += 1;
513
218
        } else if (b0 < 0x20 || b0 > 0xf6) {
514
99
            return fofiIdCFF8Bit;
515
99
        }
516
138
        if (pos >= endPos || pos < 0) {
517
0
            return fofiIdCFF8Bit;
518
0
        }
519
138
    }
520
26
    if (pos + 1 < endPos && reader->getByte(pos) == 12 && reader->getByte(pos + 1) == 30) {
521
0
        return fofiIdCFFCID;
522
0
    }
523
26
    return fofiIdCFF8Bit;
524
26
}