Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/libeot/src/EOT.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2013 Brennan T. Vincent <brennanv@email.arizona.edu>
2
 * This file is a part of libeot, which is licensed under the MPL license, version 2.0.
3
 * For full details, see the file LICENSE
4
 */
5
6
#include <string.h>
7
#include <stdlib.h>
8
9
#include <libeot/libeot.h>
10
11
const uint16_t EDITING_MASK = 0x0008;
12
13
uint32_t EOTreadU32LE(const uint8_t *bytes)
14
0
{
15
0
  return ((uint32_t)bytes[0]) | (((uint32_t)bytes[1]) << 8) | (((uint32_t)bytes[2]) << 16) | (((uint32_t)bytes[3]) << 24);
16
0
}
17
18
uint16_t EOTreadU16LE(const uint8_t *bytes)
19
0
{
20
0
  return bytes[0] | (bytes[1] << 8);
21
0
}
22
23
unsigned EOTgetMetadataLength(const uint8_t *bytes)
24
0
{
25
0
  uint32_t totalLength = EOTreadU32LE(bytes);
26
0
  uint32_t fontLength = EOTreadU32LE(bytes + 4);
27
0
  return totalLength - fontLength;
28
0
}
29
30
enum EOTError EOTgetString(const uint8_t **scanner, const uint8_t *begin, unsigned bytesLength,
31
      uint16_t *size, uint16_t **string)
32
0
{
33
0
  if (*string)
34
0
  {
35
0
    free(*string);
36
0
  }
37
0
  *string = 0;
38
0
  if (*scanner - begin + 2 > bytesLength)
39
0
  {
40
0
    return EOT_INSUFFICIENT_BYTES;
41
0
  }
42
0
  *size = EOTreadU16LE(*scanner);
43
0
  *scanner += 2;
44
0
  if (*size % 2 != 0) // strings are encoded in UTF-16.
45
0
  {
46
0
    return EOT_BOGUS_STRING_SIZE;
47
0
  }
48
0
  if (*scanner - begin + *size > bytesLength)
49
0
  {
50
0
    return EOT_INSUFFICIENT_BYTES;
51
0
  }
52
0
  if (*size != 0)
53
0
  {
54
0
    *string = malloc(*size);
55
0
    if (!*string)
56
0
    {
57
0
      return EOT_CANT_ALLOCATE_MEMORY;
58
0
    }
59
0
    for (unsigned i = 0; i < *size / 2; ++i)
60
0
    {
61
0
      (*string)[i] = EOTreadU16LE(*scanner);
62
0
      *scanner += 2;
63
0
    }
64
0
  }
65
0
  return EOT_SUCCESS;
66
0
}
67
68
enum EOTError EOTgetByteArray(const uint8_t **scanner, const uint8_t *begin,
69
    unsigned bytesLength, uint32_t *size, uint8_t **array)
70
0
{
71
0
  if (*array)
72
0
  {
73
0
    free(*array);
74
0
  }
75
0
  *array = 0;
76
0
  if (*scanner - begin + 4 > bytesLength)
77
0
  {
78
0
    return EOT_INSUFFICIENT_BYTES;
79
0
  }
80
0
  *size = EOTreadU32LE(*scanner);
81
0
  *scanner += 4;
82
0
  if (*scanner - begin + *size > bytesLength)
83
0
  {
84
0
    return EOT_INSUFFICIENT_BYTES;
85
0
  }
86
0
  if (*size != 0)
87
0
  {
88
0
    *array = malloc(*size);
89
0
    if (!*array)
90
0
    {
91
0
      return EOT_CANT_ALLOCATE_MEMORY;
92
0
    }
93
0
    for (unsigned i = 0; i < *size; ++i)
94
0
    {
95
0
      (*array)[i] = **scanner;
96
0
      ++(*scanner);
97
0
    }
98
0
  }
99
0
  return EOT_SUCCESS;
100
0
}
101
102
void EOTfreeMetadata(struct EOTMetadata *d)
103
0
{
104
0
  if (d->familyName)
105
0
  {
106
0
    free(d->familyName);
107
0
  }
108
0
  if (d->styleName)
109
0
  {
110
0
    free(d->styleName);
111
0
  }
112
0
  if (d->versionName)
113
0
  {
114
0
    free(d->versionName);
115
0
  }
116
0
  if (d->fullName)
117
0
  {
118
0
    free(d->fullName);
119
0
  }
120
0
  if (d->do_not_use)
121
0
  {
122
0
    free(d->do_not_use);
123
0
  }
124
0
  if (d->rootStrings)
125
0
  {
126
0
    for (unsigned i = 0; i < d->numRootStrings; ++i)
127
0
    {
128
0
      free(d->rootStrings[i].rootString);
129
0
    }
130
0
    free(d->rootStrings);
131
0
  }
132
0
  if (d->eudcInfo.fontData)
133
0
  {
134
0
    free(d->eudcInfo.fontData);
135
0
  }
136
0
  struct EOTMetadata zero = {0};
137
0
  *d = zero;
138
0
}
139
140
0
#define EOT_ENSURE_SCANNER(N) if (scanner - bytes + N >= bytesLength) { EOTfreeMetadata(out); return EOT_INSUFFICIENT_BYTES; }
141
142
0
#define EOT_ENSURE_STRING_NOERR(E) {enum EOTError macro_defined_var_E = E; if(macro_defined_var_E != EOT_SUCCESS) { EOTfreeMetadata(out); return macro_defined_var_E; }}
143
144
enum EOTError EOTfillMetadataSpecifyingVersion(const uint8_t *bytes, unsigned bytesLength,
145
    struct EOTMetadata *out, enum EOTVersion version, int currIndex)
146
0
{
147
0
  out->version = version;
148
0
  const uint8_t *scanner = bytes;
149
0
  EOT_ENSURE_SCANNER(4);
150
0
  out->flags = EOTreadU32LE(scanner);
151
0
  scanner += 4;
152
0
  EOT_ENSURE_SCANNER(10);
153
0
  memcpy(&(out->panose), scanner, 10);
154
0
  scanner += 10;
155
0
  EOT_ENSURE_SCANNER(1);
156
0
  out->charset = (enum EOTCharset)(*scanner);
157
0
  ++scanner;
158
0
  EOT_ENSURE_SCANNER(1);
159
0
  out->italic = *scanner;
160
0
  ++scanner;
161
0
  EOT_ENSURE_SCANNER(4);
162
0
  out->weight = EOTreadU32LE(scanner);
163
0
  scanner += 4;
164
0
  EOT_ENSURE_SCANNER(2);
165
0
  out->permissions = EOTreadU16LE(scanner);
166
0
  scanner += 2;
167
0
  EOT_ENSURE_SCANNER(2);
168
0
  if (EOTreadU16LE(scanner) != 0x504C)
169
0
  {
170
0
    return EOT_CORRUPT_FILE;
171
0
  }
172
0
  scanner += 2;
173
0
  for (unsigned i = 0; i < 4; ++i)
174
0
  {
175
0
    EOT_ENSURE_SCANNER(4);
176
0
    out->unicodeRange[i] = EOTreadU32LE(scanner);
177
0
    scanner += 4;
178
0
  }
179
0
  for (unsigned i = 0; i < 2; ++i)
180
0
  {
181
0
    EOT_ENSURE_SCANNER(4);
182
0
    out->codePageRange[i] = EOTreadU32LE(scanner);
183
0
    scanner += 4;
184
0
  }
185
0
  EOT_ENSURE_SCANNER(4);
186
0
  out->checkSumAdjustment = EOTreadU32LE(scanner);
187
0
  scanner += 22;
188
0
  EOT_ENSURE_STRING_NOERR(EOTgetString(&scanner, bytes, bytesLength, &(out->familyNameSize),
189
0
        &(out->familyName)));
190
0
  scanner += 2;
191
0
  EOT_ENSURE_STRING_NOERR(EOTgetString(&scanner, bytes, bytesLength, &(out->styleNameSize),
192
0
        &(out->styleName)));
193
0
  scanner += 2;
194
0
  EOT_ENSURE_STRING_NOERR(EOTgetString(&scanner, bytes, bytesLength, &(out->versionNameSize),
195
0
        &(out->versionName)));
196
0
  scanner += 2;
197
0
  EOT_ENSURE_STRING_NOERR(EOTgetString(&scanner, bytes, bytesLength, &(out->fullNameSize),
198
0
        &(out->fullName)));
199
0
  if (out->version > VERSION_1)
200
0
  {
201
0
    scanner += 2;
202
0
    EOT_ENSURE_STRING_NOERR(EOTgetString(&scanner, bytes, bytesLength, &(out->do_not_use_size),
203
0
          &(out->do_not_use)));
204
0
    if (out->version == VERSION_3)
205
0
    {
206
0
      EOT_ENSURE_SCANNER(4);
207
0
      EOTreadU32LE(scanner); /* root string checksum */
208
0
      scanner += 4;
209
0
      EOT_ENSURE_SCANNER(4);
210
0
      out->eudcInfo.codePage = EOTreadU32LE(scanner);
211
0
      scanner += 6;
212
0
      EOT_ENSURE_SCANNER(2);
213
0
      uint16_t signatureSize = EOTreadU16LE(scanner);
214
0
      scanner += 2;
215
0
      EOT_ENSURE_SCANNER(signatureSize);
216
0
      scanner += signatureSize;
217
      /* signature is reserved, so do nothing with this. */
218
0
      EOT_ENSURE_SCANNER(4);
219
0
      out->eudcInfo.flags = EOTreadU32LE(scanner);
220
0
      scanner += 4;
221
0
      EOT_ENSURE_STRING_NOERR(EOTgetByteArray(&scanner, bytes, bytesLength,
222
0
            &(out->eudcInfo.fontDataSize), &(out->eudcInfo.fontData)));
223
0
      if (out->eudcInfo.fontDataSize > 0)
224
0
      {
225
0
        out->eudcInfo.exists = true;
226
0
      }
227
0
    }
228
0
  }
229
0
  out->fontDataOffset = scanner - bytes + currIndex;
230
0
  int expectedHeaderSize = (int)(out->totalSize) - (int)(out->fontDataSize);
231
0
  if (out->fontDataOffset < expectedHeaderSize)
232
0
  {
233
0
    return EOT_HEADER_TOO_BIG;
234
0
  }
235
0
  return EOT_SUCCESS;
236
0
}
237
238
enum EOTError EOTfillMetadata(const uint8_t *bytes, unsigned bytesLength,
239
    struct EOTMetadata *out)
240
0
{
241
0
  struct EOTMetadata zero = {0};
242
0
  *out = zero;
243
0
  const uint8_t *scanner = bytes;
244
0
  if (bytesLength < 8 || bytesLength < EOTgetMetadataLength(bytes))
245
0
  {
246
0
    return EOT_INSUFFICIENT_BYTES;
247
0
  }
248
0
  EOT_ENSURE_SCANNER(4);
249
0
  unsigned totalSize = EOTreadU32LE(scanner);
250
0
  scanner += 4;
251
0
  EOT_ENSURE_SCANNER(4);
252
0
  unsigned fontDataSize = EOTreadU32LE(scanner);
253
0
  scanner += 4;
254
0
  EOT_ENSURE_SCANNER(4);
255
0
  uint32_t versionMagic = EOTreadU32LE(scanner);
256
0
  scanner += 4;
257
0
  enum EOTVersion codedVersion;
258
0
  switch (versionMagic)
259
0
  {
260
0
  case 0x00010000:
261
0
    codedVersion = VERSION_1;
262
0
    break;
263
0
  case 0x00020001:
264
0
    codedVersion = VERSION_2;
265
0
    break;
266
0
  case 0x00020002:
267
0
    codedVersion = VERSION_3;
268
0
    break;
269
0
  default:
270
0
    return EOT_CORRUPT_FILE;
271
0
  }
272
0
  enum EOTVersion tryVersion = codedVersion;
273
0
  bool bumpedUp = false, knockedDown = false;
274
0
  while (true)
275
0
  {
276
0
    EOTfreeMetadata(out);
277
0
    out->totalSize = totalSize;
278
0
    out->fontDataSize = fontDataSize;
279
0
    if (bytesLength + bytes < out->fontDataSize + scanner)
280
0
    {
281
0
      return EOT_CORRUPT_FILE;
282
0
    }
283
0
    enum EOTError result = EOTfillMetadataSpecifyingVersion(scanner, bytesLength - out->fontDataSize - (scanner - bytes), out, tryVersion, scanner - bytes);
284
0
    if (result == EOT_SUCCESS)
285
0
    {
286
0
      return tryVersion == codedVersion ? EOT_SUCCESS : EOT_WARN_BAD_VERSION;
287
0
    }
288
0
    if (result == EOT_HEADER_TOO_BIG)
289
0
    {
290
0
      if (knockedDown || (tryVersion == VERSION_3))
291
0
      {
292
0
        return EOT_CORRUPT_FILE;
293
0
      }
294
0
      knockedDown = false;
295
0
      bumpedUp = true;
296
0
      ++tryVersion;
297
0
    }
298
0
    else if (result == EOT_INSUFFICIENT_BYTES)
299
0
    {
300
0
      if (bumpedUp || (tryVersion == VERSION_1))
301
0
      {
302
0
        return EOT_CORRUPT_FILE;
303
0
      }
304
0
      knockedDown = true;
305
0
      bumpedUp = false;
306
0
      --tryVersion;
307
0
    }
308
0
    else
309
0
    {
310
0
      return result;
311
0
    }
312
0
  }
313
0
}
314
315
/* Please think twice before circumventing this function.
316
 * Does your personal sense of morality really let you take others' work without their permission?
317
 *
318
 * I'm not suggesting any system of morality is right or wrong;
319
 * I'm merely asking that you reflect on it before changing anything here.
320
 */
321
bool EOTcanLegallyEdit(const struct EOTMetadata *metadata)
322
0
{
323
0
  return metadata->permissions == 0 || ((metadata->permissions & EDITING_MASK) != 0);
324
0
}
325
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */