Skip to content

Commit

Permalink
crash: Make everything more architecture-independent
Browse files Browse the repository at this point in the history
- Document exactly which crash features are available on which
  architectures. There's several interesting crash tests we will want to
  add for specific architectures, such as misaligned memory accesses.
- Don't compile the divide by zero crash test on RISC-V, as they simply
  don't crash here.
- Rename the test for "x86 User Mode Instruction Prevention" to a more
  generic "try to execute a priviledged instruction"; whatever that
  specifically entails on any given architecture.
- Rename a bunch of internal variables to more generic names.
- Wrap the x86-specific implementation of "execute non executable
  memory" in architecture preprocessor checks.
  • Loading branch information
kleinesfilmroellchen authored and nico committed Jun 29, 2024
1 parent 311af9a commit e11f84f
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 17 deletions.
21 changes: 17 additions & 4 deletions Base/usr/share/man/man1/crash.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ $ /usr/Tests/Kernel/crash [options]
This program is used to test how the Serenity kernel handles userspace crashes,
and can be used to simulate many different kinds of crashes.

Some crash tests are only available on certain architectures.

Some crash tests are excluded from the `-A` test, since depending on the hardware or implementation they may or may not crash.
- Priviledged instructions in user mode are permitted by QEMU on some architectures such as x86. Therefore, this crash may not fail. See [discussion on pull request 10042](https://github.com/SerenityOS/serenity/pull/10042#issuecomment-920408568).

## Options

* `-A`: Test that all of the following crash types crash as expected.
All architectures:

* `-A`: Test that all of the crash types implemented on this architecture crash as expected.
* `-s`: Perform a segmentation violation by dereferencing an invalid pointer.
* `-d`: Perform a division by zero.
* `-i`: Execute an illegal CPU instruction.
* `-a`: Call `abort()`.
* `-m`: Read a pointer from uninitialized malloc memory, then read from it.
Expand All @@ -30,12 +36,19 @@ and can be used to simulate many different kinds of crashes.
* `-S`: Make a syscall from writeable memory.
* `-y`: Make a syscall from legitimate memory (but outside syscall-code mapped region).
* `-X`: Attempt to execute non-executable memory (Not mapped with PROT\_EXEC).
* `-U`: Attempt to trigger an x86 User Mode Instruction Prevention fault.
* `-I`: Use an x86 I/O instruction in userspace.
* `-U`: Attempt to use a priviledged (kernel mode or higher) instruction in user mode.
* `-p`: Violate `pledge()`'d promises.
* `-n`: Perform a failing assertion.
* `-R`: Dereference a null RefPtr.

x86_64 only:

* `-I`: Use an x86 I/O instruction in userspace.

AArch64 and x86_64 only:

* `-d`: Perform a division by zero.

## Examples

```sh
Expand Down
43 changes: 30 additions & 13 deletions Tests/Kernel/crash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ int main(int argc, char** argv)

bool do_all_crash_types = false;
bool do_segmentation_violation = false;
// RISC-V does not trap divisions by zero, see M extension version 2.0, subsection 2 and table 1.
#if !ARCH(RISCV64)
bool do_division_by_zero = false;
#endif
bool do_illegal_instruction = false;
bool do_abort = false;
bool do_write_to_uninitialized_malloc_memory = false;
Expand All @@ -51,7 +54,7 @@ int main(int argc, char** argv)
bool do_syscall_from_writeable_memory = false;
bool do_legitimate_syscall = false;
bool do_execute_non_executable_memory = false;
bool do_trigger_user_mode_instruction_prevention = false;
bool do_use_priviledged_instruction = false;
#if ARCH(X86_64)
bool do_use_io_instruction = false;
#endif
Expand All @@ -65,7 +68,9 @@ int main(int argc, char** argv)
"(i.e., Kernel or UE) by crashing in many different ways.");
args_parser.add_option(do_all_crash_types, "Test that all (except -U) of the following crash types crash as expected (default behavior)", nullptr, 'A');
args_parser.add_option(do_segmentation_violation, "Perform a segmentation violation by dereferencing an invalid pointer", nullptr, 's');
#if !ARCH(RISCV64)
args_parser.add_option(do_division_by_zero, "Perform a division by zero", nullptr, 'd');
#endif
args_parser.add_option(do_illegal_instruction, "Execute an illegal CPU instruction", nullptr, 'i');
args_parser.add_option(do_abort, "Call `abort()`", nullptr, 'a');
args_parser.add_option(do_read_from_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then read from it", nullptr, 'm');
Expand All @@ -78,7 +83,7 @@ int main(int argc, char** argv)
args_parser.add_option(do_syscall_from_writeable_memory, "Make a syscall from writeable memory", nullptr, 'S');
args_parser.add_option(do_legitimate_syscall, "Make a syscall from legitimate memory (but outside syscall-code mapped region)", nullptr, 'y');
args_parser.add_option(do_execute_non_executable_memory, "Attempt to execute non-executable memory (not mapped with PROT_EXEC)", nullptr, 'X');
args_parser.add_option(do_trigger_user_mode_instruction_prevention, "Attempt to trigger an x86 User Mode Instruction Prevention fault. WARNING: This test runs only when invoked manually, see #10042.", nullptr, 'U');
args_parser.add_option(do_use_priviledged_instruction, "Attempt to use a priviledged instruction in user mode. WARNING: This test runs only when invoked manually, see #10042.", nullptr, 'U');
#if ARCH(X86_64)
args_parser.add_option(do_use_io_instruction, "Use an x86 I/O instruction in userspace", nullptr, 'I');
#endif
Expand Down Expand Up @@ -107,6 +112,7 @@ int main(int argc, char** argv)
}).run(run_type);
}

#if !ARCH(RISCV64)
if (do_division_by_zero || do_all_crash_types) {
any_failures |= !Crash("Division by zero", []() {
int volatile lala = 10;
Expand All @@ -115,6 +121,7 @@ int main(int argc, char** argv)
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
#endif

if (do_illegal_instruction || do_all_crash_types) {
any_failures |= !Crash("Illegal instruction", []() {
Expand Down Expand Up @@ -204,11 +211,11 @@ int main(int argc, char** argv)
if (!makeshift_stack)
return Crash::Failure::UnexpectedError;

u8* makeshift_esp = makeshift_stack + 2048;
u8* makeshift_stack_pointer = makeshift_stack + 2048;
#if ARCH(X86_64)
asm volatile("mov %%eax, %%esp" ::"a"(makeshift_esp));
asm volatile("mov %%eax, %%esp" ::"a"(makeshift_stack_pointer));
#elif ARCH(AARCH64)
(void)makeshift_esp;
(void)makeshift_stack_pointer;
TODO_AARCH64();
#elif ARCH(RISCV64)
(void)makeshift_esp;
Expand All @@ -223,11 +230,11 @@ int main(int argc, char** argv)
if (!bad_stack)
return Crash::Failure::UnexpectedError;

u8* bad_esp = bad_stack + 2048;
u8* bad_stack_pointer = bad_stack + 2048;
#if ARCH(X86_64)
asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
asm volatile("mov %%eax, %%esp" ::"a"(bad_stack_pointer));
#elif ARCH(AARCH64)
(void)bad_esp;
(void)bad_stack_pointer;
TODO_AARCH64();
#elif ARCH(RISCV64)
(void)bad_esp;
Expand All @@ -246,12 +253,12 @@ int main(int argc, char** argv)
if (!bad_stack)
return Crash::Failure::UnexpectedError;

u8* bad_esp = bad_stack + 2048;
u8* bad_stack_pointer = bad_stack + 2048;
#if ARCH(X86_64)
asm volatile("movq %%rax, %%rsp" ::"a"(bad_esp));
asm volatile("movq %%rax, %%rsp" ::"a"(bad_stack_pointer));
asm volatile("pushq $0");
#elif ARCH(AARCH64)
(void)bad_esp;
(void)bad_stack_pointer;
TODO_AARCH64();
#elif ARCH(RISCV64)
(void)bad_esp;
Expand Down Expand Up @@ -286,15 +293,25 @@ int main(int argc, char** argv)
if (ptr == MAP_FAILED)
return Crash::Failure::UnexpectedError;

#if ARCH(X86_64)
ptr[0] = 0xc3; // ret
#elif ARCH(AARCH64)
(void)ptr;
TODO_AARCH64();
#elif ARCH(RISCV64)
(void)ptr;
TODO_RISCV64();
#else
# error Unknown architecture
#endif
typedef void* (*CrashyFunctionPtr)();
((CrashyFunctionPtr)ptr)();
return Crash::Failure::DidNotCrash;
}).run(run_type);
}

if (do_trigger_user_mode_instruction_prevention) {
any_failures |= !Crash("Trigger x86 User Mode Instruction Prevention", []() {
if (do_use_priviledged_instruction) {
any_failures |= !Crash("Use a priviledged instruction in user mode", []() {
#if ARCH(X86_64)
asm volatile("str %eax");
#elif ARCH(AARCH64)
Expand Down

0 comments on commit e11f84f

Please sign in to comment.