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

systemd / systemd / 24917789359

24 Apr 2026 04:59PM UTC coverage: 71.97% (-0.2%) from 72.201%
24917789359

push

github

bluca
units: order networkd resolve hook After=network-pre.target

Without this, the socket is available well before systemd-networkd.service
is able to start, because of its own After=network-pre.target ordering.
Then, if resolved handles queries before network-pre.target, it will
hang waiting for networkd to reply to hook queries.

This is currently happening in the wild with cloud-init.

322629 of 448281 relevant lines covered (71.97%)

1175521.89 hits per line

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

79.58
/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 "crypto-util.h"
8
#include "hexdecoct.h"
9
#include "log.h"
10
#include "pe-binary.h"
11
#include "sort-util.h"
12
#include "stat-util.h"
13
#include "string-table.h"
14
#include "string-util.h"
15

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

18
bool pe_header_is_64bit(const PeHeader *h) {
531✔
19
        assert(h);
531✔
20

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

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

27
        assert_not_reached();
×
28
}
29

30
static size_t pe_header_size(const PeHeader *pe_header) {
540✔
31
        assert(pe_header);
540✔
32

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

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

40
        assert(h);
23✔
41

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

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

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

53
        size_t n;
400✔
54

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

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

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

67
        return NULL;
68
}
69

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

75
        assert(pe_header);
100✔
76

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

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

85
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
466✔
86
        _cleanup_free_ PeHeader *pe_header = NULL;
233✔
87
        ssize_t n;
233✔
88

89
        assert(fd >= 0);
233✔
90

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

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

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

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

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

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

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

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

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

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

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

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

153
        return 0;
154
}
155

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

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

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

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

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

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

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

188
        return 0;
189
}
190

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

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

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

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

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

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

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

231
        return 0;
232
}
233

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

243
        const IMAGE_SECTION_HEADER *section;
×
244

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

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

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

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

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

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

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

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

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

286
bool pe_is_native(const PeHeader *pe_header) {
197✔
287
        assert(pe_header);
197✔
288

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

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

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

304
        return pe_is_native(pe_header);
147✔
305
}
306

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

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

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

319
        while (size > 0) {
473✔
320
                size_t m = MIN(size, sizeof(buffer));
256✔
321
                ssize_t n;
256✔
322

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

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

332
                offset += m;
256✔
333
                size -= m;
256✔
334
        }
335

336
        return 0;
337
}
338

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

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

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

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

362
        r = dlopen_libcrypto(LOG_DEBUG);
24✔
363
        if (r < 0)
24✔
364
                return r;
365

366
        if (fstat(fd, &st) < 0)
24✔
367
                return log_debug_errno(errno, "Failed to stat file: %m");
×
368
        r = stat_verify_regular(&st);
24✔
369
        if (r < 0)
24✔
370
                return log_debug_errno(r, "Not a regular file: %m");
2✔
371

372
        r = pe_load_headers(fd, &dos_header, &pe_header);
22✔
373
        if (r < 0)
22✔
374
                return r;
375

376
        r = pe_load_sections(fd, dos_header, pe_header, &sections);
20✔
377
        if (r < 0)
20✔
378
                return r;
379

380
        certificate_table = pe_header_get_data_directory(pe_header, IMAGE_DATA_DIRECTORY_INDEX_CERTIFICATION_TABLE);
20✔
381
        if (!certificate_table)
20✔
382
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks certificate table.");
×
383

384
        mdctx = sym_EVP_MD_CTX_new();
20✔
385
        if (!mdctx)
20✔
386
                return log_oom_debug();
×
387

388
        if (sym_EVP_DigestInit_ex(mdctx, md, NULL) != 1)
20✔
389
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to allocate message digest.");
×
390

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

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

407
        /* The rest of the header + the section table */
408
        p = le32toh(pe_header->optional.SizeOfHeaders);
20✔
409
        if (p < q)
