Coverage Report

Created: 2026-07-30 07:17

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
3.33k
    Reader() = default;
42
    Reader(const Reader &) = delete;
43
    Reader &operator=(const Reader &other) = delete;
44
45
3.33k
    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
3.33k
{
229
3.33k
    return std::make_unique<StreamReader>(getCharA, dataA);
230
3.33k
}
231
232
StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA, PrivateTag /*unused*/)
233
3.33k
{
234
3.33k
    getChar = getCharA;
235
3.33k
    data = dataA;
236
3.33k
    streamPos = 0;
237
3.33k
    bufPos = 0;
238
3.33k
    bufLen = 0;
239
3.33k
}
240
241
StreamReader::~StreamReader() = default;
242
243
int StreamReader::getByte(int pos)
244
23.7k
{
245
23.7k
    if (!fillBuf(pos, 1)) {
246
1.69k
        return -1;
247
1.69k
    }
248
22.0k
    return buf[pos - bufPos] & 0xff;
249
23.7k
}
250
251
bool StreamReader::getU16BE(int pos, int *val)
252
458
{
253
458
    if (!fillBuf(pos, 2)) {
254
78
        return false;
255
78
    }
256
380
    *val = ((buf[pos - bufPos] & 0xff) << 8) + (buf[pos - bufPos + 1] & 0xff);
257
380
    return true;
258
458
}
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
4
{
271
4
    if (!fillBuf(pos, 4)) {
272
1
        return false;
273
1
    }
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
4
}
277
278
bool StreamReader::getUVarBE(int pos, int size, unsigned int *val)
279
348
{
280
348
    int i;
281
282
348
    if (size < 1 || size > 4 || !fillBuf(pos, size)) {
283
10
        return false;
284
10
    }
285
338
    *val = 0;
286
1.11k
    for (i = 0; i < size; ++i) {
287
779
        *val = (*val << 8) + (buf[pos - bufPos + i] & 0xff);
288
779
    }
289
338
    return true;
290
348
}
291
292
bool StreamReader::cmp(int pos, const char *s)
293
6.66k
{
294
6.66k
    const int n = static_cast<int>(strlen(s));
295
6.66k
    if (!fillBuf(pos, n)) {
296
689
        return false;
297
689
    }
298
5.97k
    const int posDiff = pos - bufPos;
299
5.97k
    return !memcmp(buf + posDiff, s, n);
300
6.66k
}
301
302
bool StreamReader::fillBuf(int pos, int len)
303
31.2k
{
304
31.2k
    int c;
305
306
31.2k
    constexpr int bufSize = sizeof(buf);
307
31.2k
    if (pos < 0 || len < 0 || len > bufSize || pos > INT_MAX - bufSize) {
308
0
        return false;
309
0
    }
310
31.2k
    if (pos < bufPos) {
311
0
        return false;
312
0
    }
313
314
    // if requested region will not fit in the current buffer...
315
31.2k
    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
102
        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
102
        } else {
327
102
            bufPos += bufLen;
328
102
            bufLen = 0;
329
88.9k
            while (bufPos < pos) {
330
88.9k
                if ((c = (*getChar)(data)) < 0) {
331
95
                    return false;
332
95
                }
333
88.8k
                ++bufPos;
334
88.8k
            }
335
102
        }
336
102
    }
337
338
    // read the rest of the requested data
339
89.4k
    while (bufPos + bufLen < pos + len) {
340
60.6k
        if ((c = (*getChar)(data)) < 0) {
341
2.38k
            return false;
342
2.38k
        }
343
58.3k
        buf[bufLen++] = static_cast<char>(c);
344
58.3k
    }
345
346
28.7k
    return true;
