From 0302651274577658a9bf19d4183d3b78fae93820 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 8 Sep 2026 12:55:47 -0700 Subject: [PATCH] Add support for marking functions as safe In the same way that `Builder::allowlist_function()` and the command line `--allowlist-function` option permit marking some function as to-be-generated, the new `Builder::safe_function()` method and `--safe-functions` option allow marking functions as safe. Fixes #3443 --- .../tests/expectations/tests/unsafe-functions.rs | 16 ++++++++++++++++ bindgen-tests/tests/headers/unsafe-functions.h | 7 +++++++ bindgen/codegen/mod.rs | 12 +++++++++--- bindgen/lib.rs | 4 +++- bindgen/options/cli.rs | 5 +++++ bindgen/options/mod.rs | 13 +++++++++++++ 6 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 bindgen-tests/tests/expectations/tests/unsafe-functions.rs create mode 100644 bindgen-tests/tests/headers/unsafe-functions.h diff --git a/bindgen-tests/tests/expectations/tests/unsafe-functions.rs b/bindgen-tests/tests/expectations/tests/unsafe-functions.rs new file mode 100644 index 0000000000..abf53dc319 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/unsafe-functions.rs @@ -0,0 +1,16 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +unsafe extern "C" { + pub safe fn is_zero(n: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn is_zero_ptr(n: *mut ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub safe fn is_number_5(n: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub safe fn is_number_42(n: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn is_number_other_than_8(n: ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} diff --git a/bindgen-tests/tests/headers/unsafe-functions.h b/bindgen-tests/tests/headers/unsafe-functions.h new file mode 100644 index 0000000000..1fcd053c60 --- /dev/null +++ b/bindgen-tests/tests/headers/unsafe-functions.h @@ -0,0 +1,7 @@ +// bindgen-flags: --safe-functions is_zero --safe-functions '^is_number_\d+$' + +int is_zero(int n); +int is_zero_ptr(int *n); +int is_number_5(int n); +int is_number_42(int n); +int is_number_other_than_8(int n); diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 11bb26a021..aa79500dd6 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4921,19 +4921,25 @@ impl CodeGenerator for Function { }; let ret = utils::fnsig_return_ty(ctx, signature); - let ident = ctx.rust_ident(ident); - let safety = ctx .options() .rust_features .unsafe_extern_blocks .then(|| quote!(unsafe)); + let mark_fn_safe = ctx + .options() + .safe_functions + .matches(ident) + .then(|| quote!(safe)); + + let ident = ctx.rust_ident(ident); + let tokens = quote! { #block_attributes #safety extern #abi { #(#attributes)* - pub fn #ident ( #( #args ),* ) #ret; + pub #mark_fn_safe fn #ident ( #( #args ),* ) #ret; } }; diff --git a/bindgen/lib.rs b/bindgen/lib.rs index f5b7326e3e..1a6a3cad76 100644 --- a/bindgen/lib.rs +++ b/bindgen/lib.rs @@ -471,7 +471,7 @@ impl Builder { impl BindgenOptions { fn build(&mut self) { - const REGEX_SETS_LEN: usize = 29; + const REGEX_SETS_LEN: usize = 30; let regex_sets: [_; REGEX_SETS_LEN] = [ &mut self.blocklisted_types, @@ -503,6 +503,7 @@ impl BindgenOptions { &mut self.no_default_types, &mut self.no_hash_types, &mut self.must_use_types, + &mut self.safe_functions, ]; let record_matches = self.record_matches; @@ -540,6 +541,7 @@ impl BindgenOptions { "--no-default", "--no-hash", "--must-use", + "--safe-functions", ]) .chain((0..self.abi_overrides.len()).map(|_| "--override-abi")) .map(Some) diff --git a/bindgen/options/cli.rs b/bindgen/options/cli.rs index 18b16cfcba..af3d99de08 100644 --- a/bindgen/options/cli.rs +++ b/bindgen/options/cli.rs @@ -496,6 +496,9 @@ struct BindgenCommand { /// Wrap unsafe operations in unsafe blocks. #[arg(long)] wrap_unsafe_ops: bool, + /// Mark functions matching REGEX as `safe`. + #[arg(long, value_name = "REGEX")] + safe_functions: Vec, /// Enable fallback for clang macro parsing. #[arg(long)] clang_macro_fallback: bool, @@ -694,6 +697,7 @@ where merge_extern_blocks, override_abi, wrap_unsafe_ops, + safe_functions, clang_macro_fallback, clang_macro_fallback_build_dir, flexarray_dst, @@ -1000,6 +1004,7 @@ where merge_extern_blocks, override_abi => |b, (abi, regex)| b.override_abi(abi, regex), wrap_unsafe_ops, + safe_functions => Builder::safe_function, clang_macro_fallback => |b, _| b.clang_macro_fallback(), clang_macro_fallback_build_dir, flexarray_dst, diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs index bc0cb75a33..9586819b13 100644 --- a/bindgen/options/mod.rs +++ b/bindgen/options/mod.rs @@ -2107,6 +2107,19 @@ options! { }, as_args: "--wrap-unsafe-ops", }, + /// Functions that should be marked as `safe`. + safe_functions: RegexSet { + methods: { + regex_option! { + /// Mark the matching function as `safe`. + pub fn safe_function>(mut self, arg: T) -> Builder { + self.options.safe_functions.insert(arg); + self + } + } + }, + as_args: "--safe-functions", + }, /// Use DSTs to represent structures with flexible array members. flexarray_dst: bool { methods: {