20✔
410
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "SizeOfHeaders too short.");
×
411
        r = hash_file(fd, mdctx, q, p - q);
20✔
412
        if (r < 0)
20✔
413
                return r;
414

415
        /* Sort by location in file */
416
        typesafe_qsort(sections, le16toh(pe_header->pe.NumberOfSections), section_offset_cmp);
20✔
417

418
        FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections)) {
160✔
419
                r = hash_file(fd, mdctx, le32toh(section->PointerToRawData), le32toh(section->SizeOfRawData));
140✔
420
                if (r < 0)
140✔
421
                        return r;
422

423
                p += le32toh(section->SizeOfRawData);
140✔
424
        }
425

426
        if ((uint64_t) st.st_size > p) {
20✔
427

428
                if ((uint64_t) st.st_size - p < le32toh(certificate_table->Size))
1✔
429
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "No space for certificate table, refusing.");
×
430

431
                r = hash_file(fd, mdctx, p, (uint64_t) st.st_size - p - le32toh(certificate_table->Size));
1✔
432
                if (r < 0)
1✔
433
                        return r;
434

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

440
        int hsz = sym_EVP_MD_CTX_get_size(mdctx);
20✔
441
        if (hsz < 0)
20✔
442
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
×
443

444
        unsigned hash_size = (unsigned) hsz;
20✔
445
        _cleanup_free_ void *hash = malloc(hsz);
20✔
446
        if (!hash)
20✔
447
                return log_oom_debug();
×
448

449
        if (sym_EVP_DigestFinal_ex(mdctx, hash, &hash_size) != 1)
20✔
450
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to finalize hash function.");
×
451

452
        assert(hash_size == (unsigned) hsz);
20✔
453

454
        *ret_hash = TAKE_PTR(hash);
20✔
455
        *ret_hash_size = hash_size;
20✔
456

457
        return 0;
20✔
458
}
459

460
int pe_checksum(int fd, uint32_t *ret) {
3✔
461
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
6✔
462
        _cleanup_free_ PeHeader *pe_header = NULL;
3✔
463
        struct stat st;
3✔
464
        int r;
3✔
465

466
        assert(fd >= 0);
3✔
467
        assert(ret);
3✔
468

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

472
        r = pe_load_headers(fd, &dos_header, &pe_header);
3✔
473
        if (r < 0)
3✔
474
                return r;
475

476
        uint32_t checksum = 0, checksum_offset = le32toh(dos_header->e_lfanew) + offsetof(PeHeader, optional.CheckSum);
3✔
477
        size_t off = 0;
3✔
478
        for (;;) {
21✔
479
                uint16_t buf[32*1024];
12✔
480

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

489
                for (size_t i = 0; i < (size_t) n / 2; i++) {
274,617✔
490
                        if (off + i >= checksum_offset && off + i < checksum_offset + sizeof(pe_header->optional.CheckSum))
274,608✔
491
                                continue;
12✔
492

493
                        uint16_t val = le16toh(buf[i]);
274,596✔
494

495
                        checksum += val;
274,596✔
496
                        checksum = (checksum >> 16) + (checksum & 0xffff);
274,596✔
497
                }
498

499
                off += n;
9✔
500
        }
501

502
        checksum = (checksum >> 16) + (checksum & 0xffff);
3✔
503
        checksum += off;
3✔
504

505
        *ret = checksum;
3✔
506
        return 0;
3✔
507
}
508

509
typedef void* SectionHashArray[_UNIFIED_SECTION_MAX];
510

511
static void section_hash_array_done(SectionHashArray *array) {
8✔
512
        assert(array);
8✔
513

514
        for (size_t i = 0; i < _UNIFIED_SECTION_MAX; i++)
128✔
515
                free((*array)[i]);
120✔
516
}
8✔
517

