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

systemd / systemd / 19020191358

02 Nov 2025 05:04PM UTC coverage: 72.222% (-0.02%) from 72.241%
19020191358

push

github

web-flow
Enhance docs for ukify and direct kernel boots (#39516)

305246 of 422650 relevant lines covered (72.22%)

1085243.28 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
bool pe_header_is_64bit(const PeHeader *h) {
605✔
16
        assert(h);
605✔
17

18
        if (le16toh(h->optional.Magic) == UINT16_C(0x010B)) /* PE32 */
605✔
19
                return false;
20

21
        if (le16toh(h->optional.Magic) == UINT16_C(0x020B)) /* PE32+ */
333✔
22
                return true;
23

24
        assert_not_reached();
×
25
}
26

27
static size_t pe_header_size(const PeHeader *pe_header) {
649✔
28
        assert(pe_header);
649✔
29

30
        return offsetof(PeHeader, optional) + le16toh(pe_header->pe.SizeOfOptionalHeader);
649✔
31
}
32

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

37
        assert(h);
23✔
38

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

42
        return PE_HEADER_OPTIONAL_FIELD(h, DataDirectory) + i;
23✔
43
}
44

45
const IMAGE_SECTION_HEADER* pe_section_table_find(
680✔
46
                const IMAGE_SECTION_HEADER *sections,
47
                size_t n_sections,
48
                const char *name) {
49

50
        size_t n;
680✔
51

52
        assert(name);
680✔
53
        assert(sections || n_sections == 0);
680✔
54

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

59
        FOREACH_ARRAY(section, sections, n_sections)
4,199✔
60
                if (memcmp(section->Name, name, n) == 0 &&
4,080✔
61
                    (n == sizeof(sections[0].Name) || memeqzero(section->Name + n, sizeof(section->Name) - n)))
187✔
62
                        return section;
561✔
63

64
        return NULL;
65
}
66

67
const IMAGE_SECTION_HEADER* pe_header_find_section(
170✔
68
                const PeHeader *pe_header,
69
                const IMAGE_SECTION_HEADER *sections,
70
                const char *name) {
71

72
        assert(pe_header);
170✔
73

74
        return pe_section_table_find(sections, le16toh(pe_header->pe.NumberOfSections), name);
170✔
75
}
76

77
int pe_load_headers(
270✔
78
                int fd,
79
                IMAGE_DOS_HEADER **ret_dos_header,
80
                PeHeader **ret_pe_header) {
81

82
        _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
540✔
83
        _cleanup_free_ PeHeader *pe_header = NULL;
270✔
84
        ssize_t n;
270✔
85

86
        assert(fd >= 0);
270✔
87

88
        dos_header = new(IMAGE_DOS_HEADER, 1);
270✔
89
        if (!dos_header)
270✔
90
                return log_oom_debug();
×
91

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

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

104
        pe_header = new(PeHeader, 1);
268✔
105
        if (!pe_header)
268✔
106
                return log_oom_debug();
×
107

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

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

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

123
        PeHeader *pe_header_tmp = realloc(pe_header, MAX(sizeof(PeHeader), pe_header_size(pe_header)));
268✔
124
        if (!pe_header_tmp)
268✔
125
                return log_oom_debug();
×
126
        pe_header = pe_header_tmp;
268✔
127

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

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

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

145
        if (ret_dos_header)
268✔
146
                *ret_dos_header = TAKE_PTR(dos_header);
119✔
147
        if (ret_pe_header)
268✔
148
                *ret_pe_header = TAKE_PTR(pe_header);
268✔
149

150
        return 0;
151
}
152

153
int pe_load_sections(
113✔
154
                int fd,
155
                const IMAGE_DOS_HEADER *dos_header,
156
                const PeHeader *pe_header,
157
                IMAGE_SECTION_HEADER **ret_sections) {
158

159
        _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
113✔
160
        size_t nos;
113✔
161
        ssize_t n;
113✔
162

163
        assert(fd >= 0);
113✔
164
        assert(dos_header);
113✔
165
        assert(pe_header);
113✔
166

167
        nos = le16toh(pe_header->pe.NumberOfSections);
113✔
168

169
        sections = new(IMAGE_SECTION_HEADER, nos);
113✔
170
        if (!sections)
113✔
171
                return log_oom_debug();
×
172

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

182
        if (ret_sections)
113✔
183
                *ret_sections = TAKE_PTR(sections);
113✔
184

185
        return 0;
186
}
187

