Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 41 additions & 6 deletions fuzz/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{
struct Side<'a> {
prefix: &'a str,
source: PathBuf,
include: &'a Path,
includes: &'a [&'a Path],
defines: &'a [(&'a str, Option<&'a str>)],
}

Expand All @@ -34,16 +34,21 @@ fn main() {
&Side {
prefix: "mc",
source: mc_dir.join(sqlite3mc_src::SOURCE_FILE),
include: mc_dir,
includes: &[mc_dir],
defines: &mc_defines,
},
&out,
);
let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
build(
&Side {
prefix: "sc",
source: sc_dir.join(sqlcipher_src::WASM_SOURCE_FILE),
include: sc_dir,
source: sqlcipher_unit(crate_dir, sc_dir, &out),
includes: &[
sc_dir,
&sc_dir.join(sqlcipher_src::LIBTOMCRYPT_INCLUDE_DIR),
crate_dir,
],
defines: &common,
},
&out,
Expand All @@ -53,14 +58,24 @@ fn main() {
println!("cargo:rustc-link-lib={lib}");
}
println!("cargo:rerun-if-changed=build.rs");
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");
}

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);
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);
}
Expand Down Expand Up @@ -88,6 +103,26 @@ fn build(side: &Side<'_>, out: &Path) {
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();
Expand Down
14 changes: 14 additions & 0 deletions fuzz/sqlcipher-entropy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* SQLCipher's entropy hook, giving its LibTomCrypt RNG the harness's deterministic getentropy. */
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);
}
29 changes: 29 additions & 0 deletions fuzz/sqlcipher-ltc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* The LibTomCrypt configuration SQLCipher and every libtomcrypt source in the unit share. */
#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
18 changes: 18 additions & 0 deletions fuzz/sqlcipher-wrapper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* 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. */
#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

#define LTC_SOURCE
#include "sqlcipher-ltc.h"
#include "sqlcipher.c"
2 changes: 1 addition & 1 deletion fuzz/src/entropy.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading