blob: 6296f0e44b16fe7e0bebba4fac888ca87f2fc1a5 [file] [log] [blame]
Andrew Geissler517393d2023-01-13 08:55:19 -06001From 8d2cb4f9ab8d564904c292099a022ffb3cccd52d Mon Sep 17 00:00:00 2001
2From: Jason <otherjason@nodomain.com>
3Date: Fri, 2 Dec 2022 10:01:41 -0500
4Subject: [PATCH] Fix bug in file shifting that could cause conflicting PT_LOAD
5 segments
6
7When a section in the file needs to be enlarged (e.g. to accommodate
8setting a larger RPATH), shiftFile() is used to shift all content
9following the growing section to a later position in the file.
10
11Commit 109b771f53ee3d37ede8c0f165665605183c0975 introduced logic to
12ensure that, after the segment split, no sections span multiple
13segments. This is done by sliding the portion of the segment after the
14split point later in the file, then adding a new PT_LOAD segment that
15contains the preceding data plus the extra room that is being added. The
16existing implementation does this by simply adding
17`extraPages*getPageSize()` bytes to the number of bytes ahead of the
18split point in the segment.
19
20However, this approach can result in two PT_LOAD segments that overlap
21when page boundaries are taken into account. As an example, this PT_LOAD
22section (taken from a Python 3.10 binary):
23
24LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
25 0x0000000000000948 0x0000000000000948 R E 0x200000
26
27is split into the following two sections:
28
29LOAD 0x0000000000000000 0x00000000003ff000 0x00000000003ff000
30 0x0000000000001594 0x0000000000001594 R E 0x1000
31LOAD 0x0000000000001594 0x0000000000400594 0x0000000000400594
32 0x00000000000003b4 0x00000000000003b4 R E 0x1000
33
34Note that the two PT_LOAD sections both contain the memory page at
35address 0x400000. The Linux kernel's ELF loader (at least as of v4.18)
36does not accept this as a valid ELF executable, triggering a segfault
37with si_code=SI_KERNEL immediately when the binary is executed.
38
39The fix here is to set the length of the segment that comes before the
40split point more carefully; instead of adding `extraPages*getPageSize()`
41bytes to the portion of the segment that came before the split, the
42actual number of padding bytes that were needed (before rounding up to
43the next multiple of the page size) are used. This avoids the overlap
44in the PT_LOAD segments and makes the output files executable again.
45---
46 src/patchelf.cc | 10 ++++++----
47 src/patchelf.h | 2 +-
48 2 files changed, 7 insertions(+), 5 deletions(-)
49
50Upstream-Status: Submitted [https://github.com/NixOS/patchelf/pull/447]
51Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
52
53Index: git/src/patchelf.cc
54===================================================================
55--- git.orig/src/patchelf.cc
56+++ git/src/patchelf.cc
57@@ -432,7 +432,7 @@ static uint64_t roundUp(uint64_t n, uint
58
59
60 template<ElfFileParams>
61-void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, size_t startOffset)
62+void ElfFile<ElfFileParamNames>::shiftFile(unsigned int extraPages, size_t startOffset, size_t extraBytes)
63 {
64 assert(startOffset >= sizeof(Elf_Ehdr));
65
66@@ -508,7 +508,7 @@ void ElfFile<ElfFileParamNames>::shiftFi
67 wri(phdr.p_offset, phdrs.at(splitIndex).p_offset - splitShift - shift);
68 wri(phdr.p_paddr, phdrs.at(splitIndex).p_paddr - splitShift - shift);
69 wri(phdr.p_vaddr, phdrs.at(splitIndex).p_vaddr - splitShift - shift);
70- wri(phdr.p_filesz, wri(phdr.p_memsz, splitShift + shift));
71+ wri(phdr.p_filesz, wri(phdr.p_memsz, splitShift + extraBytes));
72 wri(phdr.p_flags, PF_R | PF_W);
73 wri(phdr.p_align, getPageSize());
74 }
75@@ -898,12 +898,14 @@ void ElfFile<ElfFileParamNames>::rewrite
76 neededSpace += sizeof(Elf_Phdr);
77 debug("needed space is %d\n", neededSpace);
78
79- unsigned int neededPages = roundUp(neededSpace - startOffset, getPageSize()) / getPageSize();
80+ /* Calculate how many bytes are needed out of the additional pages. */
81+ size_t extraSpace = neededSpace - startOffset;
82+ unsigned int neededPages = roundUp(extraSpace, getPageSize()) / getPageSize();
83 debug("needed pages is %d\n", neededPages);
84 if (neededPages * getPageSize() > firstPage)
85 error("virtual address space underrun!");
86
87- shiftFile(neededPages, startOffset);
88+ shiftFile(neededPages, startOffset, extraSpace);
89
90 firstPage -= neededPages * getPageSize();
91 startOffset += neededPages * getPageSize();
92Index: git/src/patchelf.h
93===================================================================
94--- git.orig/src/patchelf.h
95+++ git/src/patchelf.h
96@@ -77,7 +77,7 @@ private:
97
98 void sortShdrs();
99
100- void shiftFile(unsigned int extraPages, size_t sizeOffset);
101+ void shiftFile(unsigned int extraPages, size_t sizeOffset, size_t extraBytes);
102
103 std::string getSectionName(const Elf_Shdr & shdr) const;
104