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 * ====================================================================== */