• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 21846209963

09 Feb 2026 03:52PM UTC coverage: 72.697% (-0.02%) from 72.716%
21846209963

push

github

daandemeyer
meson: guard symlinks in sysconfdir behind install_sysconfidr

Symlinks to files inside sysconfdir are now only installed if
ìnstall_sysconfdir=true (which is the default).

If sshconfdir,sshdconfdir,shellprofiledir are not inside sysconfdir and
install_sysconfidr=false, these symlinks are still installed to the
configured directory.

311951 of 429113 relevant lines covered (72.7%)

1156102.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/src/shared/pe-binary.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/stat.h>
4
#include <unistd.h>
5

6
#include "alloc-util.h"
7
#include "hexdecoct.h"
8
#include "log.h"
9
#include "pe-binary.h"
10
#include "sort-util.h"
11
#include "stat-util.h"
12
#include "string-table.h"
13
#include "string-util.h"
14

15
/* Note: none of these function change the file position of the provided fd, as they use pread() */
16

17
bool pe_header_is_64bit(const PeHeader *h) {
529✔
18
        assert(h);
529✔
19

20
        if (le16toh(h->optional.Magic) == UINT16_C(0x010B)) /* PE32 */
529✔
21
                return false;
22

23
        if (le16toh(h->optional.Magic) == UINT16_C(0x020B)) /* PE32+ */
263✔
24
                return true;
25

26
        assert_not_reached();
×
27
}
28

29
static size_t pe_header_size(const PeHeader *pe_header) {
538✔
30
        assert(pe_header);
538✔
31

32
        return offsetof(PeHeader, optional) + le16toh(pe_header->pe.SizeOfOptionalHeader);
538✔
33
}
34

35
const IMAGE_DATA_DIRECTORY* pe_header_get_data_directory(
23✔
36
                const PeHeader *h,
37
                size_t i) {
38

39
        assert(h);
23✔
40

41
        if (i >= le32toh(PE_HEADER_OPTIONAL_FIELD(h, NumberOfRvaAndSizes)))
23✔
42
                return NULL;
43

44
        return PE_HEADER_OPTIONAL_FIELD(h, DataDirectory) + i;
23✔
45
}
46

47
const IMAGE_SECTION_HEADER* pe_section_table_find(
400✔
48
                const IMAGE_SECTION_HEADER *sections,
49
                size_t n_sections,
50
                const char *name) {
51

52
        size_t n;
400✔
53

54
        assert(name);
400✔
55
        assert(sections || n_sections == 0);
400✔
56

57
        n = strlen(name);
400✔
58
        if (n > sizeof(sections[0].Name)) /* Too long? */
400✔
59
                return NULL;
60

61
        FOREACH_ARRAY(section, sections, n_sections)
2,470✔
62
                if (memcmp(section->Name, name, n) == 0 &&
2,400✔
63
                    (n == sizeof(sections[0].Name) || memeqzero(section->Name + n, sizeof(section->Name) - n)))
110✔
64
                        return section;
330✔
65

66
        return NULL;
67
}
68

69
const IMAGE_SECTION_HEADER* pe_header_find_section(
100✔
70
                const PeHeader *pe_header,
71
                const IMAGE_SECTION_HEADER *sections,
72
                const char *name) {
73

74
        assert(pe_header);
100✔
75

76
        return pe_section_table_find(sections, le16toh(pe_header->pe.NumberOfSections), name);
100✔
77
}
78

