blob: 129ebe261626989a22b3a5a47814b76b58e7d901 [file]
// Copyright 2023 The Fuchsia Authors
//
// 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
//! Undefined Behavior commands.
//! These commands can be used to test the undefined behavior sanitizer.
//! A kernel compiled with the `kubsan` variant should be able to detect
//! each of them.
#![cfg(ubsan)]
use boot_options::{BootOptions, CheckFailAction};
use core::ffi::c_int;
use kprint::kprintln;
use zx_status::Status;
use super::debugcommands_ffi::{
cpp_ub_array_oob, cpp_ub_invalid_builtin_clz, cpp_ub_invalid_builtin_ctz,
cpp_ub_misaligned_ptr, cpp_ub_nonnull_return, cpp_ub_nullability_return, cpp_ub_overflow_ptr,
cpp_ub_overflow_signed_int_add, cpp_ub_overflow_signed_int_shift, cpp_ub_unaligned_assumption,
cpp_ub_undefined_bool, cpp_ub_undefined_enum, cpp_ub_unreachable,
};
use crate::console::{CMD_AVAIL_NORMAL, CmdArgs, static_command};
struct UndefinedBehaviorCommand {
name: &'static str,
func: Option<unsafe extern "C" fn()>,
description: &'static str,
cannot_continue: bool,
}
const UB_COMMANDS: [UndefinedBehaviorCommand; 14] = [
UndefinedBehaviorCommand {
name: "all",
func: None,
description: "run each subcommand in turn (requires kernel.ubsan.panic=false)",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "array_oob",
func: Some(cpp_ub_array_oob),
description: "array out of bounds access",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "invalid_builtin_clz",
func: Some(cpp_ub_invalid_builtin_clz),
description: "call __builtin_clz with 0",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "invalid_builtin_ctz",
func: Some(cpp_ub_invalid_builtin_ctz),
description: "call __builtin_ctz with 0",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "misaligned_ptr",
func: Some(cpp_ub_misaligned_ptr),
description: "use a misaligned pointer",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "nonnull_return",
func: Some(cpp_ub_nonnull_return),
description: "return nullptr from returns_nonnull function",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "nullability_return",
func: Some(cpp_ub_nullability_return),
description: "return nullptr from _Nonnull function",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "overflow_ptr",
func: Some(cpp_ub_overflow_ptr),
description: "pointer arithmetic that overflows",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "overflow_signed_int_add",
func: Some(cpp_ub_overflow_signed_int_add),
description: "signed integer addition that overflows",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "overflow_signed_int_shift",
func: Some(cpp_ub_overflow_signed_int_shift),
description: "signed integer shift that overflows",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "unaligned_assumption",
func: Some(cpp_ub_unaligned_assumption),
description: "make a wrong alignment assumption",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "undefined_enum",
func: Some(cpp_ub_undefined_enum),
description: "use an undefined value in a enum",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "undefined_bool",
func: Some(cpp_ub_undefined_bool),
description: "use a bool that is not true nor false",
cannot_continue: false,
},
UndefinedBehaviorCommand {
name: "unreachable",
func: Some(cpp_ub_unreachable),
description: "execute unreachable code.",
cannot_continue: true,
},
];
const MAX_COMMAND_NAME_SIZE: usize = {
let mut size = 0;
let mut i = 0;
while i < UB_COMMANDS.len() {
let len = UB_COMMANDS[i].name.len();
if len > size {
size = len;
}
i += 1;
}
size
};
zr::static_assert!(MAX_COMMAND_NAME_SIZE == 25);
fn cmd_usage(cmd_name: &str) -> c_int {
kprintln!("usage:");
for ub_cmd in &UB_COMMANDS {
kprintln!("{:s} {:<25s} : {:s}", cmd_name, ub_cmd.name, ub_cmd.description);
}
Status::INTERNAL.into_raw()
}
/// # Safety
///
/// `argv` must point to `argc` valid `CmdArgs` elements.
unsafe extern "C" fn cmd_ub(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 name = args[0].as_str();
if argc != 2 {
kprintln!("Exactly one argument required.");
return cmd_usage(name);
}
let subcommand = args[1].as_str();
for ub_cmd in &UB_COMMANDS {
if ub_cmd.name == subcommand {
if let Some(func) = ub_cmd.func {
// SAFETY: Intentionally triggers the requested C++ UBSan test helper.
unsafe { func() };
} else {
for next_ub_cmd in &UB_COMMANDS[1..] {
if BootOptions::get().ubsan_action == CheckFailAction::Oops
&& next_ub_cmd.cannot_continue
{
kprintln!(
"*** Skipping `ub {:s}`, which cannot avoid panic ***",
next_ub_cmd.name
);
} else if let Some(func) = next_ub_cmd.func {
kprintln!("*** ub {:s}", next_ub_cmd.name);
// SAFETY: Intentionally triggers the C++ UBSan test helper.
unsafe { func() };
}
}
}
return 0;
}
}
cmd_usage(name)
}
static_command!(
UB_CMD,
c"ub".as_ptr(),
c"trigger undefined behavior".as_ptr(),
cmd_ub,
CMD_AVAIL_NORMAL
);