The Pedigree Project 0.1
Elf-validation.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/TargetInfo.h"
21#include "pedigree/kernel/linker/Elf.h"
22#include "pedigree/kernel/utilities/utility.h"
23
24namespace {
25constexpr uint8_t ElfClass32 = 1;
26constexpr uint8_t ElfClass64 = 2;
27constexpr uint8_t ElfDataLittleEndian = 1;
28constexpr uint8_t ElfDataBigEndian = 2;
29constexpr uint8_t ElfCurrentVersion = 1;
30
31static_assert(sizeof(Elf_Off) <= sizeof(size_t));
32static_assert(sizeof(Elf_Addr) <= sizeof(uintptr_t));
33static_assert(sizeof(Elf_Xword) <= sizeof(uintptr_t));
34
35bool isExpectedMachine(Elf_Half machine) {
36#if X64 || defined(MACH_HOSTED)
37 return machine == 62; // EM_X86_64
38#elif ARM64
39 return machine == 183; // EM_AARCH64
40#elif ARMV7
41 return machine == 40; // EM_ARM
42#else
43 return false;
44#endif
45}
46
47bool rangeWithinFile(Elf_Off offset, Elf_Xword size, size_t fileSize) {
48 if (offset > fileSize) {
49 return false;
50 }
51
52 return size <= (fileSize - static_cast<size_t>(offset));
53}
54
55bool loadPageRange(Elf_Addr address, Elf_Xword size, uintptr_t& start, uintptr_t& end) {
56 const uintptr_t maximum = ~uintptr_t{0};
57 const uintptr_t pageMask = TargetInfo::getPageOffsetMask();
58 const uintptr_t nativeAddress = static_cast<uintptr_t>(address);
59 const uintptr_t nativeSize = static_cast<uintptr_t>(size);
60 if (nativeSize > (maximum - nativeAddress)) {
61 return false;
62 }
63
64 const uintptr_t pageOffset = nativeAddress & pageMask;
65 if (nativeSize > (maximum - pageOffset)) {
66 return false;
67 }
68
69 start = nativeAddress & ~pageMask;
70 end = nativeAddress + nativeSize;
71 if (end & pageMask) {
72 if (end > (maximum - pageMask)) {
73 return false;
74 }
75 end = (end + pageMask) & ~pageMask;
76 }
77
78 return true;
79}
80} // namespace
81
82Elf::ExecutableValidationResult Elf::validateExecutableHeader(const uint8_t* pBuffer, size_t length,
83 size_t fileSize,
84 ExecutableMetadata& metadata) {
85 metadata = ExecutableMetadata{};
86 if (!pBuffer || length < sizeof(ElfHeader_t) || fileSize < sizeof(ElfHeader_t)) {
87 return ExecutableValidationResult::Malformed;
88 }
89
90 ElfHeader_t header;
91 MemoryCopy(&header, pBuffer, sizeof(header));
92 if (header.ident[0] != 0x7f || header.ident[1] != 'E' || header.ident[2] != 'L' ||
93 header.ident[3] != 'F') {
94 return ExecutableValidationResult::Malformed;
95 }
96
97 const uint8_t expectedClass = BITS_32 ? ElfClass32 : ElfClass64;
98 const uint8_t expectedData =
99 TargetInfo::isLittleEndian() ? ElfDataLittleEndian : ElfDataBigEndian;
100 if (header.ident[4] != expectedClass || header.ident[5] != expectedData ||
101 !isExpectedMachine(header.machine)) {
102 return ExecutableValidationResult::WrongArchitecture;
103 }
104
105 if (header.ident[6] != ElfCurrentVersion || header.version != ElfCurrentVersion) {
106 return ExecutableValidationResult::Malformed;
107 }
108 if (header.type != ET_EXEC && header.type != ET_DYN) {
109 return ExecutableValidationResult::UnsupportedType;
110 }
111 if (header.ehsize != sizeof(ElfHeader_t) || header.phentsize != sizeof(ElfProgramHeader_t) ||
112 !header.phnum ||
113 header.phnum > (MaximumProgramHeaderTableSize / sizeof(ElfProgramHeader_t))) {
114 return ExecutableValidationResult::Malformed;
115 }
116
117 const size_t programHeaderSize = static_cast<size_t>(header.phnum) * sizeof(ElfProgramHeader_t);
118 if (header.phoff < sizeof(ElfHeader_t) ||
119 !rangeWithinFile(header.phoff, programHeaderSize, fileSize)) {
120 return ExecutableValidationResult::Malformed;
121 }
122
123 metadata.type = header.type;
124 metadata.entryPoint = static_cast<uintptr_t>(header.entry);
125 metadata.programHeaderOffset = static_cast<size_t>(header.phoff);
126 metadata.programHeaderCount = header.phnum;
127 metadata.programHeaderSize = programHeaderSize;
128 return ExecutableValidationResult::Valid;
129}
130
131Elf::ExecutableValidationResult Elf::validateExecutableProgramHeaders(
132 const uint8_t* pBuffer, size_t length, size_t fileSize, ExecutableMetadata& metadata) {
133 if (!pBuffer || !metadata.programHeaderCount ||
134 metadata.programHeaderCount > (MaximumProgramHeaderTableSize / sizeof(ElfProgramHeader_t))) {
135 return ExecutableValidationResult::Malformed;
136 }
137
138 const size_t expectedSize = metadata.programHeaderCount * sizeof(ElfProgramHeader_t);
139 if (metadata.programHeaderSize != expectedSize || length < expectedSize ||
140 !rangeWithinFile(metadata.programHeaderOffset, expectedSize, fileSize)) {
141 return ExecutableValidationResult::Malformed;
142 }
143
144 metadata.loadStart = ~uintptr_t{0};
145 metadata.loadEnd = 0;
146 metadata.interpreterOffset = 0;
147 metadata.interpreterSize = 0;
148 metadata.hasInterpreter = false;
149
150 const uintptr_t pageMask = TargetInfo::getPageOffsetMask();
151 bool hasLoadSegment = false;
152 bool entryPointCovered = false;
153 for (size_t i = 0; i < metadata.programHeaderCount; ++i) {
154 ElfProgramHeader_t header;
155 MemoryCopy(&header, pBuffer + (i * sizeof(header)), sizeof(header));
156
157 if (header.type != PT_NULL && header.filesz &&
158 !rangeWithinFile(header.offset, header.filesz, fileSize)) {
159 return ExecutableValidationResult::Malformed;
160 }
161
162 if (header.type == PT_INTERP) {
163 if (metadata.hasInterpreter) {
164 return ExecutableValidationResult::MultipleInterpreters;
165 }
166 if (header.filesz < 2 || header.filesz > MaximumInterpreterSize ||
167 !rangeWithinFile(header.offset, header.filesz, fileSize)) {
168 return ExecutableValidationResult::Malformed;
169 }
170
171 metadata.hasInterpreter = true;
172 metadata.interpreterOffset = static_cast<size_t>(header.offset);
173 metadata.interpreterSize = static_cast<size_t>(header.filesz);
174 }
175
176 if (header.type != PT_LOAD) {
177 continue;
178 }
179 if (!rangeWithinFile(header.offset, header.filesz, fileSize) || header.filesz > header.memsz) {
180 return ExecutableValidationResult::Malformed;
181 }
182 if (header.align > 1 &&
183 ((header.align & (header.align - 1)) ||
184 ((header.vaddr & (header.align - 1)) != (header.offset & (header.align - 1))))) {
185 return ExecutableValidationResult::Malformed;
186 }
187 if ((header.vaddr & pageMask) != (header.offset & pageMask)) {
188 return ExecutableValidationResult::Malformed;
189 }
190 if (!header.memsz) {
191 continue;
192 }
193
194 uintptr_t pageStart = 0;
195 uintptr_t pageEnd = 0;
196 if (!loadPageRange(header.vaddr, header.memsz, pageStart, pageEnd)) {
197 return ExecutableValidationResult::Malformed;
198 }
199
200 for (size_t j = 0; j < i; ++j) {
201 ElfProgramHeader_t previous;
202 MemoryCopy(&previous, pBuffer + (j * sizeof(previous)), sizeof(previous));
203 if (previous.type != PT_LOAD || !previous.memsz) {
204 continue;
205 }
206
207 uintptr_t previousStart = 0;
208 uintptr_t previousEnd = 0;
209 if (!loadPageRange(previous.vaddr, previous.memsz, previousStart, previousEnd)) {
210 return ExecutableValidationResult::Malformed;
211 }
212 if (pageStart < previousEnd && previousStart < pageEnd) {
213 return ExecutableValidationResult::UnsupportedLayout;
214 }
215 }
216
217 hasLoadSegment = true;
218 if (pageStart < metadata.loadStart) {
219 metadata.loadStart = pageStart;
220 }
221 if (pageEnd > metadata.loadEnd) {
222 metadata.loadEnd = pageEnd;
223 }
224
225 if ((header.flags & PF_X) && metadata.entryPoint >= header.vaddr &&
226 (metadata.entryPoint - header.vaddr) < header.memsz) {
227 entryPointCovered = true;
228 }
229 }
230
231 if (!hasLoadSegment || !entryPointCovered) {
232 return ExecutableValidationResult::Malformed;
233 }
234 if (metadata.type == ET_DYN && metadata.loadStart != 0) {
235 return ExecutableValidationResult::UnsupportedLayout;
236 }
237
238 return ExecutableValidationResult::Valid;
239}
240
241Elf::ExecutableValidationResult Elf::validateExecutableInterpreter(
242 const uint8_t* pBuffer, size_t length, const ExecutableMetadata& metadata) {
243 if (!metadata.hasInterpreter) {
244 return ExecutableValidationResult::Valid;
245 }
246 if (!pBuffer || metadata.interpreterSize < 2 ||
247 metadata.interpreterSize > MaximumInterpreterSize || length < metadata.interpreterSize ||
248 pBuffer[metadata.interpreterSize - 1] != 0) {
249 return ExecutableValidationResult::Malformed;
250 }
251
252 return ExecutableValidationResult::Valid;
253}
static ExecutableValidationResult validateExecutableProgramHeaders(const uint8_t *pBuffer, size_t length, size_t fileSize, ExecutableMetadata &metadata)
static ExecutableValidationResult validateExecutableInterpreter(const uint8_t *pBuffer, size_t length, const ExecutableMetadata &metadata)
static ExecutableValidationResult validateExecutableHeader(const uint8_t *pBuffer, size_t length, size_t fileSize, ExecutableMetadata &metadata)