79
int pe_load_headers(
232✔
80
                int fd,
81
                IMAGE_DOS_HEADER **ret_dos_header,
82
                PeHeader **ret_pe_header) {
83

84
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
464✔
85
        _cleanup_free_ PeHeader *pe_header = NULL;
232✔
86
        ssize_t n;
232✔
87

88
        assert(fd >= 0);
232✔
89

90
        dos_header = new(IMAGE_DOS_HEADER, 1);
232✔
91
        if (!dos_header)
232✔
92
                return log_oom_debug();
×
93

94
        n = pread(fd,
232✔
95
                  dos_header,
96
                  sizeof(IMAGE_DOS_HEADER),
97
                  0);
98
        if (n < 0)
232✔
99
                return log_debug_errno(errno, "Failed to read DOS header: %m");
×
100
        if ((size_t) n != sizeof(IMAGE_DOS_HEADER))
232✔
101
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading MZ executable header.");
×
102

103
        if (le16toh(dos_header->e_magic) != UINT16_C(0x5A4D))
232✔
104
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks MZ executable header.");
2✔
105

106
        pe_header = new(PeHeader, 1);
230✔
107
        if (!pe_header)
230✔
108
                return log_oom_debug();
×
109

110
        n = pread(fd,
460✔
111
                  pe_header,
112
                  offsetof(PeHeader, optional),
113
                  le32toh(dos_header->e_lfanew));
230✔
114
        if (n < 0)
230✔
115
                return log_debug_errno(errno, "Failed to read PE executable header: %m");
×
116
        if ((size_t) n != offsetof(PeHeader, optional))
230✔
117
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading PE executable header.");
×
118

119
        if (le32toh(pe_header->signature) != UINT32_C(0x00004550))
230✔
120
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks PE executable header.");
×
121

122
        if (le16toh(pe_header->pe.SizeOfOptionalHeader) < sizeof_field(PeHeader, optional.Magic))
230✔
123
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Optional header size too short for magic.");
×
124

125
        PeHeader *pe_header_tmp = realloc(pe_header, MAX(sizeof(PeHeader), pe_header_size(pe_header)));
230✔
126
        if (!pe_header_tmp)
230✔
127
                return log_oom_debug();
×
128
        pe_header = pe_header_tmp;
230✔
129

130
        n = pread(fd,
460✔
131
                  &pe_header->optional,
230✔
132
                  le16toh(pe_header->pe.SizeOfOptionalHeader),
230✔
133
                  le32toh(dos_header->e_lfanew) + offsetof(PeHeader, optional));
230✔
134
        if (n < 0)
230✔
135
                return log_debug_errno(errno, "Failed to read PE executable optional header: %m");
×
136
        if ((size_t) n != le16toh(pe_header->pe.SizeOfOptionalHeader))
230✔
137
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading PE executable optional header.");
×
138

139
        if (!IN_SET(le16toh(pe_header->optional.Magic), UINT16_C(0x010B), UINT16_C(0x020B)))
230✔
140
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Optional header magic invalid.");
×
141

142
        if (pe_header_size(pe_header) !=
230✔
143
            PE_HEADER_OPTIONAL_FIELD_OFFSET(pe_header, DataDirectory) +
230✔
144
            sizeof(IMAGE_DATA_DIRECTORY) * (uint64_t) le32toh(PE_HEADER_OPTIONAL_FIELD(pe_header, NumberOfRvaAndSizes)))
230✔
145
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Optional header size mismatch.");
×
146

147
        if (ret_dos_header)
230✔
148
                *ret_dos_header = TAKE_PTR(dos_header);
84✔
149
        if (ret_pe_header)
230✔
150
                *ret_pe_header = TAKE_PTR(pe_header);
230✔
151

152
        return 0;
153
}
154

155
int pe_load_sections(
78✔
156
                int fd,
157
                const IMAGE_DOS_HEADER *dos_header,
158
                const PeHeader *pe_header,
159
                IMAGE_SECTION_HEADER **ret_sections) {
160

161
        _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
78✔
162
        size_t nos;
78✔
163
        ssize_t n;
78✔
164

165
        assert(fd >= 0);
78✔
166
        assert(dos_header);
78✔
167
        assert(pe_header);
78✔
168

169
        nos = le16toh(pe_header->pe.NumberOfSections);
78✔
170

171
        sections = new(IMAGE_SECTION_HEADER, nos);
78✔
172
        if (!sections)
78✔
173
                return log_oom_debug();
×
174

175
        n = pread(fd,
234✔
176
                  sections,
177
                  sizeof(IMAGE_SECTION_HEADER) * nos,
78✔
178
                  le32toh(dos_header->e_lfanew) + pe_header_size(pe_header));
78✔
179
        if (n < 0)
78✔
180
                return log_debug_errno(errno, "Failed to read section table: %m");
×
181
        if ((size_t) n != sizeof(IMAGE_SECTION_HEADER) * nos)
78✔
182
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading section table.");
×
183

184
        if (ret_sections)
78✔
185
                *ret_sections = TAKE_PTR(sections);
78✔
186

187
        return 0;
188
}
189

