From 68d4b11c7235d97e14a4e1d20004a7a839d5f880 Mon Sep 17 00:00:00 2001 From: Antoine Busque Date: Mon, 9 May 2016 17:54:44 -0400 Subject: [PATCH] Fix: erroneous computation of ELF in-memory size The current algorithm for computation of ELF in-memory size computed values using the `p_align` field from program headers to align loaded segments, when in fact `p_align` is only used to describe the relationship between a segment's offset in the ELF file and its virtual address once loaded in memory (`p_vaddr`), not the alignment between segments. (Refer to the ELF specification version 1.1 at pages 2-2 and 2-8 for more details.) This implementation instead uses the `p_memsz` and `p_vaddr` fields to compute the highest virtual address of the executable, and uses the difference from its base address as the in-memory size. Signed-off-by: Antoine Busque --- include/lttng/ust-elf.h | 3 +++ liblttng-ust/lttng-ust-elf.c | 29 ++++++++++------------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/include/lttng/ust-elf.h b/include/lttng/ust-elf.h index e9b0ac7b..c4abee2d 100644 --- a/include/lttng/ust-elf.h +++ b/include/lttng/ust-elf.h @@ -77,6 +77,7 @@ bswap((phdr).p_filesz); \ bswap((phdr).p_memsz); \ bswap((phdr).p_align); \ + bswap((phdr).p_vaddr); \ } while (0) #define bswap_shdr(shdr) \ @@ -117,6 +118,7 @@ (dst_phdr).p_filesz = (src_phdr).p_filesz; \ (dst_phdr).p_memsz = (src_phdr).p_memsz; \ (dst_phdr).p_align = (src_phdr).p_align; \ + (dst_phdr).p_vaddr = (src_phdr).p_vaddr; \ } while (0) #define copy_shdr(src_shdr, dst_shdr) \ @@ -172,6 +174,7 @@ struct lttng_ust_elf_phdr { uint64_t p_filesz; uint64_t p_memsz; uint64_t p_align; + uint64_t p_vaddr; }; struct lttng_ust_elf_shdr { diff --git a/liblttng-ust/lttng-ust-elf.c b/liblttng-ust/lttng-ust-elf.c index 34654558..c9d2b6b3 100644 --- a/liblttng-ust/lttng-ust-elf.c +++ b/liblttng-ust/lttng-ust-elf.c @@ -354,7 +354,7 @@ void lttng_ust_elf_destroy(struct lttng_ust_elf *elf) int lttng_ust_elf_get_memsz(struct lttng_ust_elf *elf, uint64_t *memsz) { uint16_t i; - uint64_t _memsz = 0; + uint64_t low_addr = UINT64_MAX, high_addr = 0; if (!elf || !memsz) { goto error; @@ -362,7 +362,6 @@ int lttng_ust_elf_get_memsz(struct lttng_ust_elf *elf, uint64_t *memsz) for (i = 0; i < elf->ehdr->e_phnum; ++i) { struct lttng_ust_elf_phdr *phdr; - uint64_t align; phdr = lttng_ust_elf_get_phdr(elf, i); if (!phdr) { @@ -377,27 +376,19 @@ int lttng_ust_elf_get_memsz(struct lttng_ust_elf *elf, uint64_t *memsz) goto next_loop; } - /* - * A p_align of 0 means no alignment, i.e. aligned to - * 1 byte. - */ - align = phdr->p_align == 0 ? 1 : phdr->p_align; - /* Align the start of the segment. */ - _memsz += offset_align(_memsz, align); - _memsz += phdr->p_memsz; - /* - * Add padding at the end of the segment, so it ends - * on a multiple of the align value (which usually - * means a page boundary). This makes the computation - * valid even in cases where p_align would change from - * one segment to the next. - */ - _memsz += offset_align(_memsz, align); + low_addr = phdr->p_vaddr < low_addr ? phdr->p_vaddr : low_addr; + high_addr = phdr->p_vaddr + phdr->p_memsz > high_addr ? + phdr->p_vaddr + phdr->p_memsz : high_addr; next_loop: free(phdr); } - *memsz = _memsz; + if (high_addr < low_addr) { + /* No PT_LOAD segments or corrupted data. */ + goto error; + } + + *memsz = high_addr - low_addr; return 0; error: return -1; -- 2.34.1