Coverage Report

Created: 2026-10-14 06:52

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
8.24k
    Reader() = default;
42
    Reader(const Reader &) = delete;
43
    Reader &operator=(const Reader &other) = delete;
44
45
8.24k
    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
601
{
100
601
    FILE *fA;
101
102
601
    if (!(fA = openFile(fileName, "rb"))) {
103
0
        return nullptr;
104
0
    }
105
601
    return std::make_unique<FileReader>(fA);
106
601
}
107
108
FileReader::FileReader(FILE *fA)
109
601
{
110
601
    f = fA;
111
601
    bufPos = 0;
112
601
    bufLen = 0;
113
601
}
114
115
FileReader::~FileReader()
116
601
{
117
601
    fclose(f);
118
601
}
119
120
int FileReader::getByte(int pos)
121
3.00k
{
122
3.00k
    if (!fillBuf(pos, 1)) {
123
0
        return -1;
124
0
    }
125
3.00k
    return buf[pos - bufPos] & 0xff;
126
3.00k
}
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
1.20k
{
171
1.20k
    int n;
172
173
1.20k
    n = static_cast<int>(strlen(s));
174
1.20k
    if (!fillBuf(pos, n)) {
175
0
        return false;
176
0
    }
177
1.20k
    return !memcmp(buf - bufPos + pos, s, n);
178
1.20k
}
179
180
bool FileReader::fillBuf(int pos, int len)
181
4.20k
{
182
4.20k
    constexpr int bufSize = sizeof(buf);
183
4.20k
    if (pos < 0 || len < 0 || len > bufSize || pos > INT_MAX - bufSize) {
184
0
        return false;
185
0
    }
186
4.20k
    if (pos >= bufPos && pos + len <= bufPos + bufLen) {
187
3.60k
        return true;
188
3.60k
    }
189
601
    if (fseek(f, pos, SEEK_SET)) {
190
0
        return false;
191
0
    }
192
601
    bufPos = pos;
193
601
    bufLen = static_cast<int>(fread(buf, 1, sizeof(buf), f));
194
601
    return bufLen >= len;
195
601
}
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
7.64k
{
229
7.64k
    return std::make_unique<StreamReader>(getCharA, dataA);
230
7.64k
}
231
232
StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA, PrivateTag /*unused*/)
233
7.64k
{
234
7.64k
    getChar = getCharA;
235
7.64k
    data = dataA;
236
7.64k
    streamPos = 0;
237
7.64k
    bufPos = 0;
238
7.64k
    bufLen = 0;
239
7.64k
}
240
241
StreamReader::~StreamReader() = default;
242
243
int StreamReader::getByte(int pos)
244
60.0k
{
245
60.0k
    if (!fillBuf(pos, 1)) {
246
1.43k
        return -1;
247
1.43k
    }
248
58.6k
    return buf[pos - bufPos] & 0xff;
249
60.0k
}
250
251
bool StreamReader::getU16BE(int pos, int *val)
252
5.72k
{
253
5.72k
    if (!fillBuf(pos, 2)) {
254
69
        return false;
255
69
    }
256
5.65k
    *val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff);
257
5.65k
    return true;
258
5.72k
}
259
260
bool StreamReader::getU32BE(int pos, unsigned int *val)
261
51
{
262
51
    if (!fillBuf(pos, 4)) {
263
0
        return false;
264
0
    }
265
51
    *val = ((buf[pos - bufPos] & 0xff) << 24) + ((buf[pos - bufPos + 1] & 0xff) << 16) + ((buf[pos - bufPos + 2] & 0xff) << 8) + (buf[pos - bufPos + 3] & 0xff);
266
51
    return true;
267
51
}
268
269
bool StreamReader::getU32LE(int pos, unsigned int *val)
270
22
{
271
22
    if (!fillBuf(pos, 4)) {
272
6
        return false;
273
6
    }
274
16
    *val = (buf[pos - bufPos] & 0xff) + ((buf[pos - bufPos + 1] & 0xff) << 8) + ((buf[pos - bufPos + 2] & 0xff) << 16) + ((buf[pos - bufPos + 3] & 0xff) << 24);
275
16
    return true;
276
22
}
277
278
bool StreamReader::getUVarBE(int pos, int size, unsigned int *val)
279
7.94k
{
280
7.94k
    int i;
281
282
7.94k
    if (size < 1 || size > 4 || !fillBuf(pos, size)) {
283
22
        return false;
284
22
    }
285
7.92k
    *val = 0;
286
16.3k
    for (i = 0; i < size; ++i) {
287
8.40k
        *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
288
8.40k
    }
289
7.92k
    return true;
290
7.94k
}
291
292
bool StreamReader::cmp(int pos, const char *s)
293
158k
{
294
158k
    const int n = static_cast<int>(strlen(s));
295
158k
    if (!fillBuf(pos, n)) {
296
141k
        return false;
297
141k
    }
298
17.0k
    const int posDiff = pos - bufPos;
299
17.0k
    return !memcmp(buf + posDiff, s, n);
300
158k
}
301
302
bool StreamReader::fillBuf(int pos, int len)
303
232k
{
304
232k
    int c;
305
306
232k
    constexpr int bufSize = sizeof(buf);
307
232k
    if (pos < 0 || len < 0 || len > bufSize || pos > INT_MAX - bufSize) {
308
0
        return false;
309
0
    }
310
232k
    if (pos < bufPos) {
311
0
        return false;
312
0
    }
313
314
    // if requested region will not fit in the current buffer...
315
232k
    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
140k
        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
140k
        } else {
327
140k
            bufPos += bufLen;
328
140k
            bufLen = 0;
329
536k
            while (bufPos < pos) {
330
536k
                if ((c = (*getChar)(data)) < 0) {
331
140k
                    return false;
332
140k
                }
333
396k
                ++bufPos;
334
396k
            }
335
140k
        }