190
int pe_read_section_data(
100✔
191
                int fd,
192
                const IMAGE_SECTION_HEADER *section,
193
                size_t max_size,
194
                void **ret,
195
                size_t *ret_size) {
196

197
        assert(fd >= 0);
100✔
198
        assert(section);
100✔
199

200
        size_t n = le32toh(section->VirtualSize);
100✔
201
        if (n > MIN(max_size, (size_t) SSIZE_MAX))
100✔
202
                return -E2BIG;
100✔
203

204
        _cleanup_free_ void *data = malloc(n+1);
100✔
205
        if (!data)
100✔
206
                return -ENOMEM;
207

208
        ssize_t ss = pread(fd, data, n, le32toh(section->PointerToRawData));
100✔
209
        if (ss < 0)
100✔
210
                return -errno;
×
211
        if ((size_t) ss != n)
100✔
212
                return -EIO;
213

214
        if (ret_size)
100✔
215
                *ret_size = n;
×
216
        else {
217
                /* Check that there are no embedded NUL bytes if the caller doesn't want to know the size
218
                 * (i.e. treats the blob as a string) */
219
                const char *nul;
100✔
220

221
                nul = memchr(data, 0, n);
100✔
222
                if (nul && !memeqzero(nul, n - (nul - (const char*) data))) /* If there's a NUL it must only be NULs from there on */
100✔
223
                        return -EBADMSG;
224
        }
225
        if (ret) {
100✔
226
                ((uint8_t*) data)[n] = 0; /* NUL terminate, no matter what */
100✔
227
                *ret = TAKE_PTR(data);
100✔
228
        }
229

230
        return 0;
231
}
232

233
int pe_read_section_data_by_name(
×
234
                int fd,
235
                const PeHeader *pe_header,
236
                const IMAGE_SECTION_HEADER *sections,
237
                const char *name,
238
                size_t max_size,
239
                void **ret,
240
                size_t *ret_size) {
241

242
        const IMAGE_SECTION_HEADER *section;
×
243

244
        assert(fd >= 0);
×
245
        assert(pe_header);
×
246
        assert(sections || pe_header->pe.NumberOfSections == 0);
×
247
        assert(name);
×
248

249
        section = pe_header_find_section(pe_header, sections, name);
×
250
        if (!section)
×
251
                return -ENXIO;
252

253
        return pe_read_section_data(fd, section, max_size, ret, ret_size);
×
254
}
255

256
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
40✔
257
        assert(pe_header);
40✔
258
        assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
40✔
259

260
        if (le16toh(pe_header->optional.Subsystem) != IMAGE_SUBSYSTEM_EFI_APPLICATION)
40✔
261
                return false;
262

263
        /* Note that the UKI spec only requires .linux, but we are stricter here, and require .osrel too,
264
         * since for sd-boot it just doesn't make sense to not have that. */
265
        return
40✔
266
                pe_header_find_section(pe_header, sections, ".osrel") &&
80✔
267
                pe_header_find_section(pe_header, sections, ".linux");
40✔
268
}
269

270
bool pe_is_addon(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
10✔
271
        assert(pe_header);
10✔
272
        assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
10✔
273

274
        if (le16toh(pe_header->optional.Subsystem) != IMAGE_SUBSYSTEM_EFI_APPLICATION)
10✔
275
                return false;
276

277
        /* Add-ons do not have a Linux kernel, but do have one of .cmdline, .dtb, .initrd or .ucode (currently) */
278
        return !pe_header_find_section(pe_header, sections, ".linux") &&
20✔
279
                (pe_header_find_section(pe_header, sections, ".cmdline") ||
10✔
280
                 pe_header_find_section(pe_header, sections, ".dtb") ||
×
281
                 pe_header_find_section(pe_header, sections, ".initrd") ||
×
282
                 pe_header_find_section(pe_header, sections, ".ucode"));
×
283
}
284

285
bool pe_is_native(const PeHeader *pe_header) {
196✔
286
        assert(pe_header);
196✔
287

288
#ifdef _IMAGE_FILE_MACHINE_NATIVE
289
        return le16toh(pe_header->pe.Machine) == _IMAGE_FILE_MACHINE_NATIVE;
196✔
290
#else
291
        return false;
292
#endif
293
}
294

