blob: d1a3480a8d8c97f980cefb5cd302e0877741d31c [file]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2012 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
//! Debug console (`k`) commands (`dd`, `dw`, `dh`, `db`, `mw`, `mh`, `mb`, `fw`, `fh`, `fb`, `mc`,
//! `mtest`, `crash`, `sleep`, `sleepm`, `build_instrumentation`, `pop`, `ub`).
mod debugcommands_ffi;
mod undefined_behavior;
use core::ffi::{CStr, c_int, c_void};
use core::ptr::{with_exposed_provenance, with_exposed_provenance_mut};
use core::sync::atomic::{AtomicUsize, Ordering};
use kprint::{kprint, kprintln};
use zx_status::Status;
use crate::console::{CMD_AVAIL_ALWAYS, CMD_AVAIL_NORMAL, CmdArgs, static_command};
use crate::debugcommands::debugcommands_ffi::{
arch_sync_cache_range, cpp_before_stack_overflow, cpp_crash_assert,
cpp_crash_shadow_call_stack_stomp, cpp_crash_stack_guard, cpp_crash_stackstomp,
};
use crate::kernel::thread::{sleep_relative, spawn};
use crate::kernel::types::PAddr;
use crate::platform_rs::timer::{DurationMono, InstantMono};
use crate::top::debug::rust_debug_crash_panic;
use crate::user_memory::UserMemory;
use crate::vm::arch_vm_aspace::{
ARCH_MMU_FLAG_PERM_EXECUTE, ARCH_MMU_FLAG_PERM_READ, ARCH_MMU_FLAG_PERM_USER,
ARCH_MMU_FLAG_PERM_WRITE,
};
use crate::vm::page::VmPageDoublyLinkedList;
use crate::vm::physmap::paddr_to_physmap;
use crate::vm::pmm;
use crate::vm::vm::vaddr_to_paddr;
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements, and the target memory range must be valid
/// to read if mapped.
unsafe extern "C" fn cmd_display_mem(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
// save the last address and len so we can continue where we left off
static LAST_ADDRESS: AtomicUsize = AtomicUsize::new(0);
static LAST_LEN: AtomicUsize = AtomicUsize::new(0);
let mut address = LAST_ADDRESS.load(Ordering::Relaxed);
let mut len = LAST_LEN.load(Ordering::Relaxed);
if argc < 3 && len == 0 {
kprintln!("not enough arguments");
kprintln!("{:s} [-l] [-b] [-p] [address] [length]", args[0].as_str());
return -1;
}
let size: usize = if args[0].as_str() == "dd" {
8
} else if args[0].as_str() == "dw" {
4
} else if args[0].as_str() == "dh" {
2
} else {
1
};
let mut little_endian = cfg!(target_endian = "little");
let mut argindex = 1usize;
let mut read_address = false;
let mut phys_addr = false;
while (argc as usize) > argindex {
if args[argindex].as_str() == "-l" {
little_endian = true;
} else if args[argindex].as_str() == "-b" {
little_endian = false;
} else if args[argindex].as_str() == "-p" {
phys_addr = true;
} else if !read_address {
address = args[argindex].arg_uint as usize;
LAST_ADDRESS.store(address, Ordering::Relaxed);
read_address = true;
} else {
len = args[argindex].arg_uint as usize;
LAST_LEN.store(len, Ordering::Relaxed);
}
argindex += 1;
}
if (address & (size - 1)) != 0 {
kprintln!("unaligned address, cannot display");
return -1;
}
if phys_addr {
// Translate to a physmap address if present.
let vaddr = paddr_to_physmap(PAddr(address)).0;
if vaddr == 0 {
kprintln!("ERROR: failed to translate physical address");
return -1;
}
kprintln!(
"physical address {:#x} translated to {:p}",
address,
with_exposed_provenance::<c_void>(vaddr)
);
address = vaddr;
LAST_ADDRESS.store(address, Ordering::Relaxed);
}
// preflight the start address to see if it's mapped
if vaddr_to_paddr(with_exposed_provenance::<c_void>(address)).0 == 0 {
kprintln!("ERROR: address 0x{:x} is unmapped", address);
return -1;
}
let stop = address.wrapping_add(len);
let mut count = 0usize;
let swap = little_endian != cfg!(target_endian = "little");
while address < stop {
if count == 0 {
kprint!("0x{:08x}: ", address);
}
match size {
8 => {
// SAFETY: `address` is 8-byte aligned and preflighted as mapped kernel memory.
let raw =
unsafe { core::ptr::read_volatile(with_exposed_provenance::<u64>(address)) };
let val = if swap { raw.swap_bytes() } else { raw };
kprint!("{:016x} ", val);
}
4 => {
// SAFETY: `address` is 4-byte aligned and preflighted as mapped kernel memory.
let raw =
unsafe { core::ptr::read_volatile(with_exposed_provenance::<u32>(address)) };
let val = if swap { raw.swap_bytes() } else { raw };
kprint!("{:08x} ", val);
}
2 => {
// SAFETY: `address` is 2-byte aligned and preflighted as mapped kernel memory.
let raw =
unsafe { core::ptr::read_volatile(with_exposed_provenance::<u16>(address)) };
let val = if swap { raw.swap_bytes() } else { raw };
kprint!("{:04x} ", val);
}
1 => {
// SAFETY: `address` is preflighted as mapped kernel memory.
let val =
unsafe { core::ptr::read_volatile(with_exposed_provenance::<u8>(address)) };
kprint!("{:02x} ", val);
}
_ => unreachable!(),
}
count += size;
if count == 16 {
kprintln!("");
count = 0;
}
address = address.wrapping_add(size);
}
LAST_ADDRESS.store(address, Ordering::Relaxed);
if count != 0 {
kprintln!("");
}
0
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements, and the target address must be valid for
/// writing.
unsafe extern "C" fn cmd_modify_mem(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
if argc < 3 {
kprintln!("not enough arguments");
kprintln!("{:s} <address> <val>", args[0].as_str());
return -1;
}
let size: usize = if args[0].as_str() == "mw" {
4
} else if args[0].as_str() == "mh" {
2
} else {
1
};
let address = args[1].arg_uint as usize;
let val = args[2].arg_uint as usize;
if (address & (size - 1)) != 0 {
kprintln!("unaligned address, cannot modify");
return -1;
}
match size {
4 => {
// SAFETY: `address` is 4-byte aligned and specified by the console operator.
unsafe {
core::ptr::write_volatile(with_exposed_provenance_mut::<u32>(address), val as u32);
}
}
2 => {
// SAFETY: `address` is 2-byte aligned and specified by the console operator.
unsafe {
core::ptr::write_volatile(with_exposed_provenance_mut::<u16>(address), val as u16);
}
}
1 => {
// SAFETY: `address` is specified by the console operator.
unsafe {
core::ptr::write_volatile(with_exposed_provenance_mut::<u8>(address), val as u8);
}
}
_ => unreachable!(),
}
0
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements, and the target memory range must be valid
/// for writing.
unsafe extern "C" fn cmd_fill_mem(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
if argc < 4 {
kprintln!("not enough arguments");
kprintln!("{:s} <address> <len> <val>", args[0].as_str());
return -1;
}
let size: usize = if args[0].as_str() == "fw" {
4
} else if args[0].as_str() == "fh" {
2
} else {
1
};
let mut address = args[1].arg_uint as usize;
let len = args[2].arg_uint as usize;
let stop = address.wrapping_add(len);
let val = args[3].arg_uint as usize;
if (address & (size - 1)) != 0 {
kprintln!("unaligned address, cannot modify");
return -1;
}
while address < stop {
match size {
4 => {
// SAFETY: `address` is 4-byte aligned and specified by the console operator.
unsafe {
core::ptr::write_volatile(
with_exposed_provenance_mut::<u32>(address),
val as u32,
);
}
}
2 => {
// SAFETY: `address` is 2-byte aligned and specified by the console operator.
unsafe {
core::ptr::write_volatile(
with_exposed_provenance_mut::<u16>(address),
val as u16,
);
}
}
1 => {
// SAFETY: `address` is specified by the console operator.
unsafe {
core::ptr::write_volatile(
with_exposed_provenance_mut::<u8>(address),
val as u8,
);
}
}
_ => unreachable!(),
}
address = address.wrapping_add(size);
}
0
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements, and the source and target memory ranges
/// must be valid and non-overlapping.
unsafe extern "C" fn cmd_copy_mem(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
if argc < 4 {
kprintln!("not enough arguments");
kprintln!("{:s} <source address> <target address> <len>", args[0].as_str());
return -1;
}
let source = args[1].arg_uint as usize;
let target = args[2].arg_uint as usize;
let len = args[3].arg_uint as usize;
// SAFETY: `source` and `target` are non-overlapping raw memory ranges of `len` bytes
// specified by the console operator.
unsafe {
core::ptr::copy_nonoverlapping(
with_exposed_provenance::<u8>(source),
with_exposed_provenance_mut::<u8>(target),
len,
);
}
0
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements, and the target memory range must be
/// 4-byte aligned and valid for reading and writing.
unsafe extern "C" fn cmd_memtest(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
if argc < 3 {
kprintln!("not enough arguments");
kprintln!("{:s} <base> <len>", args[0].as_str());
return -1;
}
let ptr = with_exposed_provenance_mut::<u32>(args[1].arg_uint as usize);
let len = args[2].arg_uint as usize;
// write out
kprint!("writing first pass...");
for i in 0..(len / 4) {
// SAFETY: `ptr.add(i)` is within the caller-specified `[base, base + len)` range.
unsafe {
core::ptr::write_volatile(ptr.add(i), i as u32);
}
}
kprintln!("done");
// verify
kprint!("verifying...");
for i in 0..(len / 4) {
// SAFETY: `ptr.add(i)` is within the caller-specified `[base, base + len)` range.
let val = unsafe { core::ptr::read_volatile(ptr.add(i)) };
if (val as usize) != i {
// SAFETY: `ptr.add(i)` is within the caller-specified `[base, base + len)` range.
kprintln!("error at {:p}", unsafe { ptr.add(i) });
}
}
kprintln!("done");
0
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements.
unsafe extern "C" fn cmd_sleep(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
let mut t = DurationMono::from_seconds(1); // default to 1 second
if argc >= 2 {
t = DurationMono::from_millis(args[1].arg_uint as i64);
if args[0].as_str() == "sleep" {
t = t * 1000;
}
}
let _ = sleep_relative(t);
0
}
extern "C" fn crash_thread(_arg: *mut c_void) -> i32 {
// should crash
// SAFETY: Intentionally dereferences an invalid pointer (`1`) to crash the kernel.
unsafe {
core::ptr::write_volatile(core::ptr::without_provenance_mut::<u8>(1), 1);
}
0
}
fn crash_deref(thread: bool) -> c_int {
if thread {
// SAFETY: `c"crasher"` is a valid NUL-terminated string, and `crash_thread` does not
// dereference its argument pointer.
let t = unsafe { spawn(c"crasher".as_ptr(), crash_thread, core::ptr::null_mut()) }
.expect("Thread::Create failed");
// SAFETY: `t` was just created and has not been joined yet.
let _ = unsafe { t.join(InstantMono::INFINITE) };
return 0;
}
crash_thread(core::ptr::null_mut());
// if it didn't, panic the system
panic!("crash");
}
/// Crash by intentionally recursing to itself until the kernel
/// call stack is exceeded.
#[allow(unconditional_recursion)]
fn recurse() -> c_int {
// The compiler doesn't know what function pointer this is anymore after
// black_box, but it hasn't changed.
let self_fn: fn() -> c_int = core::hint::black_box(recurse);
self_fn().wrapping_add(1)
}
fn before_stack_overflow(what: &CStr) {
// SAFETY: `what.as_ptr()` is a valid NUL-terminated C string.
unsafe {
cpp_before_stack_overflow(what.as_ptr());
}
}
fn crash_recurse() -> c_int {
before_stack_overflow(c"Recursing");
recurse();
kprintln!("survived.");
0
}
fn crash_stackstomp() -> c_int {
// SAFETY: Intentionally overflows the kernel stack in test-only crash command.
unsafe { cpp_crash_stackstomp() }
}
fn crash_shadow_call_stack_stomp() -> c_int {
// SAFETY: Intentionally overflows the shadow call stack in test-only crash command.
unsafe { cpp_crash_shadow_call_stack_stomp() }
}
fn has_user_code_execution_protection() -> bool {
#[cfg(target_arch = "x86_64")]
{
use crate::arch_rs::feature::{X86_FEATURE_SMEP, x86_feature_has_smap, x86_feature_test};
x86_feature_test(X86_FEATURE_SMEP) || x86_feature_has_smap()
}
#[cfg(target_arch = "aarch64")]
{
// Privilege Execute Never (PXN) is available on all aarch64 machines.
true
}
#[cfg(target_arch = "riscv64")]
{
// Supervisor User Memory access control is always available on RISC-V.
true
}
}
fn crash_user_execute() -> c_int {
if !has_user_code_execution_protection() {
kprintln!(
"missing protection to avoid executing userspace code from a privileged context; will \
not crash."
);
return -1;
}
const USER_MEMORY_SIZE: usize = page::SIZE;
let Some(mem) = UserMemory::create(USER_MEMORY_SIZE) else {
kprintln!("failed to allocate user memory; will not crash.");
return -1;
};
// Define a little fragment of code that we can copy.
let begin_func: *const u8;
let func_size: usize;
// SAFETY: Places a `ret` instruction in `.rodata` and reads its address and byte length, then
// forms a slice over those `func_size` static bytes.
let code_fragment = unsafe {
#[cfg(target_arch = "aarch64")]
core::arch::asm!(
".pushsection .rodata.crash_user_exec, \"a?\", %progbits",
".balign 4",
"2:",
"ret",
"3:",
".popsection",
"adrp {ptr}, 2b",
"add {ptr}, {ptr}, :lo12:2b",
"mov {size}, #3b - 2b",
ptr = out(reg) begin_func,
size = out(reg) func_size,
options(nomem, nostack),
);
#[cfg(target_arch = "riscv64")]
core::arch::asm!(
".pushsection .rodata.crash_user_exec, \"a?\", %progbits",
".balign 4",
"2:",
"ret",
"3:",
".popsection",
"lla {ptr}, 2b",
"li {size}, 3b - 2b",
ptr = out(reg) begin_func,
size = out(reg) func_size,
options(nomem, nostack),
);
#[cfg(target_arch = "x86_64")]
core::arch::asm!(
".pushsection .rodata.crash_user_exec, \"a?\", @progbits",
"2:",
"ret",
"3:",
".popsection",
"lea 2b(%rip), {ptr}",
"mov $(3b - 2b), {size}",
ptr = out(reg) begin_func,
size = out(reg) func_size,
options(att_syntax, nomem, nostack),
);
core::slice::from_raw_parts(begin_func, func_size)
};
assert!(
code_fragment.len() <= USER_MEMORY_SIZE,
"function does not fit in allocated user memory"
);
if let Err(status) = mem.vmo_write(code_fragment, 0) {
kprintln!("failed to copy payload ({}); will not crash.", status.into_raw());
return -1;
}
if let Err(status) = mem.commit_and_map(0..USER_MEMORY_SIZE) {
kprintln!("failed to commit memory ({}); will not crash.", status.into_raw());
return -1;
}
// Set the memory as executable. We need to also make it read-only because
// in arm64, writable user mappings imply Privileged Execute Never (PXN).
if let Err(status) = mem.make_rx() {
kprintln!("failed to make memory executable ({}); will not crash.", status.into_raw());
return -1;
}
let mmu_flags = match mem.aspace().arch_aspace().query(mem.base()) {
Ok((_, flags)) => flags,
Err(status) => {
kprintln!("failed to query mmu flags ({}); will not crash.", status.into_raw());
return -1;
}
};
if (mmu_flags & ARCH_MMU_FLAG_PERM_WRITE) != 0
|| (mmu_flags
& (ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE))
== 0
{
kprintln!("incorrect memory permissions; will not crash.");
return -1;
}
let p = mem.user_in::<u8>().as_ptr();
if p.is_null() {
kprintln!("failed to get pointer; will not crash.");
return -1;
}
// SAFETY: `mem.base()..mem.base() + USER_MEMORY_SIZE` is a valid mapped user page.
unsafe {
arch_sync_cache_range(mem.base(), USER_MEMORY_SIZE);
}
kprintln!("about to crash..");
// SAFETY: `p` was checked to be non-null at line 583; intentionally jumps to a userspace
// address from kernel context to trigger a SMEP/PXN/SUM execution fault.
unsafe {
let user_func = core::mem::transmute::<*const u8, unsafe extern "C" fn()>(p);
user_func();
}
kprintln!("executed userspace code from a kernel context; did not crash.");
0
}
fn crash_user_read() -> c_int {
#[cfg(target_arch = "aarch64")]
{
// TODO(https://fxbug.dev/42137335): Once we support PAN enable this for arm64.
kprintln!("ARM64 currently does not support PAN; will not crash.");
-1
}
#[cfg(not(target_arch = "aarch64"))]
{
#[cfg(target_arch = "x86_64")]
if !crate::arch_rs::feature::x86_feature_has_smap() {
kprintln!("cpu does not support smap; will not crash.");
return -1;
}
// RISCV implements the sstatus.sum bit, similar to x86 SMAP
let Some(mem) = UserMemory::create(page::SIZE) else {
kprintln!("failed to allocate user memory; will not crash.");
return -1;
};
let p = mem.user_in::<u8>().as_ptr();
if p.is_null() {
kprintln!("failed to get pointer; will not crash.");
return -1;
}
kprintln!("about to crash..");
// SAFETY: Intentionally reads a userspace address directly from kernel context to trigger a
// SMAP/PAN/SUM read fault.
let b = unsafe { crate::debugcommands::debugcommands_ffi::cpp_read_byte(p) };
kprintln!("read {:02x}; did not crash.", b);
-1
}
}
fn crash_pmm_use_after_free() -> c_int {
// We want to corrupt one of the pages on the pmm's free list. To do so, we'll allocate a bunch
// of pages, keep track of the address of the last page, then free them all. The free list is
// LIFO so by allocating and freeing a bunch of pages we'll have a pointer "to the middle" and
// our corrupted page will be less like to be immediately allocated.
// Allocate.
const NUM_PAGES: usize = 10000;
pin_init::stack_pin_init!(let pages = VmPageDoublyLinkedList::new());
if let Err(status) = pmm::alloc_pages(NUM_PAGES, 0, pages.as_mut()) {
kprintln!("error: failed to allocate ({})", status.into_raw());
return -1;
}
// Make note of address.
let last_paddr = pages.back().unwrap().paddr();
let va = paddr_to_physmap(last_paddr).0;
// We're printing a little early because once we've returned the pages to the free list, we
// want to avoid doing anything that might cause the target page to be allocated (by this
// thread or some other thread).
kprintln!("corrupting memory at address {:p}", with_exposed_provenance::<c_void>(va));
// Free.
// SAFETY: `pages` contains valid allocated PMM pages.
unsafe {
pmm::free_list(pages.as_mut());
}
// Corrupt!
// SAFETY: Intentionally writes to a freed page in the physmap to test the PMM checker.
unsafe {
core::ptr::write_volatile(with_exposed_provenance_mut::<u8>(va), b'X');
}
kprintln!("crash_pmm_use_after_free done");
-1
}
fn crash_assert() -> c_int {
// SAFETY: Intentionally triggers a C++ `ASSERT_MSG` failure.
unsafe { cpp_crash_assert() }
}
fn crash_stack_guard() -> c_int {
// SAFETY: Intentionally overwrites the stack guard to trigger a stack-protector crash.
unsafe { cpp_crash_stack_guard() }
}
fn crash_illegal_instruction() -> c_int {
kprintln!("attempting to crash with an illegal instruction");
// SAFETY: Intentionally executes an illegal instruction to crash the kernel.
unsafe {
#[cfg(target_arch = "riscv64")]
core::arch::asm!("unimp");
#[cfg(target_arch = "x86_64")]
core::arch::asm!("ud2");
#[cfg(target_arch = "aarch64")]
core::arch::asm!("udf #0");
}
#[cfg(not(any(target_arch = "riscv64", target_arch = "x86_64", target_arch = "aarch64")))]
kprintln!("not implemented for this architecture");
0
}
fn crash_break_instruction() -> c_int {
kprintln!("attempting to crash with a break instruction");
// SAFETY: Intentionally executes a break instruction to crash the kernel.
unsafe {
#[cfg(target_arch = "riscv64")]
core::arch::asm!("ebreak");
#[cfg(target_arch = "x86_64")]
core::arch::asm!("int3");
#[cfg(target_arch = "aarch64")]
core::arch::asm!("brk #0");
}
#[cfg(not(any(target_arch = "riscv64", target_arch = "x86_64", target_arch = "aarch64")))]
kprintln!("not implemented for this architecture");
0
}
fn crash_syscall_instruction() -> c_int {
kprintln!("attempting to crash with a syscall instruction");
#[cfg(target_arch = "riscv64")]
{
// Actually not a good idea, this will probably call into firmware with undefined args.
kprintln!(
"not implemented for RISC-V, ecall instruction would probably be trapped by firmware"
);
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
// SAFETY: Intentionally executes a syscall/svc instruction in kernel mode to crash the kernel.
unsafe {
#[cfg(target_arch = "x86_64")]
core::arch::asm!("syscall");
#[cfg(target_arch = "aarch64")]
core::arch::asm!("svc #0");
}
#[cfg(not(any(target_arch = "riscv64", target_arch = "x86_64", target_arch = "aarch64")))]
kprintln!("not implemented for this architecture");
0
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements.
unsafe extern "C" fn cmd_crash(argc: c_int, argv: *const CmdArgs, _flags: u32) -> c_int {
// SAFETY: The console dispatcher guarantees `argv` points to `argc` valid `CmdArgs` elements.
let args = unsafe { core::slice::from_raw_parts(argv, argc as usize) };
let cmd_name = args[0].as_str();
let usage = || -> c_int {
kprintln!("usage:");
kprintln!(
"{:s} deref [thread] : intentionally dereference an invalid pointer",
cmd_name
);
kprintln!("{:s} stackstomp : intentionally overrun the stack", cmd_name);
kprintln!(
"{:s} recurse : intentionally overrun the stack by recursing",
cmd_name
);
kprintln!("{:s} user_read : intentionally read user memory", cmd_name);
kprintln!("{:s} user_execute : intentionally execute user memory", cmd_name);
kprintln!("{:s} pmm_use_after_free : intentionally corrupt the pmm free list", cmd_name);
kprintln!("{:s} assert : intentionally crash by failing an assert", cmd_name);
kprintln!("{:s} rust_panic : intentionally crash by panicking in Rust", cmd_name);
kprintln!(
"{:s} stack_guard : attempt to crash by overwriting the stack guard",
cmd_name
);
kprintln!(
"{:s} illegal_instruction : attempt to crash by running an illegal instruction",
cmd_name
);
kprintln!(
"{:s} break_instruction : attempt to crash by running a break instruction",
cmd_name
);
kprintln!(
"{:s} syscall_instruction : attempt to crash by running a syscall instruction",
cmd_name
);
Status::INTERNAL.into_raw()
};
if argc < 2 {
kprintln!("not enough arguments");
return usage();
}
match args[1].as_str() {
"deref" => {
if argc > 2 && args[2].as_str() == "thread" {
crash_deref(true)
} else {
crash_deref(false)
}
}
"stackstomp" => crash_stackstomp(),
"shadow_call_stack_stomp" => crash_shadow_call_stack_stomp(),
"recurse" => crash_recurse(),
"user_read" => crash_user_read(),
"user_execute" => crash_user_execute(),
"pmm_use_after_free" => crash_pmm_use_after_free(),
"assert" => crash_assert(),
"rust_panic" => {
rust_debug_crash_panic();
}
"stack_guard" => crash_stack_guard(),
"illegal_instruction" => crash_illegal_instruction(),
"break_instruction" => crash_break_instruction(),
"syscall_instruction" => crash_syscall_instruction(),
_ => {
kprintln!("unknown command");
usage()
}
}
}
/// # Safety
///
/// `_argv` must be a valid pointer to `_argc` elements if dereferenced.
unsafe extern "C" fn cmd_build_instrumentation(
_argc: c_int,
_argv: *const CmdArgs,
_flags: u32,
) -> c_int {
let print_feature = |what: &str| {
kprintln!("build_instrumentation: {:s}", what);
};
if cfg!(sanitize = "address") {
print_feature("address_sanitizer");
}
if cfg!(lk_debuglevel = "2") {
print_feature("debug_assert");
}
if cfg!(with_lock_dep) {
print_feature("lockdep");
}
if cfg!(sanitize = "safestack") {
print_feature("safe_stack");
}
if cfg!(sanitize = "shadow-call-stack") {
print_feature("shadow_call_stack");
}
if cfg!(ubsan) {
print_feature("undefined_behavior_sanitizer");
}
// missing: sancov, profile
// This print statement is load-bearing! It is scraped in a panic test.
// LINT.IfChange
kprintln!("build_instrumentation: done");
// LINT.ThenChange(//src/tests/kernel_panic/kernel_panic_test.go)
0
}
/// # Safety
///
/// `_argv` must be a valid pointer to `argc` elements if dereferenced.
unsafe extern "C" fn cmd_pop(argc: c_int, _argv: *const CmdArgs, _flags: u32) -> c_int {
if argc > 1 {
kprintln!("aren't there already enough arguments?");
return -1;
}
kprint!("drama!");
0
}
static_command!(
DD_CMD,
c"dd".as_ptr(),
c"display memory in dwords".as_ptr(),
cmd_display_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
DW_CMD,
c"dw".as_ptr(),
c"display memory in words".as_ptr(),
cmd_display_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
DH_CMD,
c"dh".as_ptr(),
c"display memory in halfwords".as_ptr(),
cmd_display_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
DB_CMD,
c"db".as_ptr(),
c"display memory in bytes".as_ptr(),
cmd_display_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
MW_CMD,
c"mw".as_ptr(),
c"modify word of memory".as_ptr(),
cmd_modify_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
MH_CMD,
c"mh".as_ptr(),
c"modify halfword of memory".as_ptr(),
cmd_modify_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
MB_CMD,
c"mb".as_ptr(),
c"modify byte of memory".as_ptr(),
cmd_modify_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
FW_CMD,
c"fw".as_ptr(),
c"fill range of memory by word".as_ptr(),
cmd_fill_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
FH_CMD,
c"fh".as_ptr(),
c"fill range of memory by halfword".as_ptr(),
cmd_fill_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
FB_CMD,
c"fb".as_ptr(),
c"fill range of memory by byte".as_ptr(),
cmd_fill_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
MC_CMD,
c"mc".as_ptr(),
c"copy a range of memory".as_ptr(),
cmd_copy_mem,
CMD_AVAIL_ALWAYS
);
static_command!(
MTEST_CMD,
c"mtest".as_ptr(),
c"simple memory test".as_ptr(),
cmd_memtest,
CMD_AVAIL_NORMAL
);
static_command!(
CRASH_CMD,
c"crash".as_ptr(),
c"intentionally crash".as_ptr(),
cmd_crash,
CMD_AVAIL_NORMAL
);
static_command!(
SLEEP_CMD,
c"sleep".as_ptr(),
c"sleep number of seconds".as_ptr(),
cmd_sleep,
CMD_AVAIL_NORMAL
);
static_command!(
SLEEPM_CMD,
c"sleepm".as_ptr(),
c"sleep number of milliseconds".as_ptr(),
cmd_sleep,
CMD_AVAIL_NORMAL
);
static_command!(
BUILD_INSTRUMENTATION_CMD,
c"build_instrumentation".as_ptr(),
c"display a non-exhaustive list of build instrumentations used to build this kernel image"
.as_ptr(),
cmd_build_instrumentation,
CMD_AVAIL_NORMAL
);
static_command!(POP_CMD, c"pop".as_ptr(), c"start drama".as_ptr(), cmd_pop, CMD_AVAIL_NORMAL);