336
140k
    }
337
338
    // read the rest of the requested data
339
386k
    while (bufPos + bufLen < pos + len) {
340
297k
        if ((c = (*getChar)(data)) < 0) {
341
2.81k
            return false;
342
2.81k
        }
343
294k
        buf[bufLen++] = static_cast<char>(c);
344
294k
    }
345
346
89.3k
    return true;
347
92.1k
}
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
601
{
359
360
601
    std::unique_ptr<FileReader> reader = FileReader::make(fileName);
361
601
    if (!reader) {
362
0
        return fofiIdError;
363
0
    }
364
601
    return identify(reader.get());
365
601
}
366
367
FoFiIdentifierType FoFiIdentifier::identifyStream(int (*getChar)(void *data), void *data)
368
7.64k
{
369
7.64k
    std::unique_ptr<StreamReader> reader = StreamReader::make(getChar, data);
370
371
7.64k
    if (!reader) {
372
0
        return fofiIdError;
373
0
    }
374
7.64k
    return identify(reader.get());
375
7.64k
}
376
377
static FoFiIdentifierType identify(Reader *reader)
378
8.24k
{
379
8.24k
    unsigned int n;
380
381
    //----- PFA
382
8.24k
    if (reader->cmp(0, "%!PS-AdobeFont-1") || reader->cmp(0, "%!FontType1")) {
383
2.14k
        return fofiIdType1PFA;
384
2.14k
    }
385
386
    //----- PFB
387
6.09k
    if (reader->getByte(0) == 0x80 && reader->getByte(1) == 0x01 && reader->getU32LE(2, &n)) {
388
16
        if ((n >= 16 && reader->cmp(6, "%!PS-AdobeFont-1")) || (n >= 11 && reader->cmp(6, "%!FontType1"))) {
389
10
            return fofiIdType1PFB;
390
10
        }
391
16
    }
392
393
    //----- TrueType
394
6.08k
    if ((reader->getByte(0) == 0x00 && reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00 && reader->getByte(3) == 0x00)
395
4.75k
        || (reader->getByte(0) == 0x74 && // 'true'
396
2.07k
            reader->getByte(1) == 0x72 && reader->getByte(2) == 0x75 && reader->getByte(3) == 0x65)) {
397
2.07k
        return fofiIdTrueType;
398
2.07k
    }
399
4.01k
    if (reader->getByte(0) == 0x74 && // 'ttcf'
400
148
        reader->getByte(1) == 0x74 && reader->getByte(2) == 0x63 && reader->getByte(3) == 0x66) {
401
1
        return fofiIdTrueTypeCollection;
402
1
    }
403
404
    //----- OpenType
405
4.01k
    if (reader->getByte(0) == 0x4f && // 'OTTO
406
76
        reader->getByte(1) == 0x54 && reader->getByte(2) == 0x54 && reader->getByte(3) == 0x4f) {
407
63
        return identifyOpenType(reader);
408
63
    }
409
410
    //----- CFF
411
3.95k
    if (reader->getByte(0) == 0x01 && reader->getByte(1) == 0x00) {
412
2.68k
        return identifyCFF(reader, 0);
413
2.68k
    }
414
    // some tools embed CFF fonts with an extra whitespace char at the
415
    // beginning
416
1.26k
    if (reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00) {
417
177
        return identifyCFF(reader, 1);
418
177
    }
419
420
1.08k
    return fofiIdUnknown;
421
1.26k
}
422
423
static FoFiIdentifierType identifyOpenType(Reader *reader)
424
63
{
425
63
    FoFiIdentifierType type;
426
63
    unsigned int offset;
427
63
    int nTables, i;
428
429
63
    if (!reader->getU16BE(4, &nTables)) {
430
0
        return fofiIdUnknown;
431
0
    }
432
145k
    for (i = 0; i < nTables; ++i) {
433
145k
        if (reader->cmp(12 + i * 16, "CFF ")) {
434
51
            if (reader->getU32BE(12 + i * 16 + 8, &offset) && offset < static_cast<unsigned int>(INT_MAX)) {
435
50
                type = identifyCFF(reader, static_cast<int>(offset));
436
50
                if (type == fofiIdCFF8Bit) {
437
32
                    type = fofiIdOpenTypeCFF8Bit;
438
32
                } else if (type == fofiIdCFFCID) {
439
0
                    type = fofiIdOpenTypeCFFCID;
440
0
                }
441
50
                return type;
442
50
            }
443
1
            return fofiIdUnknown;
444
51
        }
445
145k
    }
446
12
    return fofiIdUnknown;
447
63
}
448
449
static FoFiIdentifierType identifyCFF(Reader *reader, int start)
450
2.91k
{
451
2.91k
    unsigned int offset0, offset1;
452
2.91k
    int hdrSize, offSize0, offSize1, pos, endPos, b0, n, i;
453
454
    //----- read the header
455
2.91k
    if (reader->getByte(start) != 0x01 || reader->getByte(start + 1) != 0x00) {
456
18
        return fofiIdUnknown;
457
18
    }
458
2.89k
    if ((hdrSize = reader->getByte(start + 2)) < 0) {
459
6
        return fofiIdUnknown;
460
6
    }
461
2.89k
    if ((offSize0 = reader->getByte(start + 3)) < 1 || offSize0 > 4) {
462
30
        return fofiIdUnknown;
463
30
    }
464
2.86k
    pos = start + hdrSize;
465
2.86k
    if (pos < 0) {
466
0
        return fofiIdUnknown;
467
0
    }
468
469
    //----- skip the name index
470
2.86k
    if (!reader->getU16BE(pos, &n)) {
471
24
        return fofiIdUnknown;
472
24
    }
473
2.83k
    if (n == 0) {
474
176
        pos += 2;
475
2.66k
    } else {
476
2.66k
        if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
477
19
            return fofiIdUnknown;
478
19
        }
479
2.64k
        if (!reader->getUVarBE(pos + 3 + n * offSize1, offSize1, &offset1) || offset1 > static_cast<unsigned int>(INT_MAX)) {
480
16
            return fofiIdUnknown;
481
16
        }
482
2.62k
        pos += 3 + (n + 1) * offSize1 + static_cast<int>(offset1) - 1;
483
2.62k
    }