295
int pe_is_native_fd(int fd) {
146✔
296
        _cleanup_free_ PeHeader *pe_header = NULL;
146✔
297
        int r;
146✔
298

299
        r = pe_load_headers(fd, /* ret_dos_header= */ NULL, &pe_header);
146✔
300
        if (r < 0)
146✔
301
                return r;
302

303
        return pe_is_native(pe_header);
146✔
304
}
305

306
/* Implements:
307
 *
308
 * https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/authenticode_pe.docx
309
 * → Section "Calculating the PE Image Hash"
310
 */
311

312
#if HAVE_OPENSSL
313
static int hash_file(int fd, EVP_MD_CTX *md_ctx, uint64_t offset, uint64_t size) {
217✔
314
        uint8_t buffer[64*1024];
217✔
315

316
        log_debug("Hashing %" PRIu64 " @ %" PRIu64 " → %" PRIu64, size, offset, offset + size);
217✔
317

318
        while (size > 0) {
453✔
319
                size_t m = MIN(size, sizeof(buffer));
236✔
320
                ssize_t n;
236✔
321

322
                n = pread(fd, buffer, m, offset);
236✔
323
                if (n < 0)
236✔
324
                        return log_debug_errno(errno, "Failed to read file for hashing: %m");
×
325
                if ((size_t) n != m)
236✔
326
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while hashing.");
×
327

328
                if (EVP_DigestUpdate(md_ctx, buffer, m) != 1)
236✔
329
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
×
330

331
                offset += m;
236✔
332
                size -= m;
236✔
333
        }
334

335
        return 0;
336
}
337

338
static int section_offset_cmp(const IMAGE_SECTION_HEADER *a, const IMAGE_SECTION_HEADER *b) {
180✔
339
        return CMP(ASSERT_PTR(a)->PointerToRawData, ASSERT_PTR(b)->PointerToRawData);
180✔
340
}
341

342
int pe_hash(int fd,
24✔
343
            const EVP_MD *md,
344
            void **ret_hash,
345
            size_t *ret_hash_size) {
346

347
        _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
24✔
348
        _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
×
349
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
×
350
        _cleanup_free_ PeHeader *pe_header = NULL;
24✔
351
        const IMAGE_DATA_DIRECTORY *certificate_table;
24✔
352
        struct stat st;
24✔
353
        uint64_t p, q;
24✔
354
        int r;
24✔
355

356
        assert(fd >= 0);
24✔
357
        assert(md);
24✔
358
        assert(ret_hash_size);
24✔
359
        assert(ret_hash);
24✔
360

361
        if (fstat(fd, &st) < 0)
24✔
362
                return log_debug_errno(errno, "Failed to stat file: %m");
×
363
        r = stat_verify_regular(&st);
24✔
364
        if (r < 0)
24✔
365
                return log_debug_errno(r, "Not a regular file: %m");
2✔
366

367
        r = pe_load_headers(fd, &dos_header, &pe_header);
22✔
368
        if (r < 0)
22✔
369
                return r;
370

371
        r = pe_load_sections(fd, dos_header, pe_header, &sections);
20✔
372
        if (r < 0)
20✔
373
                return r;
374

375
        certificate_table = pe_header_get_data_directory(pe_header, IMAGE_DATA_DIRECTORY_INDEX_CERTIFICATION_TABLE);
20✔
376
        if (!certificate_table)
20✔
377
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks certificate table.");
×
378

379
        mdctx = EVP_MD_CTX_new();
20✔
380
        if (!mdctx)
20✔
381
                return log_oom_debug();
×
382

383
        if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
20✔
384
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to allocate message digest.");
×
385

386
        /* Everything from beginning of file to CheckSum field in PE header */
387
        p = (uint64_t) dos_header->e_lfanew +
20✔
388
                offsetof(PeHeader, optional.CheckSum);
389
        r = hash_file(fd, mdctx, 0, p);
20✔
390
        if (r < 0)
20✔
391
                return r;
392
        p += sizeof(le32_t);
20✔
393

394
        /* Everything between the CheckSum field and the Image Data Directory Entry for the Certification Table */
395
        q = (uint64_t) dos_header->e_lfanew +
40✔
396
                PE_HEADER_OPTIONAL_FIELD_OFFSET(pe_header, DataDirectory[IMAGE_DATA_DIRECTORY_INDEX_CERTIFICATION_TABLE]);
20✔
397
        r = hash_file(fd, mdctx, p, q - p);
20✔
398
        if (r < 0)
20✔
399
                return r;
400
        q += sizeof(IMAGE_DATA_DIRECTORY);
20✔
401

402
        /* The rest of the header + the section table */
403
        p = pe_header->optional.SizeOfHeaders;
20✔
404
        if (p < q)
20✔
405
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "SizeOfHeaders too short.");
×
406
        r = hash_file(fd, mdctx, q, p - q);
