8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/TargetInfo.h"
10#include "pedigree/kernel/linker/Elf.h"
11#include "pedigree/kernel/utilities/utility.h"
14bool fail(
const char* detail) {
15 ERROR(
"HOSTED-ELF-VALIDATION-TEST: FAIL: " << detail);
19class ExecutableValidationFixture final :
public Elf {
22 using ProgramHeader = ElfProgramHeader_t;
23 using Metadata = ExecutableMetadata;
24 using Result = ExecutableValidationResult;
26 static constexpr size_t ImageSize = 8192;
27 static constexpr size_t ProgramHeaderCapacity = 3;
29 ExecutableValidationFixture() {
33 void reset(Elf_Half type = ET_EXEC,
size_t programHeaderCount = 1) {
34 ByteSet(&header, 0,
sizeof(header));
35 ByteSet(programHeaders, 0,
sizeof(programHeaders));
36 ByteSet(image, 0,
sizeof(image));
37 terminateInterpreter =
true;
39 header.ident[0] = 0x7f;
40 header.ident[1] =
'E';
41 header.ident[2] =
'L';
42 header.ident[3] =
'F';
43 header.ident[4] = BITS_32 ? 1 : 2;
44 header.ident[5] = TargetInfo::isLittleEndian() ? 1 : 2;
47#if X64 || defined(MACH_HOSTED)
51 header.entry = type == ET_DYN ? 0x100 : 0x400100;
52 header.phoff =
sizeof(header);
53 header.ehsize =
sizeof(header);
54 header.phentsize =
sizeof(programHeaders[0]);
55 header.phnum = programHeaderCount;
57 ProgramHeader& load = programHeaders[0];
59 load.flags = PF_R | PF_X;
61 load.vaddr = type == ET_DYN ? 0 : 0x400000;
62 load.filesz = ImageSize;
63 load.memsz = ImageSize;
67 void addInterpreter(
size_t index = 1,
size_t offset = 512,
size_t size =
sizeof(Interpreter)) {
68 header.phnum = index + 1;
69 ProgramHeader& interpreter = programHeaders[index];
70 ByteSet(&interpreter, 0,
sizeof(interpreter));
71 interpreter.type = PT_INTERP;
72 interpreter.offset = offset;
73 interpreter.filesz = size;
74 interpreter.memsz = size;
75 interpreter.align = 1;
78 Result validate(
size_t programHeaderLength = ~
size_t{0},
size_t interpreterLength = ~size_t{0}) {
80 return validate(metadata, programHeaderLength, interpreterLength);
83 Result validate(Metadata& metadata,
size_t programHeaderLength = ~
size_t{0},
84 size_t interpreterLength = ~size_t{0}) {
86 metadata = Metadata{};
88 if (result != Result::Valid) {
92 const size_t actualProgramHeaderLength =
93 programHeaderLength == ~size_t{0} ? metadata.programHeaderSize : programHeaderLength;
95 actualProgramHeaderLength, ImageSize, metadata);
96 if (result != Result::Valid || !metadata.hasInterpreter) {
100 const size_t actualInterpreterLength =
101 interpreterLength == ~size_t{0} ? metadata.interpreterSize : interpreterLength;
103 actualInterpreterLength, metadata);
106 Result validateHeader(
size_t length =
sizeof(Header),
size_t fileSize = ImageSize) {
113 ProgramHeader programHeaders[ProgramHeaderCapacity];
114 bool terminateInterpreter;
117 static constexpr char Interpreter[] =
"/lib/ld.so";
120 ByteSet(image, 0,
sizeof(image));
121 uint8_t*
const data = image + 1;
122 MemoryCopy(data, &header,
sizeof(header));
125 header.phnum < ProgramHeaderCapacity ? header.phnum : ProgramHeaderCapacity;
126 const size_t programHeaderSize = count *
sizeof(programHeaders[0]);
127 if (header.phoff <= ImageSize && programHeaderSize <= (ImageSize - header.phoff)) {
128 MemoryCopy(data + header.phoff, programHeaders, programHeaderSize);
131 for (
size_t i = 0; i < count; ++i) {
132 const ProgramHeader& programHeader = programHeaders[i];
133 if (programHeader.type != PT_INTERP || programHeader.offset >= ImageSize) {
137 const size_t available = ImageSize - programHeader.offset;
138 const size_t copySize =
sizeof(Interpreter) < available ?
sizeof(Interpreter) : available;
139 MemoryCopy(data + programHeader.offset, Interpreter, copySize);
140 if (!terminateInterpreter && programHeader.filesz && programHeader.filesz <= available) {
141 data[programHeader.offset + programHeader.filesz - 1] =
'x';
147 alignas(Elf_Xword) uint8_t image[ImageSize + 1];
150constexpr char ExecutableValidationFixture::Interpreter[];
152bool expect(Elf::ExecutableValidationResult actual, Elf::ExecutableValidationResult expected,
153 const char* detail) {
154 return actual == expected || fail(detail);
157#define EXPECT_RESULT(actual, expected, detail) \
159 if (!expect((actual), (expected), (detail))) \
163bool validImagesAndMetadata() {
164 using Fixture = ExecutableValidationFixture;
165 using Result = Elf::ExecutableValidationResult;
168 Fixture::Metadata metadata{};
169 EXPECT_RESULT(
fixture.validate(metadata), Result::Valid,
"valid ET_EXEC rejected");
170 if (metadata.type != ET_EXEC || metadata.entryPoint != 0x400100 ||
171 metadata.programHeaderOffset !=
sizeof(Fixture::Header) || metadata.programHeaderCount != 1 ||
172 metadata.programHeaderSize !=
sizeof(Fixture::ProgramHeader) ||
173 metadata.loadStart != 0x400000 || metadata.loadEnd != 0x400000 + Fixture::ImageSize ||
174 metadata.hasInterpreter) {
175 return fail(
"valid ET_EXEC metadata was incorrect");
179 EXPECT_RESULT(
fixture.validate(metadata), Result::Valid,
"valid zero-based ET_DYN rejected");
180 if (metadata.loadStart != 0 || metadata.loadEnd != Fixture::ImageSize) {
181 return fail(
"valid ET_DYN metadata was incorrect");
187 fixture.programHeaders[1].type = PT_LOAD;
188 fixture.programHeaders[1].flags = PF_R;
194 EXPECT_RESULT(
fixture.validate(), Result::Valid,
"non-overlapping load segments rejected");
198bool headerValidation() {
199 using Fixture = ExecutableValidationFixture;
200 using Result = Elf::ExecutableValidationResult;
203 Fixture::Metadata metadata{};
206 Result::Malformed,
"null ELF header accepted");
207 EXPECT_RESULT(
fixture.validateHeader(
sizeof(Fixture::Header) - 1), Result::Malformed,
208 "truncated ELF header accepted");
209 EXPECT_RESULT(
fixture.validateHeader(
sizeof(Fixture::Header),
sizeof(Fixture::Header) - 1),
210 Result::Malformed,
"ELF header outside file accepted");
214 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
"bad ELF magic accepted");
216 fixture.header.ident[4] = BITS_32 ? 2 : 1;
217 EXPECT_RESULT(
fixture.validateHeader(), Result::WrongArchitecture,
"wrong ELF class accepted");
219 fixture.header.ident[5] = TargetInfo::isLittleEndian() ? 2 : 1;
220 EXPECT_RESULT(
fixture.validateHeader(), Result::WrongArchitecture,
221 "wrong ELF byte order accepted");
224 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
"bad ident version accepted");
227 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
"bad ELF version accepted");
230 EXPECT_RESULT(
fixture.validateHeader(), Result::UnsupportedType,
"ET_REL accepted for execution");
233 EXPECT_RESULT(
fixture.validateHeader(), Result::WrongArchitecture,
"wrong ELF machine accepted");
236 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
"bad ELF header size accepted");
239 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
"bad program header size accepted");
242 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
"empty program header table accepted");
244 fixture.header.phnum = (Elf::MaximumProgramHeaderTableSize /
sizeof(Fixture::ProgramHeader)) + 1;
245 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
246 "oversized program header table accepted");
248 fixture.header.phoff =
sizeof(Fixture::Header) - 1;
249 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
250 "overlapping ELF and program headers accepted");
252 fixture.header.phoff = Fixture::ImageSize - 1;
253 EXPECT_RESULT(
fixture.validateHeader(), Result::Malformed,
254 "program header table past EOF accepted");
256 fixture.header.phoff = ~Elf_Off{0} - 8;
257 EXPECT_RESULT(
fixture.validateHeader(
sizeof(Fixture::Header), ~
size_t{0}), Result::Malformed,
258 "overflowing program header table accepted");
262bool loadSegmentValidation() {
263 using Fixture = ExecutableValidationFixture;
264 using Result = Elf::ExecutableValidationResult;
267 EXPECT_RESULT(
fixture.validate(
sizeof(Fixture::ProgramHeader) - 1), Result::Malformed,
268 "truncated program header buffer accepted");
270 fixture.programHeaders[0].type = PT_NOTE;
271 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"ELF without a load segment accepted");
273 fixture.programHeaders[0].filesz = 0;
274 fixture.programHeaders[0].memsz = 0;
275 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"ELF without a nonempty load accepted");
277 fixture.programHeaders[0].memsz = Fixture::ImageSize - 1;
278 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"load filesz greater than memsz accepted");
280 fixture.programHeaders[0].offset = Fixture::ImageSize - 8;
281 fixture.programHeaders[0].filesz = 16;
282 fixture.programHeaders[0].memsz = 16;
283 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"load segment past EOF accepted");
285 fixture.programHeaders[1].type = PT_NOTE;
286 fixture.programHeaders[1].offset = Fixture::ImageSize - 8;
287 fixture.programHeaders[1].filesz = 16;
288 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
289 "file-backed non-load segment past EOF accepted");
291 fixture.programHeaders[0].vaddr = ~Elf_Addr{0} - 16;
292 fixture.programHeaders[0].offset =
293 fixture.programHeaders[0].vaddr & TargetInfo::getPageOffsetMask();
294 fixture.programHeaders[0].filesz = 0;
295 fixture.programHeaders[0].memsz = 32;
296 fixture.programHeaders[0].align = 1;
298 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"overflowing load address accepted");
300 fixture.programHeaders[0].vaddr = ~Elf_Addr{0} - TargetInfo::getPageOffsetMask();
301 fixture.programHeaders[0].filesz = 0;
302 fixture.programHeaders[0].memsz = TargetInfo::getPageOffsetMask();
304 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"overflowing page rounding accepted");
306 fixture.programHeaders[0].align = 3;
307 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"non-power-of-two load alignment accepted");
309 fixture.programHeaders[0].offset = 1;
310 fixture.programHeaders[0].filesz = 128;
312 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"misaligned load segment accepted");
314 fixture.programHeaders[0].align = 1;
315 fixture.programHeaders[0].offset = 1;
316 fixture.programHeaders[0].filesz = 128;
318 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"page-incongruent load segment accepted");
320 fixture.header.entry = 0x500000;
321 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"unmapped entry point accepted");
323 fixture.programHeaders[0].flags = PF_R;
324 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"non-executable entry point accepted");
329 EXPECT_RESULT(
fixture.validate(), Result::UnsupportedLayout,
"nonzero-based ET_DYN accepted");
334 fixture.programHeaders[0].align = 1;
335 fixture.programHeaders[1].type = PT_LOAD;
336 fixture.programHeaders[1].flags = PF_R;
341 fixture.programHeaders[1].align = 1;
342 EXPECT_RESULT(
fixture.validate(), Result::UnsupportedLayout,
343 "page-overlapping load segments accepted");
347bool interpreterValidation() {
348 using Fixture = ExecutableValidationFixture;
349 using Result = Elf::ExecutableValidationResult;
352 Fixture::Metadata metadata{};
354 EXPECT_RESULT(
fixture.validate(metadata), Result::Valid,
"valid PT_INTERP rejected");
355 if (!metadata.hasInterpreter || metadata.interpreterOffset != 512 || !metadata.interpreterSize) {
356 return fail(
"valid PT_INTERP metadata was incorrect");
360 fixture.addInterpreter(1, 512, 1);
361 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"one-byte PT_INTERP accepted");
363 fixture.addInterpreter(1, 512, Elf::MaximumInterpreterSize + 1);
364 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"oversized PT_INTERP accepted");
366 fixture.addInterpreter(1, Fixture::ImageSize - 1, 2);
367 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"PT_INTERP past EOF accepted");
370 fixture.addInterpreter(2, 640);
371 EXPECT_RESULT(
fixture.validate(), Result::MultipleInterpreters,
"duplicate PT_INTERP accepted");
374 fixture.terminateInterpreter =
false;
375 EXPECT_RESULT(
fixture.validate(), Result::Malformed,
"unterminated PT_INTERP accepted");
378 EXPECT_RESULT(
fixture.validate(~
size_t{0}, 1), Result::Malformed,
379 "truncated PT_INTERP buffer accepted");
381 fixture.addInterpreter(1, 512, Elf::MaximumInterpreterSize);
382 EXPECT_RESULT(
fixture.validate(), Result::Valid,
"maximum-size PT_INTERP rejected");
389bool runHostedElfValidationRegressions() {
390 if (!validImagesAndMetadata() || !headerValidation() || !loadSegmentValidation() ||
391 !interpreterValidation()) {
395 NOTICE(
"HOSTED-ELF-VALIDATION-TEST: PASS");
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)
bool validate(uint8_t *pBuffer, size_t length)
static constexpr size_t getPageSize() noexcept