484
2.80k
    if (pos < 0) {
485
0
        return fofiIdUnknown;
486
0
    }
487
488
    //----- parse the top dict index
489
2.80k
    if (!reader->getU16BE(pos, &n) || n < 1) {
490
56
        return fofiIdUnknown;
491
56
    }
492
2.74k
    if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
493
88
        return fofiIdUnknown;
494
88
    }
495
2.65k
    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
2.62k
    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
1
        return fofiIdUnknown;
500
1
    }
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
5.61k
    for (i = 0; i < 3; ++i) {
506
5.40k
        b0 = reader->getByte(pos++);
507
5.40k
        if (b0 == 0x1c) {
508
4
            pos += 2;
509
5.39k
        } else if (b0 == 0x1d) {
510
1
            pos += 4;
511
5.39k
        } else if (b0 >= 0xf7 && b0 <= 0xfe) {
512
2.55k
            pos += 1;
513
2.84k
        } else if (b0 < 0x20 || b0 > 0xf6) {
514
2.41k
            return fofiIdCFF8Bit;
515
2.41k
        }
516
2.98k
        if (pos >= endPos || pos < 0) {
517
0
            return fofiIdCFF8Bit;
518
0
        }
519
2.98k
    }
520
212
    if (pos + 1 < endPos && reader->getByte(pos) == 12 && reader->getByte(pos + 1) == 30) {
521
145
        return fofiIdCFFCID;
522
145
    }
523
67
    return fofiIdCFF8Bit;
524
212
}