20✔
407
        if (r < 0)
20✔
408
                return r;
409

410
        /* Sort by location in file */
411
        typesafe_qsort(sections, pe_header->pe.NumberOfSections, section_offset_cmp);
20✔
412

413
        FOREACH_ARRAY(section, sections, pe_header->pe.NumberOfSections) {
160✔
414
                r = hash_file(fd, mdctx, section->PointerToRawData, section->SizeOfRawData);
140✔
415
                if (r < 0)
140✔
416
                        return r;
417

418
                p += section->SizeOfRawData;
140✔
419
        }
420

421
        if ((uint64_t) st.st_size > p) {
20✔
422

423
                if (st.st_size - p < certificate_table->Size)
1✔
424
                        return log_debug_errno(errno, "No space for certificate table, refusing.");
×
425

426
                r = hash_file(fd, mdctx, p, st.st_size - p - certificate_table->Size);
1✔
427
                if (r < 0)
1✔
428
                        return r;
429

430
                /* If the file size is not a multiple of 8 bytes, pad the hash with zero bytes. */
431
                if (st.st_size % 8 != 0 && EVP_DigestUpdate(mdctx, (const uint8_t[8]) {}, 8 - (st.st_size % 8)) != 1)
1✔
432
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
×
433
        }
434

435
        int hsz = EVP_MD_CTX_size(mdctx);
20✔
436
        if (hsz < 0)
20✔
437
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
×
438

439
        unsigned hash_size = (unsigned) hsz;
20✔
440
        _cleanup_free_ void *hash = malloc(hsz);
20✔
441
        if (!hash)
20✔
442
                return log_oom_debug();
×
443

444
        if (EVP_DigestFinal_ex(mdctx, hash, &hash_size) != 1)
20✔
445
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to finalize hash function.");
×
446

447
        assert(hash_size == (unsigned) hsz);
20✔
448

449
        *ret_hash = TAKE_PTR(hash);
20✔
450
        *ret_hash_size = hash_size;
20✔
451

452
        return 0;
20✔
453
}
454

455
int pe_checksum(int fd, uint32_t *ret) {
3✔
456
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
6✔
457
        _cleanup_free_ PeHeader *pe_header = NULL;
3✔
458
        struct stat st;
3✔
459
        int r;
3✔
460

461
        assert(fd >= 0);
3✔
462
        assert(ret);
3✔
463

464
        if (fstat(fd, &st) < 0)
3✔
465
                return log_debug_errno(errno, "Failed to stat file: %m");
×
466

467
        r = pe_load_headers(fd, &dos_header, &pe_header);
3✔
468
        if (r < 0)
3✔
469
                return r;
470

471
        uint32_t checksum = 0, checksum_offset = le32toh(dos_header->e_lfanew) + offsetof(PeHeader, optional.CheckSum);
3✔
472
        size_t off = 0;
3✔
473
        for (;;) {
21✔
474
                uint16_t buf[32*1024];
12✔
475

476
                ssize_t n = pread(fd, buf, sizeof(buf), off);
12✔
477
                if (n == 0)
12✔
478
                        break;
479
                if (n < 0)
9✔
480
                        return log_debug_errno(errno, "Failed to read from PE file: %m");
×
481
                if (n % sizeof(uint16_t) != 0)
9✔
482
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Short read from PE file");
×
483

484
                for (size_t i = 0; i < (size_t) n / 2; i++) {
223,929✔
485
                        if (off + i >= checksum_offset && off + i < checksum_offset + sizeof(pe_header->optional.CheckSum))
223,920✔
486
                                continue;
12✔
487

488
                        uint16_t val = le16toh(buf[i]);
223,908✔
489

490
                        checksum += val;
223,908✔
491
                        checksum = (checksum >> 16) + (checksum & 0xffff);
223,908✔
492
                }
493

494
                off += n;
9✔
495
        }
496

497
        checksum = (checksum >> 16) + (checksum & 0xffff);
3✔
498
        checksum += off;
3✔
499

500
        *ret = checksum;
3✔
501
        return 0;
3✔
502
}
503