347
31.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
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
3.33k
{
369
3.33k
    std::unique_ptr<StreamReader> reader = StreamReader::make(getChar, data);
370
371
3.33k
    if (!reader) {
372
0
        return fofiIdError;
373
0
    }
374
3.33k
    return identify(reader.get());
375
3.33k
}
376
377
static FoFiIdentifierType identify(Reader *reader)
378
3.33k
{
379
3.33k
    unsigned int n;
380
381
    //----- PFA
382
3.33k
    if (reader->cmp(0, "%!PS-AdobeFont-1") || reader->cmp(0, "%!FontType1")) {
383
0
        return fofiIdType1PFA;
384
0
    }
385
386
    //----- PFB
387
3.33k
    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
3.33k
    if ((reader->getByte(0) == 0x00 && reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00 && reader->getByte(3) == 0x00)
395
3.32k
        || (reader->getByte(0) == 0x74 && // 'true'
396
1.88k
            reader->getByte(1) == 0x72 && reader->getByte(2) == 0x75 && reader->getByte(3) == 0x65)) {
397
1.84k
        return fofiIdTrueType;
398
1.84k
    }
399
1.48k
    if (reader->getByte(0) == 0x74 && // 'ttcf'
400
51
        reader->getByte(1) == 0x74 && reader->getByte(2) == 0x63 && reader->getByte(3) == 0x66) {
401
0
        return fofiIdTrueTypeCollection;
402
0
    }
403
404
    //----- OpenType
405
1.48k
    if (reader->getByte(0) == 0x4f && // 'OTTO
406
33
        reader->getByte(1) == 0x54 && reader->getByte(2) == 0x54 && reader->getByte(3) == 0x4f) {
407
0
        return identifyOpenType(reader);
408
0
    }
409
410
    //----- CFF
411
1.48k
    if (reader->getByte(0) == 0x01 && reader->getByte(1) == 0x00) {
412
203
        return identifyCFF(reader, 0);
413
203
    }
414
    // some tools embed CFF fonts with an extra whitespace char at the
415
    // beginning
416
1.28k
    if (reader->getByte(1) == 0x01 && reader->getByte(2) == 0x00) {
417
59
        return identifyCFF(reader, 1);
418
59
    }
419
420
1.22k
    return fofiIdUnknown;
421
1.28k
}
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
262
{
451
262
    unsigned int offset0, offset1;
452
262
    int hdrSize, offSize0, offSize1, pos, endPos, b0, n, i;
453
454
    //----- read the header
455
262
    if (reader->getByte(start) != 0x01 || reader->getByte(start + 1) != 0x00) {
456
0
        return fofiIdUnknown;
457
0
    }
458
262
    if ((hdrSize = reader->getByte(start + 2)) < 0) {
459
5
        return fofiIdUnknown;
460
5
    }
461
257
    if ((offSize0 = reader->getByte(start + 3)) < 1 || offSize0 > 4) {
462
8
        return fofiIdUnknown;
463
8
    }
464
249
    pos = start + hdrSize;
465
249
    if (pos < 0) {
466
0
        return fofiIdUnknown;
467
0
    }
468
469
    //----- skip the name index
470
249
    if (!reader->getU16BE(pos, &n)) {
471
20
        return fofiIdUnknown;
472
20
    }
473
229
    if (n == 0) {
474
136
        pos += 2;
475
136
    } else {
476
93
        if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
477
8
            return fofiIdUnknown;
478
8
        }
479
85
        if (!reader->getUVarBE(pos + 3 + n * offSize1, offSize1, &offset1) || offset1 > static_cast<unsigned int>(INT_MAX)) {
480
12
            return fofiIdUnknown;
481
12
        }
482
73
        pos += 3 + (n + 1) * offSize1 + static_cast<int>(offset1) - 1;
483
73
    }
484
209
    if (pos < 0) {
485
0
        return fofiIdUnknown;
486
0
    }
487
488
    //----- parse the top dict index
489
209
    if (!reader->getU16BE(pos, &n) || n < 1) {
490
65
        return fofiIdUnknown;
491
65
    }
492
144
    if ((offSize1 = reader->getByte(pos + 2)) < 1 || offSize1 > 4) {
493
3
        return fofiIdUnknown;
494
3
    }
495
141
    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
30
        return fofiIdUnknown;
497
30
    }
498
111
    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
205
    for (i = 0; i < 3; ++i) {
506
192
        b0 = reader->getByte(pos++);
507
192
        if (b0 == 0x1c) {
508
0
            pos += 2;
509
192
        } else if (b0 == 0x1d) {
510
0
            pos += 4;
511
192
        } else if (b0 >= 0xf7 && b0 <= 0xfe) {
512
17
            pos += 1;
513
175
        } else if (b0 < 0x20 || b0 > 0xf6) {
514
98
            return fofiIdCFF8Bit;
515
98
        }
516
94
        if (pos >= endPos || pos < 0) {
517
0
            return fofiIdCFF8Bit;
518
0
        }
519
94
    }
520
13
    if (pos + 1 < endPos && reader->getByte(pos) == 12 && reader->getByte(pos + 1) == 30) {
521
0
        return fofiIdCFFCID;
522
0
    }
523
13
    return fofiIdCFF8Bit;
524
13
}