188
int pe_read_section_data(
170✔
189
                int fd,
190
                const IMAGE_SECTION_HEADER *section,
191
                size_t max_size,
192
                void **ret,
193
                size_t *ret_size) {
194

195
        assert(fd >= 0);
170✔
196
        assert(section);
170✔
197

198
        size_t n = le32toh(section->VirtualSize);
170✔
199
        if (n > MIN(max_size, (size_t) SSIZE_MAX))
170✔
200
                return -E2BIG;
170✔
201

202
        _cleanup_free_ void *data = malloc(n+1);
170✔
203
        if (!data)
170✔
204
                return -ENOMEM;
205

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

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

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

228
        return 0;
229
}
230

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

240
        const IMAGE_SECTION_HEADER *section;
×
241

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

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

251
        return pe_read_section_data(fd, section, max_size, ret, ret_size);
×
252
}
253

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

258
        if (le16toh(pe_header->optional.Subsystem) != IMAGE_SUBSYSTEM_EFI_APPLICATION)
68✔
259
                return false;
260

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

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

272
        if (le16toh(pe_header->optional.Subsystem) != IMAGE_SUBSYSTEM_EFI_APPLICATION)
17✔
273
                return false;
274

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

283
bool pe_is_native(const PeHeader *pe_header) {
234✔
284
        assert(pe_header);
234✔
285

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

293
int pe_is_native_fd(int fd) {
149✔
294
        _cleanup_free_ PeHeader *pe_header = NULL;
149✔
295
        int r;
149✔
296

297
        r = pe_load_headers(fd, /* ret_dos_header= */ NULL, &pe_header);
149✔
298
        if (r < 0)
149✔
299
                return r;
300

301
        return pe_is_native(pe_header);
149✔
302
}
303

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

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

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

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

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

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

329
                offset += m;
236✔
330
                size -= m;
236✔
331
        }
332

333
        return 0;
334
}
335

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

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

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

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

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

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

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

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

377
        mdctx = EVP_MD_CTX_new();
20✔
378
        if (!mdctx)
20✔
379
                return log_oom_debug();
×
380

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

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

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

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

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

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

416
                p += section->SizeOfRawData;
140✔
417
        }
418

419
        if ((uint64_t) st.st_size > p) {
20✔
420

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

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

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

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

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

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

445
        assert(hash_size == (unsigned) hsz);
20✔
446

447
        *ret_hash = TAKE_PTR(hash);
20✔
448
        *ret_hash_size = hash_size;
20✔
449

450
        return 0;
20✔
451
}
452

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

459
        assert(fd >= 0);
3✔
460
        assert(ret);
3✔
461

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

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

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

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

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

486
                        uint16_t val = le16toh(buf[i]);
223,140✔
487

488
                        checksum += val;
223,140✔
489
                        checksum = (checksum >> 16) + (checksum & 0xffff);
223,140✔
490
                }
491

492
                off += n;
9✔
493
        }
494

495
        checksum = (checksum >> 16) + (checksum & 0xffff);
3✔
496
        checksum += off;
3✔
497

498
        *ret = checksum;
3✔
499
        return 0;
3✔
500
}
501

502
typedef void* SectionHashArray[_UNIFIED_SECTION_MAX];
503

504
static void section_hash_array_done(SectionHashArray *array) {
8✔
505
        assert(array);
8✔
506

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

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

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

522
        assert(fd >= 0);
8✔
523
        assert(ret_hashes);
8✔
524
        assert(ret_hash_size);
8✔
525

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

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

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

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

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

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

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

554
                mdctx = EVP_MD_CTX_new();
16✔
555
                if (!mdctx)
16✔
556
                        return log_oom_debug();
×
557

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

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

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

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

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

575
                                remaining -= sz;
×
576
                        }
577
                }
578

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

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

587
                assert(hash_size == (unsigned) hsz);
16✔
588

589
                if (DEBUG_LOGGING) {
16✔
590
                        _cleanup_free_ char *hs = NULL;
16✔
591

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

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

601
        return 0;
8✔
602
}
603
#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