| // 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 | |
| ); |