From 762840e4e0ee97057131c10b462d63cc7e8d121a Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 7 Oct 2026 13:35:37 +0200 Subject: [PATCH] pe-binary: only read the raw data of a section pe_read_section_data() reads VirtualSize bytes starting at PointerToRawData. When VirtualSize is larger than SizeOfRawData, because the section ends with zero-initialized data, it returns the start of the next section, or fails with EBADMSG at the end of the file. Read MIN(VirtualSize, SizeOfRawData) bytes, like systemd-stub and uki_hash() do. (cherry picked from commit f219e7e790ec7b1064b71173ad697f48a5d71830) --- src/shared/pe-binary.c | 5 ++++- src/test/test-pe-binary.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/shared/pe-binary.c b/src/shared/pe-binary.c index 7c93f5c087..2b7e8fdbfd 100644 --- a/src/shared/pe-binary.c +++ b/src/shared/pe-binary.c @@ -251,7 +251,10 @@ int pe_read_section_data( assert(fd >= 0); assert(section); - size_t n = le32toh(section->VirtualSize); + /* SizeOfRawData includes padding up to the file alignment. VirtualSize is larger than + * SizeOfRawData when the section ends with zero-initialized data. The file does not contain + * that data. Reading VirtualSize bytes would return bytes of the next section instead. */ + size_t n = MIN(le32toh(section->VirtualSize), le32toh(section->SizeOfRawData)); if (n > MIN(max_size, (size_t) SSIZE_MAX)) return -EBADMSG; diff --git a/src/test/test-pe-binary.c b/src/test/test-pe-binary.c index af806009b7..eb15ad81e8 100644 --- a/src/test/test-pe-binary.c +++ b/src/test/test-pe-binary.c @@ -342,6 +342,40 @@ TEST(pe_load_sections_multi_one_past_eof) { ASSERT_ERROR(load_headers_and_sections(fd, &dos, &pe, §ions), EBADMSG); } +/* ====================================================================== + * pe_read_section_data + * ====================================================================== */ + +/* The bytes after the SizeOfRawData bytes of .cmdline belong to .uname, so reading .cmdline must not + * return them. */ +TEST(pe_read_section_data_reads_raw_data_only) { + size_t base = SECTION_TABLE_OFFSET + 2 * IMAGE_SECTION_HEADER_BYTES; + SectionSpec specs[2] = { + { .name = ".cmdline", .virtual_size = 32, .size_of_raw_data = 16, + .pointer_to_raw_data = (uint32_t) base }, + { .name = ".uname", .virtual_size = 8, .size_of_raw_data = 16, + .pointer_to_raw_data = (uint32_t) (base + 16) }, + }; + static const char contents[] = "0123456789abcdefABCDEFGHIJKLMNOP"; + _cleanup_close_ int fd = build_pe_file(specs, /* n_sections= */ 2, /* override_file_size= */ 0); + ASSERT_OK_EQ_ERRNO(pwrite(fd, contents, strlen(contents), base), (ssize_t) strlen(contents)); + + _cleanup_free_ PeHeader *pe = NULL; + _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL; + ASSERT_OK(pe_load_headers_and_sections(fd, &pe, §ions)); + + _cleanup_free_ void *data = NULL; + size_t size; + ASSERT_OK(pe_read_section_data(fd, sections + 0, SIZE_MAX, &data, &size)); + ASSERT_EQ(size, 16U); + ASSERT_EQ(memcmp(data, "0123456789abcdef", size), 0); + + data = mfree(data); + ASSERT_OK(pe_read_section_data(fd, sections + 1, SIZE_MAX, &data, &size)); + ASSERT_EQ(size, 8U); + ASSERT_EQ(memcmp(data, "ABCDEFGH", size), 0); +} + /* ====================================================================== * uki_hash — zero-padding cap from PLAN §3.2 * ====================================================================== */