518
int uki_hash(int fd,
8✔
519
             const EVP_MD *md,
520
             void* ret_hashes[static _UNIFIED_SECTION_MAX],
521
             size_t *ret_hash_size) {
522

523
        _cleanup_(section_hash_array_done) SectionHashArray hashes = {};
×
524
        _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
×
525
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
×
526
        _cleanup_free_ PeHeader *pe_header = NULL;
8✔
527
        int r;
8✔
528

529
        assert(fd >= 0);
8✔
530
        assert(ret_hashes);
8✔
531
        assert(ret_hash_size);
8✔
532

533
        r = dlopen_libcrypto(LOG_DEBUG);
8✔
534
        if (r < 0)
8✔
535
                return r;
536

537
        r = pe_load_headers(fd, &dos_header, &pe_header);
8✔
538
        if (r < 0)
8✔
539
                return r;
540

541
        r = pe_load_sections(fd, dos_header, pe_header, &sections);
8✔
542
        if (r < 0)
8✔
543
                return r;
544

545
        int hsz = sym_EVP_MD_get_size(md);
8✔
546
        if (hsz < 0)
8✔
547
                return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
×
548

549
        FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections)) {
64✔
550
                _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
×
551
                _cleanup_free_ char *n = NULL;
56✔
552
                ssize_t i;
56✔
553

554
                n = memdup_suffix0(section->Name, sizeof(section->Name));
56✔
555
                if (!n)
56✔
556
                        return log_oom_debug();
×
557

558
                i = string_table_lookup_from_string(unified_sections, _UNIFIED_SECTION_MAX, n);
56✔
559
                if (i < 0)
56✔
560
                        continue;
40✔
561

562
                if (hashes[i])
16✔
563
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section");
×
564

565
                mdctx = sym_EVP_MD_CTX_new();
16✔
566
                if (!mdctx)
16✔
567
                        return log_oom_debug();
×
568

569
                if (sym_EVP_DigestInit_ex(mdctx, md, NULL) != 1)
16✔
570
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to allocate message digest.");
×
571

572
                r = hash_file(fd, mdctx, le32toh(section->PointerToRawData), MIN(le32toh(section->VirtualSize), le32toh(section->SizeOfRawData)));
16✔
573
                if (r < 0)
16✔
574
                        return r;
575

576
                if (le32toh(section->SizeOfRawData) < le32toh(section->VirtualSize)) {
16✔
577
                        uint8_t zeroes[1024] = {};
×
578
                        size_t remaining = le32toh(section->VirtualSize) - le32toh(section->SizeOfRawData);
×
579

580
                        while (remaining > 0) {
×
581
                                size_t sz = MIN(sizeof(zeroes), remaining);
×
582

583
                                if (sym_EVP_DigestUpdate(mdctx, zeroes, sz) != 1)
×
584
                                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
×
585

586
                                remaining -= sz;
×
587
                        }
588
                }
589

590
                hashes[i] = malloc(hsz);
16✔
591
                if (!hashes[i])
16✔
592
                        return log_oom_debug();
×
593

594
                unsigned hash_size = (unsigned) hsz;
16✔
595
                if (sym_EVP_DigestFinal_ex(mdctx, hashes[i], &hash_size) != 1)
16✔
596
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to finalize hash function.");
×
597

598
                assert(hash_size == (unsigned) hsz);
16✔
599

600
                if (DEBUG_LOGGING) {
16✔
601
                        _cleanup_free_ char *hs = NULL;
16✔
602

603
                        hs = hexmem(hashes[i], hsz);
16✔
604
                        log_debug("Section %s with %s is %s.", n, sym_EVP_MD_get0_name(md), strna(hs));
16✔
605
                }
606
        }
607

608
        memcpy(ret_hashes, hashes, sizeof(hashes));
8✔
609
        zero(hashes);
8✔
610
        *ret_hash_size = (unsigned) hsz;
8✔
611

612
        return 0;
8✔
613
}
614
#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