Skip to content

Commit 7452471

Browse files
chore(fuzzing): enable canister sandboxing for fuzzers (#2513)
- Add a new helper lib for fuzzers to invoke canister sandbox `//rs/execution_environment/fuzz:fuzzer_sandbox` - Adapt existing fuzzers to use the `fuzzer_sandbox`
1 parent 6d94310 commit 7452471

File tree

5 files changed

+98
-69
lines changed

5 files changed

+98
-69
lines changed

rs/canister_sandbox/src/replica_controller/process_exe_and_args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const RUNNABLE_AS_SANDBOX: &[&str] = &[
3131
// need a different approach to enable multiple fuzzers use this
3232
// approach. The logic can be gated with #[cfg(feature = "fuzzing_code")]
3333
"execute_with_wasm_executor_system_api_call",
34+
"execute_subnet_message_update_settings",
3435
];
3536

3637
enum SandboxCrate {

rs/execution_environment/fuzz/BUILD.bazel

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,47 @@
1-
load("//bazel:fuzz_testing.bzl", "rust_fuzz_test_binary")
1+
load("@rules_rust//rust:defs.bzl", "rust_library")
2+
load("//bazel:fuzz_testing.bzl", "DEFAULT_RUSTC_FLAGS_FOR_FUZZING", "rust_fuzz_test_binary")
23

34
package(default_visibility = ["//visibility:private"])
45

56
MACRO_DEPENDENCIES = []
67

8+
rust_library(
9+
name = "fuzzer_sandbox",
10+
testonly = True,
11+
srcs = glob(["src/*.rs"]),
12+
crate_features = select({
13+
"//bazel:fuzzing_code_enabled": ["fuzzing_code"],
14+
"//conditions:default": [],
15+
}),
16+
proc_macro_deps = MACRO_DEPENDENCIES,
17+
rustc_flags = select({
18+
"//bazel:fuzzing_code_enabled": DEFAULT_RUSTC_FLAGS_FOR_FUZZING,
19+
"//conditions:default": [],
20+
}),
21+
version = "0.1.0",
22+
deps = [
23+
"//rs/canister_sandbox:backend_lib",
24+
"@crate_index//:libfuzzer-sys",
25+
],
26+
)
27+
728
rust_fuzz_test_binary(
829
name = "execute_subnet_message_update_settings",
930
srcs = [
1031
"fuzz_targets/execute_subnet_message_update_settings.rs",
1132
],
33+
allow_main = True, # To allow the fuzzer to export it's own main fn
1234
proc_macro_deps = MACRO_DEPENDENCIES,
1335
deps = [
1436
# Keep sorted.
1537
"//rs/test_utilities/execution_environment",
1638
"//rs/types/management_canister_types",
1739
"@crate_index//:libfuzzer-sys",
18-
],
40+
] + [":fuzzer_sandbox"],
1941
)
2042

2143
SYSTEM_API_FUZZ_DEPENDENCIES = [
2244
# Keep sorted.
23-
"//rs/canister_sandbox:backend_lib",
2445
"//rs/config",
2546
"//rs/embedders/fuzz:wasm_fuzzers",
2647
"//rs/registry/subnet_type",
@@ -40,5 +61,5 @@ rust_fuzz_test_binary(
4061
],
4162
allow_main = True, # To allow the fuzzer to export it's own main fn
4263
proc_macro_deps = MACRO_DEPENDENCIES,
43-
deps = SYSTEM_API_FUZZ_DEPENDENCIES,
64+
deps = SYSTEM_API_FUZZ_DEPENDENCIES + [":fuzzer_sandbox"],
4465
)

rs/execution_environment/fuzz/fuzz_targets/execute_subnet_message_update_settings.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![no_main]
21
use ic_management_canister_types::{Method, Payload, UpdateSettingsArgs};
32
use ic_test_utilities_execution_environment::ExecutionTestBuilder;
43
use libfuzzer_sys::{fuzz_target, Corpus};
@@ -7,14 +6,16 @@ use libfuzzer_sys::{fuzz_target, Corpus};
76
//
87
// The fuzz test is only compiled but not executed by CI.
98
//
10-
// To execute the fuzzer run
11-
// bazel run --config=fuzzing //rs/execution_environment/fuzz:execute_subnet_message_update_settings
9+
// ASAN_OPTIONS="detect_leaks=0:allow_user_segv_handler=1:handle_segv=1:handle_sigfpe=1:handle_sigill=0:quarantine_size_mb=16"
10+
// LSAN_OPTIONS="handle_sigill=0"
11+
// ASAN_OPTIONS=$ASAN_OPTIONS LSAN_OPTIONS=$LSAN_OPTIONS bazel run --config=fuzzing //rs/execution_environment/fuzz:execute_subnet_message_update_settings
12+
13+
fn main() {
14+
fuzzer_sandbox::fuzzer_main();
15+
}
1216

1317
fuzz_target!(|args: UpdateSettingsArgs| -> Corpus {
14-
let mut test = ExecutionTestBuilder::new()
15-
.with_deterministic_time_slicing_disabled()
16-
.with_canister_sandboxing_disabled()
17-
.build();
18+
let mut test = ExecutionTestBuilder::new().build();
1819

1920
let wat = r#"(module)"#;
2021
let canister_id = test.canister_from_wat(wat).unwrap();

rs/execution_environment/fuzz/fuzz_targets/execute_system_api_call.rs

Lines changed: 3 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
use ic_canister_sandbox_backend_lib::{
2-
canister_sandbox_main, compiler_sandbox::compiler_sandbox_main,
3-
launcher::sandbox_launcher_main, RUN_AS_CANISTER_SANDBOX_FLAG, RUN_AS_COMPILER_SANDBOX_FLAG,
4-
RUN_AS_SANDBOX_LAUNCHER_FLAG,
5-
};
61
use ic_config::{
72
embedders::Config as EmbeddersConfig, embedders::FeatureFlags,
83
execution_environment::Config as ExecutionConfig, flag_status::FlagStatus,
@@ -13,11 +8,9 @@ use ic_registry_subnet_type::SubnetType;
138
use ic_state_machine_tests::{StateMachine, StateMachineBuilder, StateMachineConfig};
149
use ic_types::{CanisterId, Cycles, NumBytes};
1510

16-
use libfuzzer_sys::{fuzz_target, test_input_wrap};
11+
use libfuzzer_sys::fuzz_target;
1712
use slog::Level;
1813
use std::cell::RefCell;
19-
use std::ffi::CString;
20-
use std::os::raw::c_char;
2114
use wasm_fuzzers::ic_wasm::ICWasmModule;
2215

2316
thread_local! {
@@ -30,61 +23,13 @@ const HELLO_WORLD_WAT: &str = r#"
3023
(export "canister_query hi" (func $hi))
3124
)"#;
3225

33-
#[allow(improper_ctypes)]
34-
extern "C" {
35-
fn LLVMFuzzerRunDriver(
36-
argc: *const isize,
37-
argv: *const *const *const u8,
38-
UserCb: fn(data: *const u8, size: usize) -> i32,
39-
) -> i32;
40-
}
41-
42-
// In general, fuzzers don't include `main()` and the initialisation logic is deferred to libfuzzer.
43-
// However, to enable canister sandboxing, we override the initialisation by providing our own `main()`
44-
// which acts as a dispatcher for different sandboxed under certain arguments.
45-
//
46-
// The default case invokes `LLVMFuzzerRunDriver` which invokes a callback with similar signature as
47-
// `LLVMFuzzerTestOneInput`. For more details, see https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library
48-
//
49-
// We provide `libfuzzer_sys::test_input_wrap` as callback for `LLVMFuzzerRunDriver` since libfuzzer_sys
50-
// already exports `LLVMFuzzerTestOneInput` and we can't override it. `test_input_wrap` internally calls
51-
// `rust_fuzzer_test_input`, which is generated via the macro `fuzz_target!`.
52-
// See https://github.com/rust-fuzz/libfuzzer/blob/c8275d1517933765b56a6de61a371bb1cc4268cb/src/lib.rs#L62
53-
5426
// To run the fuzzer,
55-
// ASAN_OPTIONS="detect_leaks=0:allow_user_segv_handler=1:handle_segv=1:handle_sigfpe=1:handle_sigill=0"
27+
// ASAN_OPTIONS="detect_leaks=0:allow_user_segv_handler=1:handle_segv=1:handle_sigfpe=1:handle_sigill=0:quarantine_size_mb=16"
5628
// LSAN_OPTIONS="handle_sigill=0"
5729
// ASAN_OPTIONS=$ASAN_OPTIONS LSAN_OPTIONS=$LSAN_OPTIONS bazel run --config=fuzzing //rs/execution_environment/fuzz:execute_with_wasm_executor_system_api_call
5830

5931
fn main() {
60-
if std::env::args().any(|arg| arg == RUN_AS_CANISTER_SANDBOX_FLAG) {
61-
#[cfg(not(fuzzing))]
62-
canister_sandbox_main();
63-
} else if std::env::args().any(|arg| arg == RUN_AS_SANDBOX_LAUNCHER_FLAG) {
64-
#[cfg(not(fuzzing))]
65-
sandbox_launcher_main();
66-
} else if std::env::args().any(|arg| arg == RUN_AS_COMPILER_SANDBOX_FLAG) {
67-
#[cfg(not(fuzzing))]
68-
compiler_sandbox_main();
69-
} else {
70-
// Collect command-line arguments
71-
let args: Vec<CString> = std::env::args()
72-
.map(|arg| CString::new(arg).unwrap())
73-
.collect();
74-
75-
// Prepare argc as *const isize
76-
let argc = args.len() as isize;
77-
let argc: *const isize = &argc;
78-
79-
// Prepare argv as *const *const *const u8
80-
let argv: Vec<*const c_char> = args.iter().map(|arg| arg.as_ptr()).collect();
81-
let argv_ptr: *const *const u8 = argv.as_ptr() as *const *const u8;
82-
let argv: *const *const *const u8 = &argv_ptr;
83-
84-
unsafe {
85-
LLVMFuzzerRunDriver(argc, argv, test_input_wrap);
86-
}
87-
}
32+
fuzzer_sandbox::fuzzer_main();
8833
}
8934

9035
fuzz_target!(|data: ICWasmModule| {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use ic_canister_sandbox_backend_lib::{
2+
canister_sandbox_main, compiler_sandbox::compiler_sandbox_main,
3+
launcher::sandbox_launcher_main, RUN_AS_CANISTER_SANDBOX_FLAG, RUN_AS_COMPILER_SANDBOX_FLAG,
4+
RUN_AS_SANDBOX_LAUNCHER_FLAG,
5+
};
6+
use libfuzzer_sys::test_input_wrap;
7+
use std::ffi::CString;
8+
use std::os::raw::c_char;
9+
10+
#[allow(improper_ctypes)]
11+
extern "C" {
12+
fn LLVMFuzzerRunDriver(
13+
argc: *const isize,
14+
argv: *const *const *const u8,
15+
UserCb: fn(data: *const u8, size: usize) -> i32,
16+
) -> i32;
17+
18+
}
19+
20+
// In general, fuzzers don't include `main()` and the initialisation logic is deferred to libfuzzer.
21+
// However, to enable canister sandboxing, we override the initialisation by providing our own `main()`
22+
// which acts as a dispatcher for different sandboxed under certain arguments.
23+
//
24+
// The default case invokes `LLVMFuzzerRunDriver` which invokes a callback with similar signature as
25+
// `LLVMFuzzerTestOneInput`. For more details, see https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library
26+
//
27+
// We provide `libfuzzer_sys::test_input_wrap` as callback for `LLVMFuzzerRunDriver` since libfuzzer_sys
28+
// already exports `LLVMFuzzerTestOneInput` and we can't override it. `test_input_wrap` internally calls
29+
// `rust_fuzzer_test_input`, which is generated via the macro `fuzz_target!`.
30+
// See https://github.com/rust-fuzz/libfuzzer/blob/c8275d1517933765b56a6de61a371bb1cc4268cb/src/lib.rs#L62
31+
32+
pub fn fuzzer_main() {
33+
if std::env::args().any(|arg| arg == RUN_AS_CANISTER_SANDBOX_FLAG) {
34+
#[cfg(not(fuzzing))]
35+
canister_sandbox_main();
36+
} else if std::env::args().any(|arg| arg == RUN_AS_SANDBOX_LAUNCHER_FLAG) {
37+
#[cfg(not(fuzzing))]
38+
sandbox_launcher_main();
39+
} else if std::env::args().any(|arg| arg == RUN_AS_COMPILER_SANDBOX_FLAG) {
40+
#[cfg(not(fuzzing))]
41+
compiler_sandbox_main();
42+
} else {
43+
// Collect command-line arguments
44+
let args: Vec<CString> = std::env::args()
45+
.map(|arg| CString::new(arg).unwrap())
46+
.collect();
47+
48+
// Prepare argc as *const isize
49+
let argc = args.len() as isize;
50+
let argc: *const isize = &argc;
51+
52+
// Prepare argv as *const *const *const u8
53+
let argv: Vec<*const c_char> = args.iter().map(|arg| arg.as_ptr()).collect();
54+
let argv_ptr: *const *const u8 = argv.as_ptr() as *const *const u8;
55+
let argv: *const *const *const u8 = &argv_ptr;
56+
57+
unsafe {
58+
LLVMFuzzerRunDriver(argc, argv, test_input_wrap);
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)