From 2a64921a73ae7a755f45e56c16c6e7b5f77ee25f Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 20 Mar 2025 14:27:18 -0400 Subject: [PATCH 01/24] Fixed UI tests, ensuring that our instrumentation is actually inserted and executed. --- src/bootstrap/defaults/config.bsan.dev.toml | 1 + src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 25 +++++++------------ .../bsan/bsan-driver/cargo-bsan/Cargo.toml | 1 + .../bsan/bsan-driver/cargo-bsan/src/phases.rs | 4 +-- .../bsan/bsan-driver/cargo-bsan/src/setup.rs | 1 + .../bsan/bsan-driver/cargo-bsan/src/util.rs | 3 ++- src/tools/bsan/bsan-driver/src/lib.rs | 12 +++++---- 8 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/bootstrap/defaults/config.bsan.dev.toml b/src/bootstrap/defaults/config.bsan.dev.toml index b5832846a7be7..9f5441116fa76 100644 --- a/src/bootstrap/defaults/config.bsan.dev.toml +++ b/src/bootstrap/defaults/config.bsan.dev.toml @@ -4,6 +4,7 @@ change-id = 132494 build-stage = 1 test-stage = 1 docs = false +incremental = true extended = true tools = [ "bsan-rt", diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 994dfb975009e..1d894798b33f3 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2375,7 +2375,7 @@ impl Step for RustDev { fn run(self, builder: &Builder<'_>) -> Option { let target = self.target; - /* run only if llvm-config isn't used */ + /* run only if isn't used */ if let Some(config) = builder.config.target_config.get(&target) { if let Some(ref _s) = config.llvm_config { builder.info(&format!("Skipping RustDev ({target}): external LLVM")); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 622b283ef7ddd..d315d2114354d 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -476,6 +476,9 @@ impl BsanDriver { target: TargetSelection, bsan_runtime_dir: &PathBuf, ) -> PathBuf { + // We need the BSAN runtime to be able to build our instrumented sysroot. + builder.ensure(BsanRT { compiler, target }); + let bsan_sysroot = builder.out.join(compiler.host).join("bsan-sysroot"); let mut cargo = builder::Cargo::new( builder, @@ -548,7 +551,7 @@ impl Step for BsanDriver { // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage // compilers, which isn't what we want. Rustdoc should be linked in the same way as the // rustc compiler it's paired with, so it must be built with the previous stage compiler. - let host_compiler = builder.compiler(stage - 1, host); + let host_compiler = builder.compiler(stage-1, host); // We need the BSAN runtime to be available so that we can // build our instrumented sysroot. @@ -570,13 +573,7 @@ impl Step for BsanDriver { // We also need sysroots, for BSAN and for the host (the latter for build scripts). // This is for the tests so everything is done with the target compiler. - let bsan_sysroot = BsanDriver::build_bsan_sysroot( - builder, - target_compiler, - target, - &builder.sysroot(host_compiler), - ); - + let sysroot = BsanDriver::build_bsan_sysroot(builder, target_compiler, target); builder.ensure(compile::Std::new(target_compiler, host)); let host_sysroot = builder.sysroot(target_compiler); @@ -584,17 +581,17 @@ impl Step for BsanDriver { // the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors. if !builder.config.dry_run() { let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("bsan_ui"); - // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see + // The mtime of `bsan_sysroot` changes when the sysroot gets rebuilt (also see // ). // We can hence use that directly as a signal to clear the ui test dir. - builder.clear_if_dirty(&ui_test_dep_dir, &bsan_sysroot); + builder.clear_if_dirty(&ui_test_dep_dir, &sysroot); } // Run `cargo test`. // This is with the bsan-driver crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( builder, - host_compiler, + target_compiler, Mode::ToolRustc, host, Kind::Test, @@ -610,8 +607,7 @@ impl Step for BsanDriver { let mut cargo = prepare_cargo_test(cargo, &[], &[], "bsan", host_compiler, host, builder); // bsan tests need to know about the stage sysroot - - cargo.env("BSAN_SYSROOT", &bsan_sysroot); + cargo.env("BSAN_SYSROOT", &sysroot); cargo.env("BSAN_HOST_SYSROOT", &host_sysroot); // Since our runtime is build with the Stage N-1 compiler, @@ -623,9 +619,6 @@ impl Step for BsanDriver { cargo.env("BSAN_RT_SYSROOT", &builder.sysroot(host_compiler)); cargo.env("BSAN", &bsan_driver); - - // Set the target. - cargo.env("BSAN_TEST_TARGET", target.rustc_target_arg()); { let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "bsan", host, target); let _time = helpers::timeit(builder); diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml b/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml index 410bc569aa384..d84856e641b8e 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml +++ b/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml @@ -11,6 +11,7 @@ log = "0.4.22" rustc-build-sysroot = "0.5.4" rustc_version = "0.4.1" rustc_tools_util = "0.4.0" +regex = "1.5.5" [build-dependencies] rustc_tools_util = "0.4.0" diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs b/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs index 8f357967b4664..ff9764fae1a08 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs +++ b/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs @@ -113,8 +113,8 @@ pub fn phase_cargo_bsan(mut args: impl Iterator) { cmd.env("BSAN_VERBOSE", verbose.to_string()); // This makes the other phases verbose. } - if env::var_os("BSAN_RT_SYSROOT").is_none() { - cmd.env("BSAN_RT_SYSROOT", get_host_sysroot_dir(verbose)); + if env::var_os("BSAN_HOST_SYSROOT").is_none() { + cmd.env("BSAN_HOST_SYSROOT", get_host_sysroot_dir(verbose)); } // Run cargo. diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs b/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs index 529f4601fc372..1151204306f24 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs +++ b/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs @@ -197,3 +197,4 @@ pub fn setup( sysroot_dir } + diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs b/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs index 9f1e3cd7434fe..0fdae3545dcde 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs +++ b/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs @@ -1,12 +1,13 @@ use std::env; use std::ffi::OsString; -use std::fs::File; +use std::fs::{File}; use std::io::{self, Write}; use std::ops::Not; use std::path::{Path, PathBuf}; use std::process::Command; use cargo_metadata::{Metadata, MetadataCommand}; +//use regex::Regex; use crate::arg::*; diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index 54322fc6a61a6..a706a9a9a9820 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -3,6 +3,7 @@ extern crate rustc_driver; +use std::path::PathBuf; use std::env; use std::sync::Arc; @@ -22,12 +23,13 @@ pub fn run_compiler( using_internal_features: Arc, ) -> ! { if target_crate { - let mut additional_args = - BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); - if let Some(runtime) = env::var_os("BSAN_RT_SYSROOT") { - let rt = runtime.to_string_lossy(); - additional_args.push(format!("-L{}/lib", rt)); + let mut additional_args = Vec::::new(); + if let Some(runtime) = env::var_os("BSAN_HOST_SYSROOT") { + let mut path = PathBuf::from(runtime.to_str().expect("non-UTF-8 component in path")); + path.push("lib"); + additional_args.push(format!("-L{}", path.display())); } + additional_args.extend(BSAN_DEFAULT_ARGS.iter().map(ToString::to_string)); args.splice(1..1, additional_args); } let exit_code = rustc_driver::catch_with_exit_code(move || { From 2924079fab43d3d0eadf82553744181b4381a607 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 20 Mar 2025 14:53:18 -0400 Subject: [PATCH 02/24] fmt --- src/bootstrap/defaults/config.bsan.dev.toml | 1 - src/bootstrap/src/core/build_steps/dist.rs | 2 +- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml | 1 - src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs | 1 - src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs | 3 +-- src/tools/bsan/bsan-driver/src/lib.rs | 2 +- 7 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/bootstrap/defaults/config.bsan.dev.toml b/src/bootstrap/defaults/config.bsan.dev.toml index 9f5441116fa76..b5832846a7be7 100644 --- a/src/bootstrap/defaults/config.bsan.dev.toml +++ b/src/bootstrap/defaults/config.bsan.dev.toml @@ -4,7 +4,6 @@ change-id = 132494 build-stage = 1 test-stage = 1 docs = false -incremental = true extended = true tools = [ "bsan-rt", diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 1d894798b33f3..994dfb975009e 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2375,7 +2375,7 @@ impl Step for RustDev { fn run(self, builder: &Builder<'_>) -> Option { let target = self.target; - /* run only if isn't used */ + /* run only if llvm-config isn't used */ if let Some(config) = builder.config.target_config.get(&target) { if let Some(ref _s) = config.llvm_config { builder.info(&format!("Skipping RustDev ({target}): external LLVM")); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index d315d2114354d..aa5f27fafa03f 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -551,7 +551,7 @@ impl Step for BsanDriver { // we'd have stageN/bin/rustc and stageN/bin/rustdoc be effectively different stage // compilers, which isn't what we want. Rustdoc should be linked in the same way as the // rustc compiler it's paired with, so it must be built with the previous stage compiler. - let host_compiler = builder.compiler(stage-1, host); + let host_compiler = builder.compiler(stage - 1, host); // We need the BSAN runtime to be available so that we can // build our instrumented sysroot. diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml b/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml index d84856e641b8e..410bc569aa384 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml +++ b/src/tools/bsan/bsan-driver/cargo-bsan/Cargo.toml @@ -11,7 +11,6 @@ log = "0.4.22" rustc-build-sysroot = "0.5.4" rustc_version = "0.4.1" rustc_tools_util = "0.4.0" -regex = "1.5.5" [build-dependencies] rustc_tools_util = "0.4.0" diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs b/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs index 1151204306f24..529f4601fc372 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs +++ b/src/tools/bsan/bsan-driver/cargo-bsan/src/setup.rs @@ -197,4 +197,3 @@ pub fn setup( sysroot_dir } - diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs b/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs index 0fdae3545dcde..9f1e3cd7434fe 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs +++ b/src/tools/bsan/bsan-driver/cargo-bsan/src/util.rs @@ -1,13 +1,12 @@ use std::env; use std::ffi::OsString; -use std::fs::{File}; +use std::fs::File; use std::io::{self, Write}; use std::ops::Not; use std::path::{Path, PathBuf}; use std::process::Command; use cargo_metadata::{Metadata, MetadataCommand}; -//use regex::Regex; use crate::arg::*; diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index a706a9a9a9820..067e3647aa3a7 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -3,8 +3,8 @@ extern crate rustc_driver; -use std::path::PathBuf; use std::env; +use std::path::PathBuf; use std::sync::Arc; pub const BSAN_BUG_REPORT_URL: &str = "https://github.com/BorrowSanitizer/rust/issues/new"; From 0c6f8aa7bffae86293b25b71da3c10f305f212a2 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 20 Mar 2025 14:56:37 -0400 Subject: [PATCH 03/24] Cleaned-up arg insertion. --- src/tools/bsan/bsan-driver/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index 067e3647aa3a7..fb64adbe8bede 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -23,13 +23,10 @@ pub fn run_compiler( using_internal_features: Arc, ) -> ! { if target_crate { - let mut additional_args = Vec::::new(); + let mut additional_args = BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); if let Some(runtime) = env::var_os("BSAN_HOST_SYSROOT") { - let mut path = PathBuf::from(runtime.to_str().expect("non-UTF-8 component in path")); - path.push("lib"); - additional_args.push(format!("-L{}", path.display())); + additional_args.push(format!("-L{}/lib", runtime.to_string_lossy())); } - additional_args.extend(BSAN_DEFAULT_ARGS.iter().map(ToString::to_string)); args.splice(1..1, additional_args); } let exit_code = rustc_driver::catch_with_exit_code(move || { From b4eed9b2050a8fb8feb82adddacc2356856c3008 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 20 Mar 2025 14:57:01 -0400 Subject: [PATCH 04/24] fmt --- src/tools/bsan/bsan-driver/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index fb64adbe8bede..d61c10c212332 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -23,7 +23,8 @@ pub fn run_compiler( using_internal_features: Arc, ) -> ! { if target_crate { - let mut additional_args = BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); + let mut additional_args = + BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); if let Some(runtime) = env::var_os("BSAN_HOST_SYSROOT") { additional_args.push(format!("-L{}/lib", runtime.to_string_lossy())); } From 478115fe168f32e8793fb2ee571b3f5f2f08b28f Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 20 Mar 2025 17:14:20 -0400 Subject: [PATCH 05/24] Removed unused dependency. --- src/tools/bsan/bsan-driver/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index d61c10c212332..e368bc450ef24 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -4,7 +4,6 @@ extern crate rustc_driver; use std::env; -use std::path::PathBuf; use std::sync::Arc; pub const BSAN_BUG_REPORT_URL: &str = "https://github.com/BorrowSanitizer/rust/issues/new"; From 335991ffa5dc57d4e1cd08cc7c7259698d55aae9 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Sun, 23 Mar 2025 17:58:28 +0000 Subject: [PATCH 06/24] Set minimum stage to 2. --- src/bootstrap/src/core/build_steps/test.rs | 16 ++++++++-------- src/tools/bsan/bsan-driver/src/lib.rs | 7 +------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index aa5f27fafa03f..c1a7d48d2b887 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -476,9 +476,6 @@ impl BsanDriver { target: TargetSelection, bsan_runtime_dir: &PathBuf, ) -> PathBuf { - // We need the BSAN runtime to be able to build our instrumented sysroot. - builder.ensure(BsanRT { compiler, target }); - let bsan_sysroot = builder.out.join(compiler.host).join("bsan-sysroot"); let mut cargo = builder::Cargo::new( builder, @@ -573,7 +570,7 @@ impl Step for BsanDriver { // We also need sysroots, for BSAN and for the host (the latter for build scripts). // This is for the tests so everything is done with the target compiler. - let sysroot = BsanDriver::build_bsan_sysroot(builder, target_compiler, target); + let bsan_sysroot = BsanDriver::build_bsan_sysroot(builder, target_compiler, target); builder.ensure(compile::Std::new(target_compiler, host)); let host_sysroot = builder.sysroot(target_compiler); @@ -581,17 +578,17 @@ impl Step for BsanDriver { // the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors. if !builder.config.dry_run() { let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("bsan_ui"); - // The mtime of `bsan_sysroot` changes when the sysroot gets rebuilt (also see + // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see // ). // We can hence use that directly as a signal to clear the ui test dir. - builder.clear_if_dirty(&ui_test_dep_dir, &sysroot); + builder.clear_if_dirty(&ui_test_dep_dir, &bsan_sysroot); } // Run `cargo test`. // This is with the bsan-driver crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( builder, - target_compiler, + host_compiler, Mode::ToolRustc, host, Kind::Test, @@ -607,7 +604,7 @@ impl Step for BsanDriver { let mut cargo = prepare_cargo_test(cargo, &[], &[], "bsan", host_compiler, host, builder); // bsan tests need to know about the stage sysroot - cargo.env("BSAN_SYSROOT", &sysroot); + cargo.env("BSAN_SYSROOT", &bsan_sysroot); cargo.env("BSAN_HOST_SYSROOT", &host_sysroot); // Since our runtime is build with the Stage N-1 compiler, @@ -619,6 +616,9 @@ impl Step for BsanDriver { cargo.env("BSAN_RT_SYSROOT", &builder.sysroot(host_compiler)); cargo.env("BSAN", &bsan_driver); + + // Set the target. + cargo.env("BSAN_TEST_TARGET", target.rustc_target_arg()); { let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "bsan", host, target); let _time = helpers::timeit(builder); diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index e368bc450ef24..209da506a7a06 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -3,7 +3,6 @@ extern crate rustc_driver; -use std::env; use std::sync::Arc; pub const BSAN_BUG_REPORT_URL: &str = "https://github.com/BorrowSanitizer/rust/issues/new"; @@ -22,11 +21,7 @@ pub fn run_compiler( using_internal_features: Arc, ) -> ! { if target_crate { - let mut additional_args = - BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); - if let Some(runtime) = env::var_os("BSAN_HOST_SYSROOT") { - additional_args.push(format!("-L{}/lib", runtime.to_string_lossy())); - } + let additional_args = BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); args.splice(1..1, additional_args); } let exit_code = rustc_driver::catch_with_exit_code(move || { From c04ac910dbb3c8481d591a9a312a1cafca989536 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Sun, 23 Mar 2025 22:24:28 +0000 Subject: [PATCH 07/24] Added back link to host sysroot for platforms that use dynamic libraries for sanitizers. --- src/tools/bsan/bsan-driver/src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index 209da506a7a06..12e82d9823607 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -3,8 +3,8 @@ extern crate rustc_driver; +use std::env; use std::sync::Arc; - pub const BSAN_BUG_REPORT_URL: &str = "https://github.com/BorrowSanitizer/rust/issues/new"; pub const BSAN_DEFAULT_ARGS: &[&str] = @@ -21,7 +21,11 @@ pub fn run_compiler( using_internal_features: Arc, ) -> ! { if target_crate { - let additional_args = BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); + let mut additional_args = + BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); + if let Some(runtime) = env::var_os("BSAN_HOST_SYSROOT") { + additional_args.push(format!("-L{}/lib", runtime.to_string_lossy())); + } args.splice(1..1, additional_args); } let exit_code = rustc_driver::catch_with_exit_code(move || { From a717b97b2cbe6dd1472d5ca580ad3d91bb507e7a Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Mon, 24 Mar 2025 19:55:57 +0000 Subject: [PATCH 08/24] Added BSAN_RT_SYSROOT for cross-stage linking. --- src/bootstrap/src/core/build_steps/bsan.rs | 1 - src/bootstrap/src/core/build_steps/test.rs | 9 ++++++++- src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs | 4 ++-- src/tools/bsan/bsan-driver/src/lib.rs | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/bsan.rs b/src/bootstrap/src/core/build_steps/bsan.rs index 56383c457e769..448380f1ed208 100644 --- a/src/bootstrap/src/core/build_steps/bsan.rs +++ b/src/bootstrap/src/core/build_steps/bsan.rs @@ -256,7 +256,6 @@ impl Step for BsanRTCore { ); cargo.rustflag("-Cembed-bitcode=yes"); - cargo.rustflag("-Clto"); cargo.rustflag("-Cpanic=abort"); cargo.env("BSAN_HEADER_DIR", builder.cargo_out(compiler, mode, target)); let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {}); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c1a7d48d2b887..622b283ef7ddd 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -570,7 +570,13 @@ impl Step for BsanDriver { // We also need sysroots, for BSAN and for the host (the latter for build scripts). // This is for the tests so everything is done with the target compiler. - let bsan_sysroot = BsanDriver::build_bsan_sysroot(builder, target_compiler, target); + let bsan_sysroot = BsanDriver::build_bsan_sysroot( + builder, + target_compiler, + target, + &builder.sysroot(host_compiler), + ); + builder.ensure(compile::Std::new(target_compiler, host)); let host_sysroot = builder.sysroot(target_compiler); @@ -604,6 +610,7 @@ impl Step for BsanDriver { let mut cargo = prepare_cargo_test(cargo, &[], &[], "bsan", host_compiler, host, builder); // bsan tests need to know about the stage sysroot + cargo.env("BSAN_SYSROOT", &bsan_sysroot); cargo.env("BSAN_HOST_SYSROOT", &host_sysroot); diff --git a/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs b/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs index ff9764fae1a08..8f357967b4664 100644 --- a/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs +++ b/src/tools/bsan/bsan-driver/cargo-bsan/src/phases.rs @@ -113,8 +113,8 @@ pub fn phase_cargo_bsan(mut args: impl Iterator) { cmd.env("BSAN_VERBOSE", verbose.to_string()); // This makes the other phases verbose. } - if env::var_os("BSAN_HOST_SYSROOT").is_none() { - cmd.env("BSAN_HOST_SYSROOT", get_host_sysroot_dir(verbose)); + if env::var_os("BSAN_RT_SYSROOT").is_none() { + cmd.env("BSAN_RT_SYSROOT", get_host_sysroot_dir(verbose)); } // Run cargo. diff --git a/src/tools/bsan/bsan-driver/src/lib.rs b/src/tools/bsan/bsan-driver/src/lib.rs index 12e82d9823607..54322fc6a61a6 100644 --- a/src/tools/bsan/bsan-driver/src/lib.rs +++ b/src/tools/bsan/bsan-driver/src/lib.rs @@ -5,6 +5,7 @@ extern crate rustc_driver; use std::env; use std::sync::Arc; + pub const BSAN_BUG_REPORT_URL: &str = "https://github.com/BorrowSanitizer/rust/issues/new"; pub const BSAN_DEFAULT_ARGS: &[&str] = @@ -23,8 +24,9 @@ pub fn run_compiler( if target_crate { let mut additional_args = BSAN_DEFAULT_ARGS.iter().map(ToString::to_string).collect::>(); - if let Some(runtime) = env::var_os("BSAN_HOST_SYSROOT") { - additional_args.push(format!("-L{}/lib", runtime.to_string_lossy())); + if let Some(runtime) = env::var_os("BSAN_RT_SYSROOT") { + let rt = runtime.to_string_lossy(); + additional_args.push(format!("-L{}/lib", rt)); } args.splice(1..1, additional_args); } From 1eb41fada659bd225717286756e37f96d525348c Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Mon, 24 Mar 2025 21:10:21 +0000 Subject: [PATCH 09/24] Added back lto for bsan_rt. --- src/bootstrap/src/core/build_steps/bsan.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/build_steps/bsan.rs b/src/bootstrap/src/core/build_steps/bsan.rs index 448380f1ed208..56383c457e769 100644 --- a/src/bootstrap/src/core/build_steps/bsan.rs +++ b/src/bootstrap/src/core/build_steps/bsan.rs @@ -256,6 +256,7 @@ impl Step for BsanRTCore { ); cargo.rustflag("-Cembed-bitcode=yes"); + cargo.rustflag("-Clto"); cargo.rustflag("-Cpanic=abort"); cargo.env("BSAN_HEADER_DIR", builder.cargo_out(compiler, mode, target)); let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {}); From 72974c1e49567c7288b788d6e2e086fed4479b2a Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 20 Mar 2025 14:29:32 -0400 Subject: [PATCH 10/24] Updated bsan_rt API to use return pointers for provenance, and re-routed allocation through `GlobalState`. --- compiler/rustc_codegen_llvm/src/retag.rs | 1 - src/tools/bsan/bsan-rt/Cargo.toml | 5 + src/tools/bsan/bsan-rt/src/bsan_alloc.rs | 73 ------- src/tools/bsan/bsan-rt/src/global.rs | 266 ++++++++++++++++++++++- src/tools/bsan/bsan-rt/src/lib.rs | 197 +++++++++++++++-- src/tools/bsan/bsan-rt/src/shadow.rs | 57 ++--- src/tools/bsan/bsan-rt/src/tests.rs | 8 + 7 files changed, 471 insertions(+), 136 deletions(-) delete mode 100644 compiler/rustc_codegen_llvm/src/retag.rs delete mode 100644 src/tools/bsan/bsan-rt/src/bsan_alloc.rs create mode 100644 src/tools/bsan/bsan-rt/src/tests.rs diff --git a/compiler/rustc_codegen_llvm/src/retag.rs b/compiler/rustc_codegen_llvm/src/retag.rs deleted file mode 100644 index 8b137891791fe..0000000000000 --- a/compiler/rustc_codegen_llvm/src/retag.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/tools/bsan/bsan-rt/Cargo.toml b/src/tools/bsan/bsan-rt/Cargo.toml index 92a6a0f7cb8fe..915f13cb4ca38 100644 --- a/src/tools/bsan/bsan-rt/Cargo.toml +++ b/src/tools/bsan/bsan-rt/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" [dependencies] libc = { version = "0.2.169", default-features = false } +hashbrown = { version = "0.15.2", default-features = false, features = ["nightly", "inline-more"] } +rustc-hash = { version = "2.1.1", default-features = false } [lib] name = "bsan_rt" @@ -12,5 +14,8 @@ crate-type = ["staticlib"] test = true # we have unit tests doctest = false # but no doc tests +[features] +ui_test = [] + [build-dependencies] cbindgen = "0.28.0" \ No newline at end of file diff --git a/src/tools/bsan/bsan-rt/src/bsan_alloc.rs b/src/tools/bsan/bsan-rt/src/bsan_alloc.rs deleted file mode 100644 index 8a4d5d0e529ff..0000000000000 --- a/src/tools/bsan/bsan-rt/src/bsan_alloc.rs +++ /dev/null @@ -1,73 +0,0 @@ -use core::alloc::{AllocError, Allocator, GlobalAlloc, Layout}; -use core::mem::{self, zeroed}; -use core::ptr::NonNull; - -use libc::{c_int, c_void, free, malloc, off_t}; - -pub type MMap = unsafe extern "C" fn(*mut c_void, usize, c_int, c_int, c_int, i64) -> *mut c_void; -pub type MUnmap = unsafe extern "C" fn(*mut c_void, usize) -> c_int; -pub type Malloc = unsafe extern "C" fn(usize) -> *mut c_void; -pub type Free = unsafe extern "C" fn(*mut c_void); - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct BsanAllocator { - malloc: Malloc, - free: Free, - mmap: MMap, - munmap: MUnmap, -} - -unsafe impl Send for BsanAllocator {} -unsafe impl Sync for BsanAllocator {} - -unsafe impl Allocator for BsanAllocator { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - unsafe { - match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - // SAFETY: `layout` is non-zero in size, - size => unsafe { - let raw_ptr: *mut u8 = mem::transmute((self.malloc)(layout.size())); - let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; - Ok(NonNull::slice_from_raw_parts(ptr, size)) - }, - } - } - } - - unsafe fn deallocate(&self, ptr: NonNull, _layout: Layout) { - (self.free)(mem::transmute(ptr.as_ptr())) - } -} - -#[cfg(test)] -pub static TEST_ALLOC: BsanAllocator = BsanAllocator { - malloc: libc::malloc, - free: libc::free, - mmap: libc::mmap, - munmap: libc::munmap, -}; - -/// We need to declare a global allocator to be able to use `alloc` in a `#[no_std]` -/// crate. Anything other than the `BsanAllocator` object will clash with the interceptors, -/// so we use a placeholder that panics when it is used. -#[cfg(not(test))] -mod global_alloc { - use core::alloc::{GlobalAlloc, Layout}; - - #[derive(Default)] - struct DummyAllocator; - - unsafe impl GlobalAlloc for DummyAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - panic!() - } - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - panic!() - } - } - - #[global_allocator] - static GLOBAL_ALLOCATOR: DummyAllocator = DummyAllocator; -} diff --git a/src/tools/bsan/bsan-rt/src/global.rs b/src/tools/bsan/bsan-rt/src/global.rs index 3d114aad6e54f..b46b7f0c5da4d 100644 --- a/src/tools/bsan/bsan-rt/src/global.rs +++ b/src/tools/bsan/bsan-rt/src/global.rs @@ -1,28 +1,278 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use core::alloc::{AllocError, Allocator, GlobalAlloc, Layout}; use core::cell::SyncUnsafeCell; +use core::ffi::CStr; +use core::fmt; +use core::fmt::{Write, write}; +use core::mem::{self, zeroed}; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; use core::sync::atomic::AtomicUsize; -use crate::BsanAllocator; -#[cfg(test)] -use crate::TEST_ALLOC; +use hashbrown::{DefaultHashBuilder, HashMap}; +use rustc_hash::FxBuildHasher; + +use crate::BsanHooks; +/// Every action that requires a heap allocation must be performed through a globally +/// accessible, singleton instance of `GlobalContext`. Initializing or obtaining +/// a reference to this instance is unsafe, since it requires having been initialized +/// with a valid set of `BsanHooks`, which is provided from across the FFI. +/// Only shared references (&self) can be obtained, since this object will be concurrently +/// accessed. All of the API endpoints here can be soundly marked as safe under the assumption +/// that these invariants hold. This design pattern requires us to pass the `GlobalContext` instance +/// around explicitly, but it prevents us from relying on implicit global state and limits the spread +/// of unsafety throughout the library. #[derive(Debug)] pub struct GlobalContext { - allocator: BsanAllocator, + pub hooks: BsanHooks, next_alloc_id: AtomicUsize, } impl GlobalContext { - fn new(allocator: BsanAllocator) -> Self { - Self { allocator, next_alloc_id: AtomicUsize::new(1) } + /// Creates a new instance of `GlobalContext` using the given `BsanHooks`. + /// This function will also initialize our shadow heap + fn new(hooks: BsanHooks) -> Self { + Self { hooks, next_alloc_id: AtomicUsize::new(1) } + } + + /// Creates a new instance of `BVec`, allocated using the + /// `GlobalContext` instance as an `Allocator`. + pub fn vec(&self) -> BVec { + BVec::new() + } + + /// Creates a new instance of `BVecDeque`, which will be allocated using the + /// `GlobalContext` instance as an `Allocator`. + pub fn vecdeque(&self) -> BVecDeque { + BVecDeque::new() + } + + /// Prints a given set of formatted arguments. This function is not meant + /// to be called directly; instead, it should be used with the `print!`, + /// `println!`, and `ui_test!` macros. + pub fn print(&self, args: fmt::Arguments<'_>) { + let mut w = self.vec(); + let _ = write!(&mut w, "{}", args); + unsafe { + (self.hooks.print)(mem::transmute(w.as_ptr())); + } + } +} + +/// Prints to stdout. +macro_rules! print { + ($ctx:expr, $($arg:tt)*) => {{ + ctx.print(core::format_args!($($arg)*)); + }}; +} +pub(crate) use print; + +/// Prints to stdout, appending a newline. +macro_rules! println { + ($ctx:expr) => { + $crate::print!($ctx, "\n") + }; + ($ctx:expr, $($arg:tt)*) => {{ + $ctx.print(core::format_args_nl!($($arg)*)); + }}; +} +pub(crate) use println; + +// General-purpose debug logging, which is only enabled in debug builds. +macro_rules! debug { + ($ctx:expr, $($arg:tt)*) => { + #[cfg(debug_assertions)] + $crate::println!($ctx, $($arg)*); + }; +} +pub(crate) use debug; + +// Logging for UI testing, which is enabled by the `ui_test` feature. +macro_rules! ui_test { + ($ctx:expr, $($arg:tt)*) => { + #[cfg(feature = "ui_test")] + $crate::println!($ctx, $($arg)*); + }; +} +pub(crate) use ui_test; + +unsafe impl Allocator for &GlobalContext { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + unsafe { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + // SAFETY: `layout` is non-zero in size, + size => unsafe { + let raw_ptr: *mut u8 = mem::transmute((self.hooks.malloc)(layout.size())); + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; + Ok(NonNull::slice_from_raw_parts(ptr, size)) + }, + } + } + } + + unsafe fn deallocate(&self, ptr: NonNull, _layout: Layout) { + (self.hooks.free)(mem::transmute(ptr.as_ptr())) + } +} + +/// A thin wrapper around `Vec` that uses `GlobalContext` as its allocator +#[derive(Debug, Clone)] +pub struct BVec(Vec); + +impl BVec { + fn new() -> Self { + unsafe { Self(Vec::new_in(global_ctx())) } + } +} + +impl Deref for BVec { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for BVec { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } +/// We provide this trait implementation so that we can use `BVec` to +/// store the temporary results of formatting a string in the implementation +/// of `GlobalContext::print` +impl core::fmt::Write for BVec { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let bytes = s.bytes(); + if self.try_reserve_exact(bytes.len()).is_err() { + Err(core::fmt::Error) + } else { + self.extend(bytes); + Ok(()) + } + } +} + +/// A thin wrapper around `VecDeque` that uses `GlobalContext` as its allocator +#[derive(Debug, Clone)] +pub struct BVecDeque(VecDeque); + +impl BVecDeque { + fn new() -> Self { + unsafe { Self(VecDeque::new_in(global_ctx())) } + } +} + +impl Deref for BVecDeque { + type Target = VecDeque; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for BVecDeque { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// The seed for the random state of the hash function for `BHashMap`. +/// Equal to the decimal encoding of the ascii for "BSAN". +static BSAN_HASH_SEED: usize = 1112752462; + +/// A thin wrapper around `HashMap` that uses `GlobalContext` as its allocator +#[derive(Debug, Clone)] +pub struct BHashMap(HashMap); + +impl BHashMap { + fn new() -> Self { + unsafe { Self(HashMap::with_hasher_in(FxBuildHasher, global_ctx())) } + } +} + +impl Deref for BHashMap { + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for BHashMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// We need to declare a global allocator to be able to use `alloc` in a `#[no_std]` +/// crate. Anything other than the `GlobalContext` object will clash with the interceptors, +/// so we provide a placeholder that panics when it is used. +#[cfg(not(test))] +mod global_alloc { + use core::alloc::{GlobalAlloc, Layout}; + + #[derive(Default)] + struct DummyAllocator; + + unsafe impl GlobalAlloc for DummyAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + panic!() + } + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + panic!() + } + } + + #[global_allocator] + static GLOBAL_ALLOCATOR: DummyAllocator = DummyAllocator; +} + +#[cfg(not(test))] pub static GLOBAL_CTX: SyncUnsafeCell> = SyncUnsafeCell::new(None); -pub unsafe fn init_global_ctx(alloc: BsanAllocator) { - *GLOBAL_CTX.get() = Some(GlobalContext::new(alloc)); +#[cfg(test)] +pub static TEST_HOOKS: BsanHooks = BsanHooks { + malloc: libc::malloc, + free: libc::free, + mmap: libc::mmap, + munmap: libc::munmap, + print: |ptr| unsafe { + println!(mem::transmute::<&str>(ptr)); + }, +}; + +/// A singleton instance of `GlobalContext`. All API functions +/// rely on this state to be initialized. +#[cfg(test)] +pub static GLOBAL_CTX: SyncUnsafeCell> = + SyncUnsafeCell::new(Some(GlobalContext::new(TEST_HOOKS))); + +/// Initializes the global context object. +/// This function must only be called once: when the program is first initialized. +/// It is marked as `unsafe`, because it relies on the set of function pointers in +/// `BsanHooks` to be valid. +#[inline] +pub unsafe fn init_global_ctx(hooks: BsanHooks) -> &'static GlobalContext { + *GLOBAL_CTX.get() = Some(GlobalContext::new(hooks)); + global_ctx() +} + +/// Deinitializes the global context object. +/// This function must only be called once: when the program is terminating. +/// It is marked as `unsafe`, since all other API functions except for `bsan_init` rely +/// on the assumption that this function has not been called yet. +#[inline] +pub unsafe fn deinit_global_ctx() { + drop((&mut *GLOBAL_CTX.get()).take()); } +/// Accessing the global context is unsafe since the user needs to ensure that +/// the context is initialized, e.g. `bsan_init` has been called and `bsan_deinit` +/// has not yet been called. #[inline] pub unsafe fn global_ctx() -> &'static GlobalContext { (&(*GLOBAL_CTX.get())).as_ref().unwrap_unchecked() diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index ba2e438d8d233..f6fbb5f78ee8e 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -1,52 +1,213 @@ #![cfg_attr(not(test), no_std)] -#![feature(allocator_api)] #![feature(sync_unsafe_cell)] -#![feature(alloc_layout_extra)] #![feature(strict_overflow_ops)] +#![feature(thread_local)] +#![feature(allocator_api)] +#![feature(alloc_layout_extra)] +#![feature(format_args_nl)] #![allow(unused)] extern crate alloc; use core::cell::UnsafeCell; -use core::ffi::c_void; +use core::ffi::{c_char, c_void}; +use core::mem::MaybeUninit; use core::num::NonZero; #[cfg(not(test))] use core::panic::PanicInfo; +use core::ptr; mod global; -use global::{global_ctx, init_global_ctx}; - -mod bsan_alloc; -pub use bsan_alloc::BsanAllocator; -#[cfg(test)] -pub use bsan_alloc::TEST_ALLOC; +mod tests; +pub use global::*; mod shadow; +pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, i64) -> *mut c_void; +pub type MUnmap = unsafe extern "C" fn(*mut c_void, usize) -> i32; +pub type Malloc = unsafe extern "C" fn(usize) -> *mut c_void; +pub type Free = unsafe extern "C" fn(*mut c_void); +pub type Print = unsafe extern "C" fn(*const c_char); + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct BsanHooks { + malloc: Malloc, + free: Free, + mmap: MMap, + munmap: MUnmap, + print: Print, +} + +/// Unique identifier for an allocation +pub type AllocID = usize; + +/// Unique identifier for a node within the tree +pub type BorTag = usize; + +/// Unique identifier for a source location. Every update to the tree +/// is associated with a `Span`, which allows us to provide a detailed history +/// of the actions that lead to an aliasing violation. +pub type Span = usize; + +/// Pointers have provenance (RFC #3559). In Tree Borrows, this includes an allocation ID +/// and a borrow tag. We also include a pointer to the "lock" location for the allocation, +/// which contains all other metadata used to detect undefined behavior. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Provenance { + pub alloc_id: AllocID, + pub bor_tag: BorTag, + pub alloc_info: *mut c_void, +} + +impl Provenance { + /// The default provenance value, which is assigned to dangling or invalid + /// pointers. + const fn null() -> Self { + Provenance { alloc_id: 0, bor_tag: 0, alloc_info: core::ptr::null_mut() } + } + + /// Pointers cast from integers receive a "wildcard" provenance value, which permits + /// any access. A provenance value with an `alloc_id` of zero and any non-zero `bor_tag` + /// is treated as a wildcard provenance value. + const fn wildcard() -> Self { + Provenance { alloc_id: 0, bor_tag: 1, alloc_info: core::ptr::null_mut() } + } +} + +/// Every allocation is associated with a "lock" object, which is an instance of `AllocInfo`. +/// Provenance is the "key" to this lock. To validate a memory access, we compare the allocation ID +/// of a pointer's provenance with the value stored in its corresponding `AllocInfo` object. If the values +/// do not match, then the access is invalid. If they do match, then we proceed to validate the access against +/// the tree for the allocation. +#[repr(C)] +struct AllocInfo { + pub alloc_id: usize, + pub base_addr: usize, + pub size: usize, + pub align: usize, + pub tree: *mut c_void, +} + +impl AllocInfo { + /// When we deallocate an allocation, we need to invalidate its metadata. + /// so that any uses-after-free are detectable. + fn dealloc(&mut self) { + self.alloc_id = 0; + self.base_addr = 0; + self.size = 0; + self.align = 1; + // FIXME: free the tree + } +} + +/// When a function returns, our instrumentation stores the provenance of its return value +/// in this thread-local array so that it can be read by the caller. +#[no_mangle] +#[thread_local] +#[allow(non_upper_case_globals)] +pub static mut __bsan_retval_tls: [Provenance; 100] = [Provenance::null(); 100]; + +/// When we call a function, we write the provenance of its arguments into this thread-local array +/// so that we can read them in the callee. +#[no_mangle] +#[thread_local] +#[allow(non_upper_case_globals)] +pub static mut __bsan_arg_tls: [Provenance; 100] = [Provenance::null(); 100]; + +/// Initializes the global state of the runtime library. +/// The safety of this library is entirely dependent on this +/// function having been executed. We assume the global invariant that +/// no other API functions will be called prior to that point. +#[no_mangle] +unsafe extern "C" fn bsan_init(hooks: BsanHooks) { + let ctx = init_global_ctx(hooks); + ui_test!(ctx, "bsan_init"); +} + +/// Deinitializes the global state of the runtime library. +/// We assume the global invariant that no other API functions +/// will be called after this function has executed. +#[no_mangle] +unsafe extern "C" fn bsan_deinit() { + ui_test!(global_ctx(), "bsan_deinit"); + deinit_global_ctx(); +} + +/// Creates a new borrow tag for the given provenance object. +#[no_mangle] +extern "C" fn bsan_retag(span: Span, prov: *mut Provenance, retag_kind: u8, place_kind: u8) { + debug_assert!(prov != ptr::null_mut()); +} + +/// Records a read access of size `access_size` at the given address `addr` using the provenance `prov`. #[no_mangle] -unsafe extern "C" fn bsan_init(alloc: BsanAllocator) { - init_global_ctx(alloc); +extern "C" fn bsan_read(span: Span, prov: *const Provenance, addr: usize, access_size: u64) { + debug_assert!(prov != ptr::null_mut()); } +/// Records a write access of size `access_size` at the given address `addr` using the provenance `prov`. #[no_mangle] -extern "C" fn bsan_expose_tag(ptr: *mut c_void) {} +extern "C" fn bsan_write(span: Span, prov: *const Provenance, addr: usize, access_size: u64) { + debug_assert!(prov != ptr::null_mut()); +} +/// Loads the provenance of a given address from shadow memory and stores +/// the result in the return pointer. #[no_mangle] -extern "C" fn bsan_retag(ptr: *mut c_void, retag_kind: u8, place_kind: u8) -> u64 { - 0 +extern "C" fn bsan_load_prov(prov: *mut MaybeUninit, addr: usize) { + debug_assert!(prov != ptr::null_mut()); + unsafe { + (*prov).write(Provenance::null()); + } } +/// Stores the given provenance value into shadow memory at the location for the given address. #[no_mangle] -extern "C" fn bsan_read(ptr: *mut c_void, access_size: u64) {} +extern "C" fn bsan_store_prov(prov: *const Provenance, addr: usize) { + debug_assert!(prov != ptr::null_mut()); +} +/// Pushes a shadow stack frame #[no_mangle] -extern "C" fn bsan_write(ptr: *mut c_void, access_size: u64) {} +extern "C" fn bsan_push_frame(span: Span) {} +/// Pops a shadow stack frame, deallocating all shadow allocations created by `bsan_alloc_stack` #[no_mangle] -extern "C" fn bsan_func_entry() {} +extern "C" fn bsan_pop_frame(span: Span) {} +// Registers a heap allocation of size `size` #[no_mangle] -extern "C" fn bsan_func_exit() {} +extern "C" fn bsan_alloc(span: Span, prov: *mut MaybeUninit, addr: usize) { + debug_assert!(prov != ptr::null_mut()); + + unsafe { + (*prov).write(Provenance::null()); + } +} + +/// Registers a stack allocation of size `size`. +#[no_mangle] +extern "C" fn bsan_alloc_stack(span: Span, prov: *mut MaybeUninit, size: usize) { + debug_assert!(prov != ptr::null_mut()); + unsafe { + (*prov).write(Provenance::null()); + } +} + +/// Deregisters a heap allocation +#[no_mangle] +extern "C" fn bsan_dealloc(span: Span, prov: *mut Provenance) { + debug_assert!(prov != ptr::null_mut()); +} + +/// Marks the borrow tag for `prov` as "exposed," allowing it to be resolved to +/// validate accesses through "wildcard" pointers. +#[no_mangle] +extern "C" fn bsan_expose_tag(prov: *const Provenance) { + debug_assert!(prov != ptr::null_mut()); +} #[cfg(not(test))] #[panic_handler] diff --git a/src/tools/bsan/bsan-rt/src/shadow.rs b/src/tools/bsan/bsan-rt/src/shadow.rs index 59f93cc941878..57c03f7cf336c 100644 --- a/src/tools/bsan/bsan-rt/src/shadow.rs +++ b/src/tools/bsan/bsan-rt/src/shadow.rs @@ -1,14 +1,17 @@ use core::alloc::Layout; +use core::iter::repeat_n; use core::marker::PhantomData; use core::mem; use core::ops::{Add, BitAnd, Deref, DerefMut, Shr}; +use crate::Provenance; + /// Different targets have a different number /// of significant bits in their pointer representation. /// On 32-bit platforms, all 32-bits are addressable. Most /// 64-bit platforms only use 48-bits. Following the LLVM Project, /// we hard-code these values based on the underlying architecture. -/// Most, if not all 64 bit architectures use 48-bits. However, a the +/// Most, if not all 64 bit architectures use 48-bits. However, the /// Armv8-A spec allows addressing 52 or 56 bits as well. No processors /// implement this yet, though, so we can use target_pointer_width. @@ -56,46 +59,41 @@ fn table_indices(address: usize) -> (usize, usize) { (l1_index, l2_index) } -// Provenance values must be sized so that we can allocate an array of them -// for the L1 page table. We can make provenance values Copy since they should -// fit within 128 bits and they are not "owned" by any particular object. -pub trait Provenance: Copy + Sized {} - #[repr(C)] -pub struct L2 { - bytes: [T; L2_LEN], +pub struct L2 { + bytes: [Provenance; L2_LEN], } -impl L2 { +impl L2 { #[inline(always)] - unsafe fn lookup_mut(&mut self, index: usize) -> &mut T { + unsafe fn lookup_mut(&mut self, index: usize) -> &mut Provenance { self.bytes.get_unchecked_mut(index) } #[inline(always)] - unsafe fn lookup(&mut self, index: usize) -> &T { + unsafe fn lookup(&mut self, index: usize) -> &Provenance { self.bytes.get_unchecked(index) } } #[repr(C)] -pub struct L1 { - entries: [*mut L2; L1_LEN], +pub struct L1 { + entries: [*mut L2; L1_LEN], } -impl L1 { +impl L1 { fn new() -> Self { Self { entries: [core::ptr::null_mut(); L1_LEN] } } #[inline(always)] - unsafe fn lookup_mut(&mut self, index: usize) -> Option<&mut T> { + unsafe fn lookup_mut(&mut self, index: usize) -> Option<&mut Provenance> { let (l1_index, l2_index) = table_indices(index); let l2 = self.entries.get_unchecked_mut(l1_index); if l2.is_null() { None } else { Some((**l2).lookup_mut(l2_index)) } } #[inline(always)] - unsafe fn lookup(&mut self, index: usize) -> Option<&T> { + unsafe fn lookup(&mut self, index: usize) -> Option<&Provenance> { let (l1_index, l2_index) = table_indices(index); let l2 = self.entries.get_unchecked(l1_index); if l2.is_null() { None } else { Some((**l2).lookup(l2_index)) } @@ -106,39 +104,26 @@ impl L1 { /// the interior, unsafe implementation, providing debug assertions /// for each method. #[repr(transparent)] -pub struct ShadowHeap { - l1: L1, +pub struct ShadowHeap { + l1: L1, } -impl Default for ShadowHeap { +impl Default for ShadowHeap { fn default() -> Self { - let l1 = L1::::new(); + let l1 = L1::new(); Self { l1 } } } -impl Deref for ShadowHeap { - type Target = L1; +impl Deref for ShadowHeap { + type Target = L1; fn deref(&self) -> &Self::Target { &self.l1 } } -impl DerefMut for ShadowHeap { +impl DerefMut for ShadowHeap { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.l1 } } - -#[cfg(test)] -mod tests { - use super::*; - type TestProv = u8; - - impl Provenance for TestProv {} - - #[test] - fn create_and_drop() { - let _ = ShadowHeap::::default(); - } -} diff --git a/src/tools/bsan/bsan-rt/src/tests.rs b/src/tools/bsan/bsan-rt/src/tests.rs new file mode 100644 index 0000000000000..d8f856504dba1 --- /dev/null +++ b/src/tools/bsan/bsan-rt/src/tests.rs @@ -0,0 +1,8 @@ +#[cfg(test)] + +use crate::*; + +#[test] +fn create_and_drop() { + let _ = ShadowHeap::default(); +} From ec1a339e53a30340882d62bf215d7577454ba738 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Tue, 25 Mar 2025 15:00:10 +0000 Subject: [PATCH 11/24] Updated LLVM submodule. --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 726f06435db05..a55cec0238a91 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 726f06435db05ba03a26cd8a6c3aa53d0cf7cca9 +Subproject commit a55cec0238a914db2d001cfa2fea1a98dbf5960a From 8d00ac838d52e21d340857d9cd407bf982124677 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Tue, 25 Mar 2025 17:22:31 +0000 Subject: [PATCH 12/24] Updated LLVM submodule. --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index a55cec0238a91..14521f6ad81d1 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit a55cec0238a914db2d001cfa2fea1a98dbf5960a +Subproject commit 14521f6ad81d1f491d63dc33ef6e5e7b591f02e9 From fe35770413e470915f9534e9bac4f14d8e2e04e1 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 27 Mar 2025 14:05:26 -0400 Subject: [PATCH 13/24] Added API endpoints for copying and clearing shadow memory. --- src/tools/bsan/bsan-rt/src/lib.rs | 22 ++++++++-------------- src/tools/bsan/bsan-rt/src/tests.rs | 1 - 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index f6fbb5f78ee8e..befaf822eab96 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -102,20 +102,6 @@ impl AllocInfo { } } -/// When a function returns, our instrumentation stores the provenance of its return value -/// in this thread-local array so that it can be read by the caller. -#[no_mangle] -#[thread_local] -#[allow(non_upper_case_globals)] -pub static mut __bsan_retval_tls: [Provenance; 100] = [Provenance::null(); 100]; - -/// When we call a function, we write the provenance of its arguments into this thread-local array -/// so that we can read them in the callee. -#[no_mangle] -#[thread_local] -#[allow(non_upper_case_globals)] -pub static mut __bsan_arg_tls: [Provenance; 100] = [Provenance::null(); 100]; - /// Initializes the global state of the runtime library. /// The safety of this library is entirely dependent on this /// function having been executed. We assume the global invariant that @@ -209,6 +195,14 @@ extern "C" fn bsan_expose_tag(prov: *const Provenance) { debug_assert!(prov != ptr::null_mut()); } +/// Copies provenance stored in the given range in shadow memory from the source to destination. +#[no_mangle] +extern "C" fn bsan_shadow_copy(dst: usize, src: usize, access_size: usize) {} + +/// Clears provenance from shadow memory starting at a given address. +#[no_mangle] +extern "C" fn bsan_shadow_clear(addr: usize, access_size: usize) {} + #[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo<'_>) -> ! { diff --git a/src/tools/bsan/bsan-rt/src/tests.rs b/src/tools/bsan/bsan-rt/src/tests.rs index d8f856504dba1..bdd58c89bc5aa 100644 --- a/src/tools/bsan/bsan-rt/src/tests.rs +++ b/src/tools/bsan/bsan-rt/src/tests.rs @@ -1,5 +1,4 @@ #[cfg(test)] - use crate::*; #[test] From d03712e837aa8900b620f697865aad0c5e42a4d5 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 27 Mar 2025 15:26:37 -0400 Subject: [PATCH 14/24] Switched to GlobalCtx, moved tests, added BsanAllocHooks. --- Cargo.lock | 58 ++++++++---- src/tools/bsan/bsan-rt/Cargo.toml | 3 +- src/tools/bsan/bsan-rt/src/global.rs | 130 +++++++++++---------------- src/tools/bsan/bsan-rt/src/lib.rs | 126 +++++++++++++++++++++----- src/tools/bsan/bsan-rt/src/shadow.rs | 8 ++ 5 files changed, 208 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25f143e58b819..4a2e89ed548c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,6 +255,17 @@ dependencies = [ "serde", ] +[[package]] +name = "biri" +version = "0.1.0" +dependencies = [ + "colored", + "regex", + "rustc_version", + "tempfile", + "ui_test", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -307,9 +318,10 @@ name = "bsan-rt" version = "0.1.0" dependencies = [ "cbindgen", - "env_logger", + "hashbrown 0.15.2", "libc", - "log", + "rustc-hash 2.1.1", + "smallvec", ] [[package]] @@ -392,6 +404,19 @@ dependencies = [ "serde", ] +[[package]] +name = "cargo-biri" +version = "0.1.0" +dependencies = [ + "cargo_metadata 0.18.1", + "directories 5.0.1", + "rustc-build-sysroot", + "rustc_tools_util", + "rustc_version", + "serde", + "serde_json", +] + [[package]] name = "cargo-bsan" version = "0.1.0" @@ -1610,10 +1635,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ + "allocator-api2", "foldhash", ] @@ -1870,7 +1896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "rustc-rayon", "serde", ] @@ -2012,7 +2038,7 @@ dependencies = [ "anyhow", "clap", "fs-err", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustdoc-json-types", "serde", "serde_json", @@ -2561,7 +2587,7 @@ checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "crc32fast", "flate2", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "indexmap", "memchr", "ruzstd", @@ -3178,7 +3204,7 @@ dependencies = [ "proc-macro2", "quote", "rinja_parser", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "serde", "syn 2.0.87", ] @@ -3242,9 +3268,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-main" @@ -3626,7 +3652,7 @@ dependencies = [ "memmap2", "parking_lot", "portable-atomic", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustc-rayon", "rustc-stable-hash", "rustc_arena", @@ -4327,7 +4353,7 @@ dependencies = [ name = "rustc_pattern_analysis" version = "0.0.0" dependencies = [ - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "rustc_abi", "rustc_apfloat", "rustc_arena", @@ -4723,7 +4749,7 @@ name = "rustdoc-json-types" version = "0.1.0" dependencies = [ "bincode", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "serde", "serde_json", ] @@ -4986,9 +5012,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" @@ -5367,7 +5393,7 @@ dependencies = [ "ignore", "miropt-test-tools", "regex", - "rustc-hash 2.0.0", + "rustc-hash 2.1.1", "semver", "similar", "termcolor", diff --git a/src/tools/bsan/bsan-rt/Cargo.toml b/src/tools/bsan/bsan-rt/Cargo.toml index 915f13cb4ca38..6c2b424c4e077 100644 --- a/src/tools/bsan/bsan-rt/Cargo.toml +++ b/src/tools/bsan/bsan-rt/Cargo.toml @@ -5,8 +5,9 @@ edition = "2021" [dependencies] libc = { version = "0.2.169", default-features = false } -hashbrown = { version = "0.15.2", default-features = false, features = ["nightly", "inline-more"] } +hashbrown = { version = "0.15.2", default-features = false, features = ["default-hasher", "nightly", "inline-more"] } rustc-hash = { version = "2.1.1", default-features = false } +smallvec = { version = "1.14.0" } [lib] name = "bsan_rt" diff --git a/src/tools/bsan/bsan-rt/src/global.rs b/src/tools/bsan/bsan-rt/src/global.rs index b46b7f0c5da4d..2bedb541d2299 100644 --- a/src/tools/bsan/bsan-rt/src/global.rs +++ b/src/tools/bsan/bsan-rt/src/global.rs @@ -1,6 +1,5 @@ use alloc::collections::VecDeque; use alloc::vec::Vec; -use core::alloc::{AllocError, Allocator, GlobalAlloc, Layout}; use core::cell::SyncUnsafeCell; use core::ffi::CStr; use core::fmt; @@ -13,47 +12,39 @@ use core::sync::atomic::AtomicUsize; use hashbrown::{DefaultHashBuilder, HashMap}; use rustc_hash::FxBuildHasher; -use crate::BsanHooks; +use crate::*; /// Every action that requires a heap allocation must be performed through a globally -/// accessible, singleton instance of `GlobalContext`. Initializing or obtaining +/// accessible, singleton instance of `GlobalCtx`. Initializing or obtaining /// a reference to this instance is unsafe, since it requires having been initialized /// with a valid set of `BsanHooks`, which is provided from across the FFI. -/// Only shared references (&self) can be obtained, since this object will be concurrently -/// accessed. All of the API endpoints here can be soundly marked as safe under the assumption -/// that these invariants hold. This design pattern requires us to pass the `GlobalContext` instance +/// Only shared references (&self) can be obtained, since this object will be accessed concurrently. +/// All of its API endpoints are free from undefined behavior, under +/// that these invariants hold. This design pattern requires us to pass the `GlobalCtx` instance /// around explicitly, but it prevents us from relying on implicit global state and limits the spread /// of unsafety throughout the library. #[derive(Debug)] -pub struct GlobalContext { - pub hooks: BsanHooks, +pub struct GlobalCtx { + hooks: BsanHooks, next_alloc_id: AtomicUsize, } -impl GlobalContext { - /// Creates a new instance of `GlobalContext` using the given `BsanHooks`. +impl GlobalCtx { + /// Creates a new instance of `GlobalCtx` using the given `BsanHooks`. /// This function will also initialize our shadow heap fn new(hooks: BsanHooks) -> Self { Self { hooks, next_alloc_id: AtomicUsize::new(1) } } - /// Creates a new instance of `BVec`, allocated using the - /// `GlobalContext` instance as an `Allocator`. - pub fn vec(&self) -> BVec { - BVec::new() - } - - /// Creates a new instance of `BVecDeque`, which will be allocated using the - /// `GlobalContext` instance as an `Allocator`. - pub fn vecdeque(&self) -> BVecDeque { - BVecDeque::new() + fn alloc(&self) -> BsanAllocHooks { + self.hooks.alloc } /// Prints a given set of formatted arguments. This function is not meant /// to be called directly; instead, it should be used with the `print!`, /// `println!`, and `ui_test!` macros. pub fn print(&self, args: fmt::Arguments<'_>) { - let mut w = self.vec(); + let mut w = BVec::new(self); let _ = write!(&mut w, "{}", args); unsafe { (self.hooks.print)(mem::transmute(w.as_ptr())); @@ -98,38 +89,12 @@ macro_rules! ui_test { } pub(crate) use ui_test; -unsafe impl Allocator for &GlobalContext { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - unsafe { - match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - // SAFETY: `layout` is non-zero in size, - size => unsafe { - let raw_ptr: *mut u8 = mem::transmute((self.hooks.malloc)(layout.size())); - let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; - Ok(NonNull::slice_from_raw_parts(ptr, size)) - }, - } - } - } - - unsafe fn deallocate(&self, ptr: NonNull, _layout: Layout) { - (self.hooks.free)(mem::transmute(ptr.as_ptr())) - } -} - -/// A thin wrapper around `Vec` that uses `GlobalContext` as its allocator -#[derive(Debug, Clone)] -pub struct BVec(Vec); - -impl BVec { - fn new() -> Self { - unsafe { Self(Vec::new_in(global_ctx())) } - } -} +/// A thin wrapper around `Vec` that uses `GlobalCtx` as its allocator +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct BVec(Vec); impl Deref for BVec { - type Target = Vec; + type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 @@ -142,9 +107,15 @@ impl DerefMut for BVec { } } +impl BVec { + fn new(ctx: &GlobalCtx) -> Self { + unsafe { Self(Vec::new_in(ctx.alloc())) } + } +} + /// We provide this trait implementation so that we can use `BVec` to /// store the temporary results of formatting a string in the implementation -/// of `GlobalContext::print` +/// of `GlobalCtx::print` impl core::fmt::Write for BVec { fn write_str(&mut self, s: &str) -> core::fmt::Result { let bytes = s.bytes(); @@ -157,18 +128,12 @@ impl core::fmt::Write for BVec { } } -/// A thin wrapper around `VecDeque` that uses `GlobalContext` as its allocator +/// A thin wrapper around `VecDeque` that uses `GlobalCtx` as its allocator #[derive(Debug, Clone)] -pub struct BVecDeque(VecDeque); - -impl BVecDeque { - fn new() -> Self { - unsafe { Self(VecDeque::new_in(global_ctx())) } - } -} +pub struct BVecDeque(VecDeque); impl Deref for BVecDeque { - type Target = VecDeque; + type Target = VecDeque; fn deref(&self) -> &Self::Target { &self.0 @@ -181,22 +146,22 @@ impl DerefMut for BVecDeque { } } +impl BVecDeque { + fn new(ctx: &GlobalCtx) -> Self { + unsafe { Self(VecDeque::new_in(ctx.alloc())) } + } +} + /// The seed for the random state of the hash function for `BHashMap`. /// Equal to the decimal encoding of the ascii for "BSAN". static BSAN_HASH_SEED: usize = 1112752462; -/// A thin wrapper around `HashMap` that uses `GlobalContext` as its allocator +/// A thin wrapper around `HashMap` that uses `GlobalCtx` as its allocator #[derive(Debug, Clone)] -pub struct BHashMap(HashMap); - -impl BHashMap { - fn new() -> Self { - unsafe { Self(HashMap::with_hasher_in(FxBuildHasher, global_ctx())) } - } -} +pub struct BHashMap(HashMap); impl Deref for BHashMap { - type Target = HashMap; + type Target = HashMap; fn deref(&self) -> &Self::Target { &self.0 } @@ -208,8 +173,14 @@ impl DerefMut for BHashMap { } } +impl BHashMap { + fn new(ctx: &GlobalCtx) -> Self { + unsafe { Self(HashMap::with_hasher_in(FxBuildHasher, ctx.alloc())) } + } +} + /// We need to declare a global allocator to be able to use `alloc` in a `#[no_std]` -/// crate. Anything other than the `GlobalContext` object will clash with the interceptors, +/// crate. Anything other than the `GlobalCtx` object will clash with the interceptors, /// so we provide a placeholder that panics when it is used. #[cfg(not(test))] mod global_alloc { @@ -232,12 +203,11 @@ mod global_alloc { } #[cfg(not(test))] -pub static GLOBAL_CTX: SyncUnsafeCell> = SyncUnsafeCell::new(None); +pub static GLOBAL_CTX: SyncUnsafeCell> = SyncUnsafeCell::new(None); #[cfg(test)] pub static TEST_HOOKS: BsanHooks = BsanHooks { - malloc: libc::malloc, - free: libc::free, + alloc: BsanAllocHooks { malloc: libc::malloc, free: libc::free }, mmap: libc::mmap, munmap: libc::munmap, print: |ptr| unsafe { @@ -245,19 +215,19 @@ pub static TEST_HOOKS: BsanHooks = BsanHooks { }, }; -/// A singleton instance of `GlobalContext`. All API functions +/// A singleton instance of `GlobalCtx`. All API functions /// rely on this state to be initialized. #[cfg(test)] -pub static GLOBAL_CTX: SyncUnsafeCell> = - SyncUnsafeCell::new(Some(GlobalContext::new(TEST_HOOKS))); +pub static GLOBAL_CTX: SyncUnsafeCell> = + SyncUnsafeCell::new(Some(GlobalCtx::new(TEST_HOOKS))); /// Initializes the global context object. /// This function must only be called once: when the program is first initialized. /// It is marked as `unsafe`, because it relies on the set of function pointers in /// `BsanHooks` to be valid. #[inline] -pub unsafe fn init_global_ctx(hooks: BsanHooks) -> &'static GlobalContext { - *GLOBAL_CTX.get() = Some(GlobalContext::new(hooks)); +pub unsafe fn init_global_ctx(hooks: BsanHooks) -> &'static GlobalCtx { + *GLOBAL_CTX.get() = Some(GlobalCtx::new(hooks)); global_ctx() } @@ -274,6 +244,6 @@ pub unsafe fn deinit_global_ctx() { /// the context is initialized, e.g. `bsan_init` has been called and `bsan_deinit` /// has not yet been called. #[inline] -pub unsafe fn global_ctx() -> &'static GlobalContext { +pub unsafe fn global_ctx() -> &'static GlobalCtx { (&(*GLOBAL_CTX.get())).as_ref().unwrap_unchecked() } diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index befaf822eab96..b868b42cf9665 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -9,41 +9,115 @@ extern crate alloc; +use core::alloc::{AllocError, Allocator, GlobalAlloc, Layout}; use core::cell::UnsafeCell; use core::ffi::{c_char, c_void}; use core::mem::MaybeUninit; use core::num::NonZero; #[cfg(not(test))] use core::panic::PanicInfo; -use core::ptr; +use core::ptr::NonNull; +use core::{fmt, mem, ptr}; mod global; -mod tests; pub use global::*; mod shadow; -pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, i64) -> *mut c_void; +pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, u64) -> *mut c_void; pub type MUnmap = unsafe extern "C" fn(*mut c_void, usize) -> i32; pub type Malloc = unsafe extern "C" fn(usize) -> *mut c_void; pub type Free = unsafe extern "C" fn(*mut c_void); pub type Print = unsafe extern "C" fn(*const c_char); +pub type Exit = unsafe extern "C" fn() -> !; #[repr(C)] #[derive(Debug, Clone)] pub struct BsanHooks { - malloc: Malloc, - free: Free, + alloc: BsanAllocHooks, mmap: MMap, munmap: MUnmap, print: Print, + exit: Exit, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct BsanAllocHooks { + malloc: Malloc, + free: Free, +} + +unsafe impl Allocator for BsanAllocHooks { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + unsafe { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + // SAFETY: `layout` is non-zero in size, + size => unsafe { + let raw_ptr: *mut u8 = mem::transmute((self.malloc)(layout.size())); + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; + Ok(NonNull::slice_from_raw_parts(ptr, size)) + }, + } + } + } + + unsafe fn deallocate(&self, ptr: NonNull, _layout: Layout) { + (self.free)(mem::transmute(ptr.as_ptr())) + } } /// Unique identifier for an allocation -pub type AllocID = usize; +#[repr(transparent)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct AllocId(usize); + +impl AllocId { + pub fn new(i: usize) -> Self { + AllocId(i) + } + pub fn get(&self) -> usize { + self.0 + } + /// The minimum representable tag + pub const fn zero() -> Self { + AllocId(0) + } +} + +impl fmt::Debug for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { write!(f, "a{}", self.0) } else { write!(f, "alloc{}", self.0) } + } +} /// Unique identifier for a node within the tree -pub type BorTag = usize; +#[repr(transparent)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct BorTag(usize); + +impl BorTag { + pub fn new(i: usize) -> Self { + BorTag(i) + } + pub fn get(&self) -> usize { + self.0 + } + /// The minimum representable tag + pub const fn zero() -> Self { + BorTag(0) + } + pub const fn one() -> Self { + BorTag(1) + } +} + +impl fmt::Debug for BorTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "<{}>", self.0) + } +} /// Unique identifier for a source location. Every update to the tree /// is associated with a `Span`, which allows us to provide a detailed history @@ -56,7 +130,7 @@ pub type Span = usize; #[repr(C)] #[derive(Clone, Copy)] pub struct Provenance { - pub alloc_id: AllocID, + pub alloc_id: AllocId, pub bor_tag: BorTag, pub alloc_info: *mut c_void, } @@ -65,14 +139,22 @@ impl Provenance { /// The default provenance value, which is assigned to dangling or invalid /// pointers. const fn null() -> Self { - Provenance { alloc_id: 0, bor_tag: 0, alloc_info: core::ptr::null_mut() } + Provenance { + alloc_id: AllocId::zero(), + bor_tag: BorTag::zero(), + alloc_info: core::ptr::null_mut(), + } } /// Pointers cast from integers receive a "wildcard" provenance value, which permits /// any access. A provenance value with an `alloc_id` of zero and any non-zero `bor_tag` /// is treated as a wildcard provenance value. const fn wildcard() -> Self { - Provenance { alloc_id: 0, bor_tag: 1, alloc_info: core::ptr::null_mut() } + Provenance { + alloc_id: AllocId::zero(), + bor_tag: BorTag::one(), + alloc_info: core::ptr::null_mut(), + } } } @@ -83,7 +165,7 @@ impl Provenance { /// the tree for the allocation. #[repr(C)] struct AllocInfo { - pub alloc_id: usize, + pub alloc_id: AllocId, pub base_addr: usize, pub size: usize, pub align: usize, @@ -94,7 +176,7 @@ impl AllocInfo { /// When we deallocate an allocation, we need to invalidate its metadata. /// so that any uses-after-free are detectable. fn dealloc(&mut self) { - self.alloc_id = 0; + self.alloc_id = AllocId::zero(); self.base_addr = 0; self.size = 0; self.align = 1; @@ -139,6 +221,18 @@ extern "C" fn bsan_write(span: Span, prov: *const Provenance, addr: usize, acces debug_assert!(prov != ptr::null_mut()); } +/// Copies the provenance stored in the range `[src_addr, src_addr + access_size)` within the shadow heap +/// to the address `dst_addr`. This function will silently fail, so it should only be called in conjunction with +/// `bsan_read` and `bsan_write` or as part of an interceptor. +#[no_mangle] +extern "C" fn bsan_shadow_copy(dst_addr: usize, src_addr: usize, access_size: usize) {} + +/// Clears the provenance stored in the range `[dst_addr, dst_addr + access_size)` within the +/// shadow heap. This function will silently fail, so it should only be called in conjunction with +/// `bsan_read` and `bsan_write` or as part of an interceptor. +#[no_mangle] +extern "C" fn bsan_shadow_clear(addr: usize, access_size: usize) {} + /// Loads the provenance of a given address from shadow memory and stores /// the result in the return pointer. #[no_mangle] @@ -195,14 +289,6 @@ extern "C" fn bsan_expose_tag(prov: *const Provenance) { debug_assert!(prov != ptr::null_mut()); } -/// Copies provenance stored in the given range in shadow memory from the source to destination. -#[no_mangle] -extern "C" fn bsan_shadow_copy(dst: usize, src: usize, access_size: usize) {} - -/// Clears provenance from shadow memory starting at a given address. -#[no_mangle] -extern "C" fn bsan_shadow_clear(addr: usize, access_size: usize) {} - #[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo<'_>) -> ! { diff --git a/src/tools/bsan/bsan-rt/src/shadow.rs b/src/tools/bsan/bsan-rt/src/shadow.rs index 57c03f7cf336c..d170823d5fd2e 100644 --- a/src/tools/bsan/bsan-rt/src/shadow.rs +++ b/src/tools/bsan/bsan-rt/src/shadow.rs @@ -127,3 +127,11 @@ impl DerefMut for ShadowHeap { &mut self.l1 } } + +mod tests { + use super::*; + #[test] + fn create_and_drop() { + let _ = ShadowHeap::default(); + } +} From 206e1bfa8391414dbcc7bde448c31da33bacb084 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 27 Mar 2025 17:32:37 -0400 Subject: [PATCH 15/24] Fixed various syntax errors under #[cfg(test)]. --- src/tools/bsan/bsan-rt/src/global.rs | 39 +++++++++++++++------------- src/tools/bsan/bsan-rt/src/lib.rs | 2 +- src/tools/bsan/bsan-rt/src/tests.rs | 7 ----- 3 files changed, 22 insertions(+), 26 deletions(-) delete mode 100644 src/tools/bsan/bsan-rt/src/tests.rs diff --git a/src/tools/bsan/bsan-rt/src/global.rs b/src/tools/bsan/bsan-rt/src/global.rs index 2bedb541d2299..dea446becc4a4 100644 --- a/src/tools/bsan/bsan-rt/src/global.rs +++ b/src/tools/bsan/bsan-rt/src/global.rs @@ -55,7 +55,7 @@ impl GlobalCtx { /// Prints to stdout. macro_rules! print { ($ctx:expr, $($arg:tt)*) => {{ - ctx.print(core::format_args!($($arg)*)); + $ctx.print(core::format_args!($($arg)*)); }}; } pub(crate) use print; @@ -202,25 +202,8 @@ mod global_alloc { static GLOBAL_ALLOCATOR: DummyAllocator = DummyAllocator; } -#[cfg(not(test))] pub static GLOBAL_CTX: SyncUnsafeCell> = SyncUnsafeCell::new(None); -#[cfg(test)] -pub static TEST_HOOKS: BsanHooks = BsanHooks { - alloc: BsanAllocHooks { malloc: libc::malloc, free: libc::free }, - mmap: libc::mmap, - munmap: libc::munmap, - print: |ptr| unsafe { - println!(mem::transmute::<&str>(ptr)); - }, -}; - -/// A singleton instance of `GlobalCtx`. All API functions -/// rely on this state to be initialized. -#[cfg(test)] -pub static GLOBAL_CTX: SyncUnsafeCell> = - SyncUnsafeCell::new(Some(GlobalCtx::new(TEST_HOOKS))); - /// Initializes the global context object. /// This function must only be called once: when the program is first initialized. /// It is marked as `unsafe`, because it relies on the set of function pointers in @@ -247,3 +230,23 @@ pub unsafe fn deinit_global_ctx() { pub unsafe fn global_ctx() -> &'static GlobalCtx { (&(*GLOBAL_CTX.get())).as_ref().unwrap_unchecked() } + + +#[cfg(test)] +unsafe extern "C" fn test_print(ptr: *const c_char) { + std::println!("{}", CStr::from_ptr(ptr).to_str().expect("Invalid UTF-8")); +} + +#[cfg(test)] +unsafe extern "C" fn test_exit() -> !{ + std::process::exit(0); +} + +#[cfg(test)] +pub static TEST_HOOKS: BsanHooks = BsanHooks { + alloc: BsanAllocHooks { malloc: libc::malloc, free: libc::free }, + mmap: libc::mmap, + munmap: libc::munmap, + print: test_print, + exit: test_exit +}; \ No newline at end of file diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index b868b42cf9665..cc87aac88f661 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -24,7 +24,7 @@ pub use global::*; mod shadow; -pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, u64) -> *mut c_void; +pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, i64) -> *mut c_void; pub type MUnmap = unsafe extern "C" fn(*mut c_void, usize) -> i32; pub type Malloc = unsafe extern "C" fn(usize) -> *mut c_void; pub type Free = unsafe extern "C" fn(*mut c_void); diff --git a/src/tools/bsan/bsan-rt/src/tests.rs b/src/tools/bsan/bsan-rt/src/tests.rs deleted file mode 100644 index bdd58c89bc5aa..0000000000000 --- a/src/tools/bsan/bsan-rt/src/tests.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(test)] -use crate::*; - -#[test] -fn create_and_drop() { - let _ = ShadowHeap::default(); -} From 3192b8a5ea1e7dba078eea796ee1b1dfede37e93 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Thu, 27 Mar 2025 17:35:37 -0400 Subject: [PATCH 16/24] fmt --- src/tools/bsan/bsan-rt/src/global.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/bsan/bsan-rt/src/global.rs b/src/tools/bsan/bsan-rt/src/global.rs index dea446becc4a4..7eedaca737ec7 100644 --- a/src/tools/bsan/bsan-rt/src/global.rs +++ b/src/tools/bsan/bsan-rt/src/global.rs @@ -231,14 +231,13 @@ pub unsafe fn global_ctx() -> &'static GlobalCtx { (&(*GLOBAL_CTX.get())).as_ref().unwrap_unchecked() } - #[cfg(test)] unsafe extern "C" fn test_print(ptr: *const c_char) { std::println!("{}", CStr::from_ptr(ptr).to_str().expect("Invalid UTF-8")); } #[cfg(test)] -unsafe extern "C" fn test_exit() -> !{ +unsafe extern "C" fn test_exit() -> ! { std::process::exit(0); } @@ -248,5 +247,5 @@ pub static TEST_HOOKS: BsanHooks = BsanHooks { mmap: libc::mmap, munmap: libc::munmap, print: test_print, - exit: test_exit -}; \ No newline at end of file + exit: test_exit, +}; From 06a66dab14f62ce74245360c2ecb946119369ea2 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Fri, 28 Mar 2025 11:09:08 -0400 Subject: [PATCH 17/24] Fixed signed-ness of OFF_T using a wrapper for mmap. --- src/tools/bsan/bsan-rt/src/global.rs | 46 +++++++++++++++++++--------- src/tools/bsan/bsan-rt/src/lib.rs | 2 +- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/tools/bsan/bsan-rt/src/global.rs b/src/tools/bsan/bsan-rt/src/global.rs index 7eedaca737ec7..8aa9d7c02ed74 100644 --- a/src/tools/bsan/bsan-rt/src/global.rs +++ b/src/tools/bsan/bsan-rt/src/global.rs @@ -232,20 +232,36 @@ pub unsafe fn global_ctx() -> &'static GlobalCtx { } #[cfg(test)] -unsafe extern "C" fn test_print(ptr: *const c_char) { - std::println!("{}", CStr::from_ptr(ptr).to_str().expect("Invalid UTF-8")); -} +mod test { + use crate::*; -#[cfg(test)] -unsafe extern "C" fn test_exit() -> ! { - std::process::exit(0); -} + unsafe extern "C" fn test_print(ptr: *const c_char) { + std::println!("{}", std::ffi::CStr::from_ptr(ptr).to_str().expect("Invalid UTF-8")); + } -#[cfg(test)] -pub static TEST_HOOKS: BsanHooks = BsanHooks { - alloc: BsanAllocHooks { malloc: libc::malloc, free: libc::free }, - mmap: libc::mmap, - munmap: libc::munmap, - print: test_print, - exit: test_exit, -}; + unsafe extern "C" fn test_exit() -> ! { + std::process::exit(0); + } + + unsafe extern "C" fn test_mmap( + addr: *mut c_void, + len: usize, + prot: i32, + flags: i32, + fd: i32, + offset: u64, + ) -> *mut c_void { + // LLVM's sanitizer API uses u64 for OFF_T, but libc uses i64 + // We use this wrapper function to avoid having to manually update + // the bindings. + libc::mmap(addr, len, prot, flags, fd, offset as i64) + } + + pub static TEST_HOOKS: BsanHooks = BsanHooks { + alloc: BsanAllocHooks { malloc: libc::malloc, free: libc::free }, + mmap: test_mmap, + munmap: libc::munmap, + print: test_print, + exit: test_exit, + }; +} diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index cc87aac88f661..b868b42cf9665 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -24,7 +24,7 @@ pub use global::*; mod shadow; -pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, i64) -> *mut c_void; +pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, u64) -> *mut c_void; pub type MUnmap = unsafe extern "C" fn(*mut c_void, usize) -> i32; pub type Malloc = unsafe extern "C" fn(usize) -> *mut c_void; pub type Free = unsafe extern "C" fn(*mut c_void); From 503d0b98cca31c6d49da87856b1cf6fc6afbf218 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Fri, 28 Mar 2025 12:33:28 -0400 Subject: [PATCH 18/24] Fixed cast formatting. --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 14521f6ad81d1..7e2acb2c22a9a 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 14521f6ad81d1f491d63dc33ef6e5e7b591f02e9 +Subproject commit 7e2acb2c22a9aa856ae2b50ab59dd2d706d1ae4d From 7c9c3df5a9665d88d0d794fd6947de61427cf860 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Fri, 28 Mar 2025 13:22:30 -0400 Subject: [PATCH 19/24] Update deps. --- Cargo.lock | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a2e89ed548c0..1daa8ddcbc245 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,17 +255,6 @@ dependencies = [ "serde", ] -[[package]] -name = "biri" -version = "0.1.0" -dependencies = [ - "colored", - "regex", - "rustc_version", - "tempfile", - "ui_test", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -404,19 +393,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cargo-biri" -version = "0.1.0" -dependencies = [ - "cargo_metadata 0.18.1", - "directories 5.0.1", - "rustc-build-sysroot", - "rustc_tools_util", - "rustc_version", - "serde", - "serde_json", -] - [[package]] name = "cargo-bsan" version = "0.1.0" From 3ee52b729e185829089762fa50c6ae760c42ab8c Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Sat, 29 Mar 2025 21:37:43 -0400 Subject: [PATCH 20/24] Switched to c_ulonglong in mmap. --- src/tools/bsan/bsan-rt/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index b868b42cf9665..0faf5d72a8c71 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -8,10 +8,9 @@ #![allow(unused)] extern crate alloc; - use core::alloc::{AllocError, Allocator, GlobalAlloc, Layout}; use core::cell::UnsafeCell; -use core::ffi::{c_char, c_void}; +use core::ffi::{c_char, c_ulonglong, c_void}; use core::mem::MaybeUninit; use core::num::NonZero; #[cfg(not(test))] @@ -24,7 +23,7 @@ pub use global::*; mod shadow; -pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, u64) -> *mut c_void; +pub type MMap = unsafe extern "C" fn(*mut c_void, usize, i32, i32, i32, c_ulonglong) -> *mut c_void; pub type MUnmap = unsafe extern "C" fn(*mut c_void, usize) -> i32; pub type Malloc = unsafe extern "C" fn(usize) -> *mut c_void; pub type Free = unsafe extern "C" fn(*mut c_void); From 96864e9449d87088ee685d4cc9c40eaf1d9631d0 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Sat, 29 Mar 2025 21:40:53 -0400 Subject: [PATCH 21/24] Added GlobalCtx::exit() --- src/tools/bsan/bsan-rt/src/global.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/bsan/bsan-rt/src/global.rs b/src/tools/bsan/bsan-rt/src/global.rs index 8aa9d7c02ed74..ef2b6ab08e594 100644 --- a/src/tools/bsan/bsan-rt/src/global.rs +++ b/src/tools/bsan/bsan-rt/src/global.rs @@ -40,6 +40,10 @@ impl GlobalCtx { self.hooks.alloc } + fn exit(&self) -> ! { + unsafe { (self.hooks.exit)() } + } + /// Prints a given set of formatted arguments. This function is not meant /// to be called directly; instead, it should be used with the `print!`, /// `println!`, and `ui_test!` macros. From 995a1e35428d2cd8ed124371ecad2b96a1e953b4 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Mon, 31 Mar 2025 18:49:16 +0000 Subject: [PATCH 22/24] Updated submodule and fmt. --- src/llvm-project | 2 +- src/tools/bsan/bsan-rt/src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/llvm-project b/src/llvm-project index 7e2acb2c22a9a..5c3457300084a 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 7e2acb2c22a9aa856ae2b50ab59dd2d706d1ae4d +Subproject commit 5c3457300084a1c47d3d555b24984a7a55a2fcb1 diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index 0faf5d72a8c71..07b7983f9c451 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -260,7 +260,6 @@ extern "C" fn bsan_pop_frame(span: Span) {} #[no_mangle] extern "C" fn bsan_alloc(span: Span, prov: *mut MaybeUninit, addr: usize) { debug_assert!(prov != ptr::null_mut()); - unsafe { (*prov).write(Provenance::null()); } From 39c26bb45c03751f0cf6131dab2ddeb07a805da9 Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Mon, 31 Mar 2025 19:00:14 +0000 Subject: [PATCH 23/24] fmt --- src/tools/bsan/bsan-rt/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index 07b7983f9c451..235960e59b819 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -260,6 +260,7 @@ extern "C" fn bsan_pop_frame(span: Span) {} #[no_mangle] extern "C" fn bsan_alloc(span: Span, prov: *mut MaybeUninit, addr: usize) { debug_assert!(prov != ptr::null_mut()); + unsafe { (*prov).write(Provenance::null()); } @@ -269,6 +270,7 @@ extern "C" fn bsan_alloc(span: Span, prov: *mut MaybeUninit, addr: u #[no_mangle] extern "C" fn bsan_alloc_stack(span: Span, prov: *mut MaybeUninit, size: usize) { debug_assert!(prov != ptr::null_mut()); + unsafe { (*prov).write(Provenance::null()); } From 17c3ae7b090815ab335f50a1cc307b495f2812fa Mon Sep 17 00:00:00 2001 From: Ian McCormack Date: Tue, 1 Apr 2025 10:55:03 -0400 Subject: [PATCH 24/24] Switched to using dedicated alloc ID values for provenance. --- src/tools/bsan/bsan-rt/src/lib.rs | 40 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/tools/bsan/bsan-rt/src/lib.rs b/src/tools/bsan/bsan-rt/src/lib.rs index 235960e59b819..7cbbc956b4981 100644 --- a/src/tools/bsan/bsan-rt/src/lib.rs +++ b/src/tools/bsan/bsan-rt/src/lib.rs @@ -79,10 +79,24 @@ impl AllocId { pub fn get(&self) -> usize { self.0 } - /// The minimum representable tag - pub const fn zero() -> Self { + /// An invalid allocation + pub const fn null() -> Self { AllocId(0) } + + /// Represents any valid allocation + pub const fn wildcard() -> Self { + AllocId(1) + } + + /// A global or stack allocation, which cannot be manually freed + pub const fn sticky() -> Self { + AllocId(2) + } + + pub const fn min() -> Self { + AllocId(3) + } } impl fmt::Debug for AllocId { @@ -97,19 +111,12 @@ impl fmt::Debug for AllocId { pub struct BorTag(usize); impl BorTag { - pub fn new(i: usize) -> Self { + pub const fn new(i: usize) -> Self { BorTag(i) } pub fn get(&self) -> usize { self.0 } - /// The minimum representable tag - pub const fn zero() -> Self { - BorTag(0) - } - pub const fn one() -> Self { - BorTag(1) - } } impl fmt::Debug for BorTag { @@ -139,19 +146,18 @@ impl Provenance { /// pointers. const fn null() -> Self { Provenance { - alloc_id: AllocId::zero(), - bor_tag: BorTag::zero(), + alloc_id: AllocId::null(), + bor_tag: BorTag::new(0), alloc_info: core::ptr::null_mut(), } } /// Pointers cast from integers receive a "wildcard" provenance value, which permits - /// any access. A provenance value with an `alloc_id` of zero and any non-zero `bor_tag` - /// is treated as a wildcard provenance value. + /// any access. const fn wildcard() -> Self { Provenance { - alloc_id: AllocId::zero(), - bor_tag: BorTag::one(), + alloc_id: AllocId::wildcard(), + bor_tag: BorTag::new(0), alloc_info: core::ptr::null_mut(), } } @@ -175,7 +181,7 @@ impl AllocInfo { /// When we deallocate an allocation, we need to invalidate its metadata. /// so that any uses-after-free are detectable. fn dealloc(&mut self) { - self.alloc_id = AllocId::zero(); + self.alloc_id = AllocId::null(); self.base_addr = 0; self.size = 0; self.align = 1;