From 5d44797290c474b6c58136f67fbba4a2b890c1f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Oct 2026 13:01:44 +0000 Subject: [PATCH 1/3] Bump sqlcipher-src in /fuzz Bumps [sqlcipher-src](https://github.com/LucaCappelletti94/sqlcipher-src) from 419.0.4+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2 to 419.0.5+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2. - [Release notes](https://github.com/LucaCappelletti94/sqlcipher-src/releases) - [Commits](https://github.com/LucaCappelletti94/sqlcipher-src/compare/v419.0.4...v419.0.5) --- updated-dependencies: - dependency-name: sqlcipher-src dependency-version: 419.0.5+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- fuzz/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index b81118f..e850b63 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -115,9 +115,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "sqlcipher-src" -version = "419.0.4+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2" +version = "419.0.5+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d983a7f69ae93370d5101e82b9442dce8c4e510977d97374c3e668d6dff32d08" +checksum = "426c6ad3a422e1c3367060737b9590d7ab56459829535f9ad843979b548b2acb" [[package]] name = "sqlite3mc-src" From cef67b8b146f7887b2f4d38bc79b14ee661f230b Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Thu, 15 Oct 2026 18:07:56 +0200 Subject: [PATCH 2/3] Build SQLCipher from sqlcipher-src 419.0.6's separate libtomcrypt sources --- fuzz/Cargo.lock | 4 +- fuzz/build.rs | 144 +++++++++++++++++++++++++++++++-------- fuzz/sqlcipher-entropy.c | 24 +++++++ fuzz/sqlcipher-ltc.h | 29 ++++++++ fuzz/sqlcipher-wrapper.c | 17 +++++ fuzz/src/entropy.rs | 2 +- fuzz/src/lib.rs | 8 +++ 7 files changed, 198 insertions(+), 30 deletions(-) create mode 100644 fuzz/sqlcipher-entropy.c create mode 100644 fuzz/sqlcipher-ltc.h create mode 100644 fuzz/sqlcipher-wrapper.c diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index e850b63..79d7a71 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -115,9 +115,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "sqlcipher-src" -version = "419.0.5+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2" +version = "419.0.6+sqlcipher-4.19.0-sqlite-3.53.4-libtomcrypt-1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426c6ad3a422e1c3367060737b9590d7ab56459829535f9ad843979b548b2acb" +checksum = "ab732d48ebf2a1449e94d071b26544f99845834c75c3c210e927ace097b49aa4" [[package]] name = "sqlite3mc-src" diff --git a/fuzz/build.rs b/fuzz/build.rs index 1d3a5a9..8041b8c 100644 --- a/fuzz/build.rs +++ b/fuzz/build.rs @@ -8,11 +8,17 @@ use std::{ process::Command, }; +/// One compiler invocation of a side, its objects merge before the rename. +struct Unit<'a> { + files: &'a [PathBuf], + includes: &'a [PathBuf], + defines: &'a [(&'a str, Option<&'a str>)], + forced_include: Option<&'a str>, +} + struct Side<'a> { prefix: &'a str, - source: PathBuf, - include: &'a Path, - defines: &'a [(&'a str, Option<&'a str>)], + units: &'a [Unit<'a>], } fn main() { @@ -22,29 +28,60 @@ fn main() { "the symbol renaming needs ELF objects and GNU-compatible nm and objcopy, so the fuzz crate builds on Linux only" ); let out = PathBuf::from(env::var_os("OUT_DIR").expect("cargo sets OUT_DIR")); - let mc_dir = sqlite3mc_src::source_dir(); - let sc_dir = sqlcipher_src::source_dir(); + let crate_dir = + PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("cargo sets CARGO_MANIFEST_DIR")); + let mc_dir = sqlite3mc_src::source_dir().to_path_buf(); + let sc_dir = sqlcipher_src::source_dir().to_path_buf(); + let ltc_dir = sc_dir.join(sqlcipher_src::LIBTOMCRYPT_INCLUDE_DIR); let common = [ ("SQLITE_TEMP_STORE", Some("2")), ("SQLITE_ENABLE_DBPAGE_VTAB", None), ("SQLITE_DEFAULT_MEMSTATUS", Some("0")), ]; - let mc_defines = [common.as_slice(), &[("SQLITE_CORE", None)]].concat(); + let mc_defines: Vec<(&str, Option<&str>)> = + [common.as_slice(), &[("SQLITE_CORE", None)]].concat(); + let ltc_files: Vec = std::iter::once("sqlcipher-entropy.c") + .map(|name| crate_dir.join(name)) + .chain( + sqlcipher_src::LIBTOMCRYPT_SOURCES + .iter() + .map(|source| sc_dir.join(source)), + ) + .collect(); + let mc_files = [mc_dir.join(sqlite3mc_src::SOURCE_FILE)]; + let mc_includes = [mc_dir]; + let wrapper_files = [crate_dir.join("sqlcipher-wrapper.c")]; + let wrapper_includes = [sc_dir, ltc_dir.clone()]; + let ltc_includes = [ltc_dir, crate_dir]; build( &Side { prefix: "mc", - source: mc_dir.join(sqlite3mc_src::SOURCE_FILE), - include: mc_dir, - defines: &mc_defines, + units: &[Unit { + files: &mc_files, + includes: &mc_includes, + defines: &mc_defines, + forced_include: None, + }], }, &out, ); build( &Side { prefix: "sc", - source: sc_dir.join(sqlcipher_src::WASM_SOURCE_FILE), - include: sc_dir, - defines: &common, + units: &[ + Unit { + files: &wrapper_files, + includes: &wrapper_includes, + defines: &common, + forced_include: None, + }, + Unit { + files: <c_files, + includes: <c_includes, + defines: &[("LTC_SOURCE", None)], + forced_include: Some("sqlcipher-ltc.h"), + }, + ], }, &out, ); @@ -53,38 +90,80 @@ fn main() { println!("cargo:rustc-link-lib={lib}"); } println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=sqlcipher-wrapper.c"); + println!("cargo:rerun-if-changed=sqlcipher-entropy.c"); + println!("cargo:rerun-if-changed=sqlcipher-ltc.h"); println!("cargo:rerun-if-env-changed=NM"); println!("cargo:rerun-if-env-changed=OBJCOPY"); + println!("cargo:rerun-if-env-changed=LD"); + println!("cargo:rerun-if-env-changed=AR"); } fn build(side: &Side<'_>, out: &Path) { - println!("cargo:rerun-if-changed={}", side.include.display()); - let mut cc = cc::Build::new(); - cc.file(&side.source).include(side.include).warnings(false); - for (name, value) in side.defines { - cc.define(name, *value); - } - instrument(&mut cc); - let objects = cc.compile_intermediates(); - let [object] = objects.as_slice() else { - panic!( + let mut objects = Vec::new(); + for unit in side.units { + for file in unit.files { + println!("cargo:rerun-if-changed={}", file.display()); + } + let mut cc = cc::Build::new(); + for file in unit.files { + cc.file(file); + } + for dir in unit.includes { + cc.include(dir); + } + cc.warnings(false); + for (name, value) in unit.defines { + cc.define(name, *value); + } + if let Some(header) = unit.forced_include { + cc.flag("-include").flag(header); + } + instrument(&mut cc); + let unit_objects = cc.compile_intermediates(); + assert_eq!( + unit_objects.len(), + unit.files.len(), "{} compiled to {} objects", - side.source.display(), - objects.len() + unit.files[0].display(), + unit_objects.len() ); + objects.extend(unit_objects); + } + + let combined = if objects.len() == 1 { + objects.remove(0) + } else { + // Clang's .group sections break the final link once a partial link merges them. + let linked: Vec = objects + .iter() + .map(|object| strip_group_sections(object)) + .collect(); + let combined = out.join(format!("{}.combined.o", side.prefix)); + // The per-global asan sections survive a bfd partial link, while a gold one + // drops most of them and ASan then never registers the side's globals. + run(Command::new(tool("LD", "ld")) + .arg("-r") + .arg("-o") + .arg(&combined) + .args(&linked)); + combined }; let map = out.join(format!("{}.redefine", side.prefix)); - fs::write(&map, redefinitions(object, side.prefix)).expect("OUT_DIR is writable"); + fs::write(&map, redefinitions(&combined, side.prefix)).expect("OUT_DIR is writable"); let renamed = out.join(format!("{}.o", side.prefix)); run(Command::new(tool("OBJCOPY", "objcopy")) .arg(format!("--redefine-syms={}", map.display())) - .arg(object) + .arg(&combined) .arg(&renamed)); let archive = out.join(format!("lib{}.a", side.prefix)); let _ = fs::remove_file(&archive); - run(cc.get_archiver().arg("crs").arg(&archive).arg(&renamed)); + run(Command::new(tool("AR", "ar")) + .arg("crs") + .arg(&archive) + .arg(&renamed)); println!("cargo:rustc-link-lib=static={}", side.prefix); } @@ -118,6 +197,17 @@ fn redefinitions(object: &Path, prefix: &str) -> String { }) } +/// Returns a copy of `object` without its `.group` section. +fn strip_group_sections(object: &Path) -> PathBuf { + let stem = object.file_stem().expect("object has a stem"); + let stripped = object.with_file_name(format!("{}.ng.o", stem.to_string_lossy())); + run(Command::new(tool("OBJCOPY", "objcopy")) + .arg("--remove-section=.group") + .arg(object) + .arg(&stripped)); + stripped +} + fn tool(var: &str, default: &str) -> String { env::var(var).unwrap_or_else(|_| default.to_owned()) } diff --git a/fuzz/sqlcipher-entropy.c b/fuzz/sqlcipher-entropy.c new file mode 100644 index 0000000..e84f244 --- /dev/null +++ b/fuzz/sqlcipher-entropy.c @@ -0,0 +1,24 @@ +/* SQLCipher's entropy hook, giving its LibTomCrypt RNG the harness's deterministic getentropy. */ +#include +#include + +#include "sqlcipher-ltc.h" +#include + +/* Declared here, the fuzz harness provides it at link time. */ +int getentropy(void *buffer, size_t len); +int sqlcipher_extra_init(const char *arg); + +long sqlcipher_wasm_no_clock(void) { abort(); } + +/* Fails closed so rng_get_bytes never falls through to the clock. */ +static unsigned long sqlcipher_wasm_rng(unsigned char *out, unsigned long len, void (*callback)(void)) { + (void)callback; + if (getentropy(out, len) != 0) abort(); + return len; +} + +int sqlcipher_wasm_extra_init(const char *arg) { + ltc_rng = sqlcipher_wasm_rng; + return sqlcipher_extra_init(arg); +} diff --git a/fuzz/sqlcipher-ltc.h b/fuzz/sqlcipher-ltc.h new file mode 100644 index 0000000..5b3c83e --- /dev/null +++ b/fuzz/sqlcipher-ltc.h @@ -0,0 +1,29 @@ +/* SQLCipher's one LibTomCrypt configuration, shared by every SQLCipher and LibTomCrypt unit so struct layouts agree. */ +#ifndef SQLCIPHER_LTC_H +#define SQLCIPHER_LTC_H + +#define LTC_NOTHING +#define LTC_RIJNDAEL +#define LTC_CBC_MODE +#define LTC_SHA1 +#define LTC_SHA256 +#define LTC_SHA512 +#define LTC_HASH_HELPERS +#define LTC_HMAC +#define LTC_FORTUNA +#define LTC_PKCS_5 +#define LTC_RNG_GET_BYTES +#define LTC_PRNG_ENABLE_LTC_RNG +#define LTC_NO_TEST +#define LTC_NO_FILE +#define LTC_NO_ASM +#define LTC_NO_MATH +#define LTC_CLEAN_STACK +/* Bad arguments return CRYPT_INVALID_ARG instead of raising a signal, which the fuzzer would count as a crash. */ +#define ARGTYPE 4 +/* rng_get_bytes always ends in a clock-jitter generator, so reaching it must abort. */ +#define XCLOCK sqlcipher_wasm_no_clock +#define XCLOCKS_PER_SEC 1 +long sqlcipher_wasm_no_clock(void); + +#endif diff --git a/fuzz/sqlcipher-wrapper.c b/fuzz/sqlcipher-wrapper.c new file mode 100644 index 0000000..1a8d2e6 --- /dev/null +++ b/fuzz/sqlcipher-wrapper.c @@ -0,0 +1,17 @@ +/* SQLCipher with its LibTomCrypt provider, whose sources build as separate units with the same configuration. */ +#define SQLITE_HAS_CODEC 1 +#define SQLCIPHER_CRYPTO_LIBTOMCRYPT 1 +/* Installs the entropy hook before SQLCipher's own init runs. */ +#define SQLITE_EXTRA_INIT sqlcipher_wasm_extra_init +#define SQLITE_EXTRA_SHUTDOWN sqlcipher_extra_shutdown +/* SQLCipher's log needs a timestamp and a device the harness does not keep. */ +#define SQLCIPHER_OMIT_LOG 1 +#define SQLCIPHER_OMIT_LOG_DEVICE 1 +#define OMIT_MEMLOCK 1 +/* SQLCipher refuses SQLITE_THREADSAFE=0, and the harness runs single-threaded, so no-op mutexes suffice. */ +#undef SQLITE_THREADSAFE +#define SQLITE_THREADSAFE 1 +#define SQLITE_MUTEX_NOOP 1 + +#include "sqlcipher-ltc.h" +#include "sqlcipher.c" diff --git a/fuzz/src/entropy.rs b/fuzz/src/entropy.rs index 34b3dd9..a65a274 100644 --- a/fuzz/src/entropy.rs +++ b/fuzz/src/entropy.rs @@ -1,5 +1,5 @@ //! Replaces libc's `getentropy`, the call SQLCipher's RNG makes directly (`sqlcipher_wasm_rng` in -//! `WASM_SOURCE_FILE`), with a deterministic stream reset once per [`run`](crate::run::run) call, so +//! `sqlcipher-entropy.c`), with a deterministic stream reset once per [`run`](crate::run::run) call, so //! the same [`Case`](crate::Case) produces the same salts and IVs on every repeat, not just the same //! workload. SQLite3MC's own RNG reads `/dev/urandom` first and falls back to `getentropy` only if //! that open fails, so this does not make SQLite3MC's own writes deterministic; it is enough for any diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index 1380909..ae6bc01 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -13,6 +13,14 @@ mod ffi; mod input; mod run; pub mod seedgen; +/// The `ASan` default `detect_odr_violation` level (2 since clang 18) reports the two +/// renamed copies of the SQLite amalgamation as a violation, so the harness turns the +/// check off. `ASan` reads its default options from the weak symbol of this name, a +/// function returning the options string. +#[no_mangle] +const extern "C" fn __asan_default_options() -> *const u8 { + c"detect_odr_violation=0".as_ptr().cast::() +} pub use input::{ Algorithm, AttachCase, BackupCase, Case, Checkpoint, Cipher, Config, Key, NativeCase, From fd56047323e4d9ce901458343e7d68998531ce03 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Thu, 15 Oct 2026 18:40:39 +0200 Subject: [PATCH 3/3] Compile SQLCipher and libtomcrypt as one fuzz unit --- fuzz/build.rs | 177 ++++++++++++++------------------------- fuzz/sqlcipher-entropy.c | 10 --- fuzz/sqlcipher-ltc.h | 2 +- fuzz/sqlcipher-wrapper.c | 3 +- fuzz/src/lib.rs | 8 -- 5 files changed, 64 insertions(+), 136 deletions(-) diff --git a/fuzz/build.rs b/fuzz/build.rs index 8041b8c..e8385d7 100644 --- a/fuzz/build.rs +++ b/fuzz/build.rs @@ -8,17 +8,11 @@ use std::{ process::Command, }; -/// One compiler invocation of a side, its objects merge before the rename. -struct Unit<'a> { - files: &'a [PathBuf], - includes: &'a [PathBuf], - defines: &'a [(&'a str, Option<&'a str>)], - forced_include: Option<&'a str>, -} - struct Side<'a> { prefix: &'a str, - units: &'a [Unit<'a>], + source: PathBuf, + includes: &'a [&'a Path], + defines: &'a [(&'a str, Option<&'a str>)], } fn main() { @@ -28,60 +22,34 @@ fn main() { "the symbol renaming needs ELF objects and GNU-compatible nm and objcopy, so the fuzz crate builds on Linux only" ); let out = PathBuf::from(env::var_os("OUT_DIR").expect("cargo sets OUT_DIR")); - let crate_dir = - PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("cargo sets CARGO_MANIFEST_DIR")); - let mc_dir = sqlite3mc_src::source_dir().to_path_buf(); - let sc_dir = sqlcipher_src::source_dir().to_path_buf(); - let ltc_dir = sc_dir.join(sqlcipher_src::LIBTOMCRYPT_INCLUDE_DIR); + let mc_dir = sqlite3mc_src::source_dir(); + let sc_dir = sqlcipher_src::source_dir(); let common = [ ("SQLITE_TEMP_STORE", Some("2")), ("SQLITE_ENABLE_DBPAGE_VTAB", None), ("SQLITE_DEFAULT_MEMSTATUS", Some("0")), ]; - let mc_defines: Vec<(&str, Option<&str>)> = - [common.as_slice(), &[("SQLITE_CORE", None)]].concat(); - let ltc_files: Vec = std::iter::once("sqlcipher-entropy.c") - .map(|name| crate_dir.join(name)) - .chain( - sqlcipher_src::LIBTOMCRYPT_SOURCES - .iter() - .map(|source| sc_dir.join(source)), - ) - .collect(); - let mc_files = [mc_dir.join(sqlite3mc_src::SOURCE_FILE)]; - let mc_includes = [mc_dir]; - let wrapper_files = [crate_dir.join("sqlcipher-wrapper.c")]; - let wrapper_includes = [sc_dir, ltc_dir.clone()]; - let ltc_includes = [ltc_dir, crate_dir]; + let mc_defines = [common.as_slice(), &[("SQLITE_CORE", None)]].concat(); build( &Side { prefix: "mc", - units: &[Unit { - files: &mc_files, - includes: &mc_includes, - defines: &mc_defines, - forced_include: None, - }], + source: mc_dir.join(sqlite3mc_src::SOURCE_FILE), + includes: &[mc_dir], + defines: &mc_defines, }, &out, ); + let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR")); build( &Side { prefix: "sc", - units: &[ - Unit { - files: &wrapper_files, - includes: &wrapper_includes, - defines: &common, - forced_include: None, - }, - Unit { - files: <c_files, - includes: <c_includes, - defines: &[("LTC_SOURCE", None)], - forced_include: Some("sqlcipher-ltc.h"), - }, + source: sqlcipher_unit(crate_dir, sc_dir, &out), + includes: &[ + sc_dir, + &sc_dir.join(sqlcipher_src::LIBTOMCRYPT_INCLUDE_DIR), + crate_dir, ], + defines: &common, }, &out, ); @@ -90,83 +58,71 @@ fn main() { println!("cargo:rustc-link-lib={lib}"); } println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=sqlcipher-wrapper.c"); - println!("cargo:rerun-if-changed=sqlcipher-entropy.c"); - println!("cargo:rerun-if-changed=sqlcipher-ltc.h"); + for file in [ + "sqlcipher-wrapper.c", + "sqlcipher-ltc.h", + "sqlcipher-entropy.c", + ] { + println!("cargo:rerun-if-changed={file}"); + } println!("cargo:rerun-if-env-changed=NM"); println!("cargo:rerun-if-env-changed=OBJCOPY"); - println!("cargo:rerun-if-env-changed=LD"); - println!("cargo:rerun-if-env-changed=AR"); } fn build(side: &Side<'_>, out: &Path) { - let mut objects = Vec::new(); - for unit in side.units { - for file in unit.files { - println!("cargo:rerun-if-changed={}", file.display()); - } - let mut cc = cc::Build::new(); - for file in unit.files { - cc.file(file); - } - for dir in unit.includes { - cc.include(dir); - } - cc.warnings(false); - for (name, value) in unit.defines { - cc.define(name, *value); - } - if let Some(header) = unit.forced_include { - cc.flag("-include").flag(header); - } - instrument(&mut cc); - let unit_objects = cc.compile_intermediates(); - assert_eq!( - unit_objects.len(), - unit.files.len(), + let mut cc = cc::Build::new(); + cc.file(&side.source).warnings(false); + for include in side.includes { + println!("cargo:rerun-if-changed={}", include.display()); + cc.include(include); + } + for (name, value) in side.defines { + cc.define(name, *value); + } + instrument(&mut cc); + let objects = cc.compile_intermediates(); + let [object] = objects.as_slice() else { + panic!( "{} compiled to {} objects", - unit.files[0].display(), - unit_objects.len() + side.source.display(), + objects.len() ); - objects.extend(unit_objects); - } - - let combined = if objects.len() == 1 { - objects.remove(0) - } else { - // Clang's .group sections break the final link once a partial link merges them. - let linked: Vec = objects - .iter() - .map(|object| strip_group_sections(object)) - .collect(); - let combined = out.join(format!("{}.combined.o", side.prefix)); - // The per-global asan sections survive a bfd partial link, while a gold one - // drops most of them and ASan then never registers the side's globals. - run(Command::new(tool("LD", "ld")) - .arg("-r") - .arg("-o") - .arg(&combined) - .args(&linked)); - combined }; let map = out.join(format!("{}.redefine", side.prefix)); - fs::write(&map, redefinitions(&combined, side.prefix)).expect("OUT_DIR is writable"); + fs::write(&map, redefinitions(object, side.prefix)).expect("OUT_DIR is writable"); let renamed = out.join(format!("{}.o", side.prefix)); run(Command::new(tool("OBJCOPY", "objcopy")) .arg(format!("--redefine-syms={}", map.display())) - .arg(&combined) + .arg(object) .arg(&renamed)); let archive = out.join(format!("lib{}.a", side.prefix)); let _ = fs::remove_file(&archive); - run(Command::new(tool("AR", "ar")) - .arg("crs") - .arg(&archive) - .arg(&renamed)); + run(cc.get_archiver().arg("crs").arg(&archive).arg(&renamed)); println!("cargo:rustc-link-lib=static={}", side.prefix); } +/// Writes the one translation unit SQLCipher builds from, the wrapper's configuration and SQLCipher, then every +/// libtomcrypt source in order, then the entropy hook, all sharing one configuration as upstream's single-file +/// libtomcrypt does. +fn sqlcipher_unit(crate_dir: &Path, sc_dir: &Path, out: &Path) -> PathBuf { + let sources = std::iter::once(crate_dir.join("sqlcipher-wrapper.c")) + .chain( + sqlcipher_src::LIBTOMCRYPT_SOURCES + .iter() + .map(|source| sc_dir.join(source)), + ) + .chain(std::iter::once(crate_dir.join("sqlcipher-entropy.c"))); + let unit = sources.fold(String::new(), |mut unit, source| { + let _ = writeln!(unit, "#include \"{}\"", source.display()); + unit + }); + let path = out.join("sqlcipher-unit.c"); + fs::write(&path, unit).expect("OUT_DIR is writable"); + path +} + /// Gives the C side the coverage and sanitizer instrumentation `cargo fuzz` gives the Rust side. fn instrument(cc: &mut cc::Build) { let fuzzing = env::var_os("CARGO_CFG_FUZZING").is_some(); @@ -197,17 +153,6 @@ fn redefinitions(object: &Path, prefix: &str) -> String { }) } -/// Returns a copy of `object` without its `.group` section. -fn strip_group_sections(object: &Path) -> PathBuf { - let stem = object.file_stem().expect("object has a stem"); - let stripped = object.with_file_name(format!("{}.ng.o", stem.to_string_lossy())); - run(Command::new(tool("OBJCOPY", "objcopy")) - .arg("--remove-section=.group") - .arg(object) - .arg(&stripped)); - stripped -} - fn tool(var: &str, default: &str) -> String { env::var(var).unwrap_or_else(|_| default.to_owned()) } diff --git a/fuzz/sqlcipher-entropy.c b/fuzz/sqlcipher-entropy.c index e84f244..b1eaf4e 100644 --- a/fuzz/sqlcipher-entropy.c +++ b/fuzz/sqlcipher-entropy.c @@ -1,14 +1,4 @@ /* SQLCipher's entropy hook, giving its LibTomCrypt RNG the harness's deterministic getentropy. */ -#include -#include - -#include "sqlcipher-ltc.h" -#include - -/* Declared here, the fuzz harness provides it at link time. */ -int getentropy(void *buffer, size_t len); -int sqlcipher_extra_init(const char *arg); - long sqlcipher_wasm_no_clock(void) { abort(); } /* Fails closed so rng_get_bytes never falls through to the clock. */ diff --git a/fuzz/sqlcipher-ltc.h b/fuzz/sqlcipher-ltc.h index 5b3c83e..3857e89 100644 --- a/fuzz/sqlcipher-ltc.h +++ b/fuzz/sqlcipher-ltc.h @@ -1,4 +1,4 @@ -/* SQLCipher's one LibTomCrypt configuration, shared by every SQLCipher and LibTomCrypt unit so struct layouts agree. */ +/* The LibTomCrypt configuration SQLCipher and every libtomcrypt source in the unit share. */ #ifndef SQLCIPHER_LTC_H #define SQLCIPHER_LTC_H diff --git a/fuzz/sqlcipher-wrapper.c b/fuzz/sqlcipher-wrapper.c index 1a8d2e6..5d23bda 100644 --- a/fuzz/sqlcipher-wrapper.c +++ b/fuzz/sqlcipher-wrapper.c @@ -1,4 +1,4 @@ -/* SQLCipher with its LibTomCrypt provider, whose sources build as separate units with the same configuration. */ +/* SQLCipher's configuration, heading the one unit build.rs assembles with libtomcrypt and the entropy hook. */ #define SQLITE_HAS_CODEC 1 #define SQLCIPHER_CRYPTO_LIBTOMCRYPT 1 /* Installs the entropy hook before SQLCipher's own init runs. */ @@ -13,5 +13,6 @@ #define SQLITE_THREADSAFE 1 #define SQLITE_MUTEX_NOOP 1 +#define LTC_SOURCE #include "sqlcipher-ltc.h" #include "sqlcipher.c" diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index ae6bc01..1380909 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -13,14 +13,6 @@ mod ffi; mod input; mod run; pub mod seedgen; -/// The `ASan` default `detect_odr_violation` level (2 since clang 18) reports the two -/// renamed copies of the SQLite amalgamation as a violation, so the harness turns the -/// check off. `ASan` reads its default options from the weak symbol of this name, a -/// function returning the options string. -#[no_mangle] -const extern "C" fn __asan_default_options() -> *const u8 { - c"detect_odr_violation=0".as_ptr().cast::() -} pub use input::{ Algorithm, AttachCase, BackupCase, Case, Checkpoint, Cipher, Config, Key, NativeCase,