504
typedef void* SectionHashArray[_UNIFIED_SECTION_MAX];
505

506
static void section_hash_array_done(SectionHashArray *array) {
8✔
507
        assert(array);
8✔
508

509
        for (size_t i = 0; i < _UNIFIED_SECTION_MAX; i++)
128✔
510
                free((*array)[i]);
120✔
511
}
8✔
512

513
int uki_hash(int fd,
8✔
514
             const EVP_MD *md,
515
             void* ret_hashes[static _UNIFIED_SECTION_MAX],
516
             size_t *ret_hash_size) {
517

518
        _cleanup_(section_hash_array_done) SectionHashArray hashes = {};
×
519
        _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
×
520
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
×
521
        _cleanup_free_ PeHeader *pe_header = NULL;
8✔
522
        int r;
8✔
523

524
        assert(fd >= 0);
8✔
525
        assert(ret_hashes);
8✔
526
        assert(ret_hash_size);
8✔
527

528
        r = pe_load_headers(fd, &dos_header, &pe_header);
8✔
529
        if (r < 0)
8✔
530
                return r;
531

532
        r = pe_load_sections(fd, dos_header, pe_header, &sections);
8✔
533
        if (r < 0)
8✔
534
                return r;
535

536
        int hsz = EVP_MD_size(md);
8✔
537
        if (hsz < 0)
8✔
538
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
×
539

540
        FOREACH_ARRAY(section, sections, pe_header->pe.NumberOfSections) {
64✔
541
                _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
16✔
542
                _cleanup_free_ char *n = NULL;
56✔
543
                ssize_t i;
56✔
544

545
                n = memdup_suffix0(section->Name, sizeof(section->Name));
56✔
546
                if (!n)
56✔
547
                        return log_oom_debug();
×
548

549
                i = string_table_lookup_from_string(unified_sections, _UNIFIED_SECTION_MAX, n);
56✔
550
                if (i < 0)
56✔
551
                        continue;
40✔
552

553
                if (hashes[i])
16✔
554
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section");
×
555

556
                mdctx = EVP_MD_CTX_new();
16✔
557
                if (!mdctx)
16✔
558
                        return log_oom_debug();
×
559

560
                if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
16✔
561
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to allocate message digest.");
×
562

563
                r = hash_file(fd, mdctx, section->PointerToRawData, MIN(section->VirtualSize, section->SizeOfRawData));
16✔
564
                if (r < 0)
16✔
565
                        return r;
566

567
                if (section->SizeOfRawData < section->VirtualSize) {
16✔
568
                        uint8_t zeroes[1024] = {};
×
569
                        size_t remaining = section->VirtualSize - section->SizeOfRawData;
×
570

571
                        while (remaining > 0) {
×
572
                                size_t sz = MIN(sizeof(zeroes), remaining);
×
573

574
                                if (EVP_DigestUpdate(mdctx, zeroes, sz) != 1)
×
575
                                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
×
576

577
                                remaining -= sz;
×
578
                        }
579
                }
580

581
                hashes[i] = malloc(hsz);
16✔
582
                if (!hashes[i])
16✔
583
                        return log_oom_debug();
×
584

585
                unsigned hash_size = (unsigned) hsz;
16✔
586
                if (EVP_DigestFinal_ex(mdctx, hashes[i], &hash_size) != 1)
16✔
587
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to finalize hash function.");
×
588

589
                assert(hash_size == (unsigned) hsz);
16✔
590

591
                if (DEBUG_LOGGING) {
16✔
592
                        _cleanup_free_ char *hs = NULL;
16✔
593

594
                        hs = hexmem(hashes[i], hsz);
16✔
595
                        log_debug("Section %s with %s is %s.", n, EVP_MD_name(md), strna(hs));
16✔
596
                }
597
        }
598

599
        memcpy(ret_hashes, hashes, sizeof(hashes));
8✔
600
        zero(hashes);
8✔
601
        *ret_hash_size = (unsigned) hsz;
8✔
602

603
        return 0;
8✔
604
}
605
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc