According to the ELF spec:
PT_LOAD
The array element specifies a loadable segment, described by
p_filesz and p_memsz. The bytes from the file are mapped to the
beginning of the memory segment. If the segment's memory
size (p_memsz) is larger than the file size (p_filesz), the
``extra'' bytes are defined to hold the value 0 and to follow the
segment's initialized area. The file size may not be larger than the
memory size. Loadable segment entries in the program header table
appear in ascending order, sorted on the p_vaddr member.
which implies while both p_filesz and p_memsz can be zero we should
never see a case where p_filesz is greater than the in memory size.
Indeed it has been reported such a hand crafted ELF can blow up, for
example during rom_reset():
address_space_set(rom->as, rom->addr + rom->datasize, 0,
rom->romsize - rom->datasize,
MEMTXATTRS_UNSPECIFIED);
which could trigger and underflow leaving QEMU slowly filling a very
large buffer.
Fixes: https://gitlab.com/qemu-project/qemu/-/work_items/4056
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Cc: qemu-stable@nongnu.org
---
include/hw/elf_ops.h.inc | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/hw/elf_ops.h.inc b/include/hw/elf_ops.h.inc
index 9c35d1b9da6..c6d94ef760c 100644
--- a/include/hw/elf_ops.h.inc
+++ b/include/hw/elf_ops.h.inc
@@ -427,6 +427,10 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd,
file_size = ph->p_filesz; /* Size of the allocated data */
data_offset = ph->p_offset; /* Offset where the data is located */
+ if (file_size > mem_size) {
+ goto fail;
+ }
+
if (file_size > 0) {
if (g_mapped_file_get_length(mapped_file) <
file_size + data_offset) {
--
2.47.3
On 23/7/26 14:53, Alex Bennée wrote:
> According to the ELF spec:
>
> PT_LOAD
>
> The array element specifies a loadable segment, described by
> p_filesz and p_memsz. The bytes from the file are mapped to the
> beginning of the memory segment. If the segment's memory
> size (p_memsz) is larger than the file size (p_filesz), the
> ``extra'' bytes are defined to hold the value 0 and to follow the
> segment's initialized area. The file size may not be larger than the
> memory size. Loadable segment entries in the program header table
> appear in ascending order, sorted on the p_vaddr member.
>
> which implies while both p_filesz and p_memsz can be zero we should
> never see a case where p_filesz is greater than the in memory size.
> Indeed it has been reported such a hand crafted ELF can blow up, for
> example during rom_reset():
>
> address_space_set(rom->as, rom->addr + rom->datasize, 0,
> rom->romsize - rom->datasize,
> MEMTXATTRS_UNSPECIFIED);
>
> which could trigger and underflow leaving QEMU slowly filling a very
> large buffer.
>
> Fixes: https://gitlab.com/qemu-project/qemu/-/work_items/4056
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Cc: qemu-stable@nongnu.org
> ---
> include/hw/elf_ops.h.inc | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/include/hw/elf_ops.h.inc b/include/hw/elf_ops.h.inc
> index 9c35d1b9da6..c6d94ef760c 100644
> --- a/include/hw/elf_ops.h.inc
> +++ b/include/hw/elf_ops.h.inc
> @@ -427,6 +427,10 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd,
> file_size = ph->p_filesz; /* Size of the allocated data */
> data_offset = ph->p_offset; /* Offset where the data is located */
>
> + if (file_size > mem_size) {
Maybe use 'ret = ELF_LOAD_TOO_BIG;' or add ELF_LOAD_TOO_SMALL = -6 and
update load_elf_strerror() to return a more useful string?
$ git log --oneline |fgrep hw/elf
159fb790e48 hw/elf_ops: Rename elf_ops.h -> elf_ops.h.inc
1fed4cd04d8 Revert "hw/elf_ops: Ignore loadable segments with zero size"
62570f14341 hw/elf_ops: Ignore loadable segments with zero size
e1fee58fea3 include/hw/elf: Remove truncating signed casts
b4c4c1f1129 hw/elf_ops: clear uninitialized segment space
8975eb891fb hw/elf_ops.h: switch to ssize_t for elf loader return type
f413e514a91 hw/elf_ops: Fix a typo
5579b524b0d hw/elf_ops: Do not ignore write failures when loading ELF
"hw/elf_ops" in subject is more precise than "include/hw".
The fix LGTM but the error path could be easily improved :)
> + goto fail;
> + }
> +
> if (file_size > 0) {
> if (g_mapped_file_get_length(mapped_file) <
> file_size + data_offset) {
© 2016 - 2026 Red Hat, Inc.