From 50ad189e485ea3132226d24487af5db77eda7053 Mon Sep 17 00:00:00 2001 From: Juanma Date: Mon, 17 Jul 2023 17:22:02 -0300 Subject: [PATCH] Experiment with ffi --- .gitmodules | 3 + c_fuzzing/Cargo.toml | 13 + c_fuzzing/README.md | 7 + c_fuzzing/build.rs | 87 +++++++ c_fuzzing/cJSON | 1 + c_fuzzing/src/c_json.rs | 520 ++++++++++++++++++++++++++++++++++++++++ c_fuzzing/src/main.rs | 13 + 7 files changed, 644 insertions(+) create mode 100644 .gitmodules create mode 100644 c_fuzzing/Cargo.toml create mode 100644 c_fuzzing/README.md create mode 100644 c_fuzzing/build.rs create mode 160000 c_fuzzing/cJSON create mode 100644 c_fuzzing/src/c_json.rs create mode 100644 c_fuzzing/src/main.rs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..210fd5db --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "c_fuzzing/cJSON"] + path = c_fuzzing/cJSON + url = https://github.com/DaveGamble/cJSON diff --git a/c_fuzzing/Cargo.toml b/c_fuzzing/Cargo.toml new file mode 100644 index 00000000..7cb9dffa --- /dev/null +++ b/c_fuzzing/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "c_fuzzing" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cargo-fuzz = "0.11.2" +serde_json = "1.0.103" + +[build-dependencies] +bindgen = "0.65.1" diff --git a/c_fuzzing/README.md b/c_fuzzing/README.md new file mode 100644 index 00000000..13ec0106 --- /dev/null +++ b/c_fuzzing/README.md @@ -0,0 +1,7 @@ +# Note on how to make it all work + +* clone `cJSON` and run `make` +* cp the `libcjson.dylib.1.7.16` dynamic library to the root dir. There has to be a better way to make this work +* run bindgen `bindgen cJSON/cJSON.h > src/c_json.rs` to create the bindings +* create the `build.rs` file, copying and modifying the file shown in the [docs](https://rust-lang.github.io/rust-bindgen/non-system-libraries.html) seems to work fine + diff --git a/c_fuzzing/build.rs b/c_fuzzing/build.rs new file mode 100644 index 00000000..fa2540da --- /dev/null +++ b/c_fuzzing/build.rs @@ -0,0 +1,87 @@ +//extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +use bindgen::CargoCallbacks; + +fn main() { + // This is the directory where the `c` library is located. + let libdir_path = PathBuf::from("cJSON") + // Canonicalize the path as `rustc-link-search` requires an absolute + // path. + .canonicalize() + .expect("cannot canonicalize path"); + + // This is the path to the `c` headers file. + let headers_path = libdir_path.join("cJSON.h"); + let headers_path_str = headers_path.to_str().expect("Path is not a valid string"); + + // This is the path to the intermediate object file for our library. + let obj_path = libdir_path.join("cJSON.o"); + // This is the path to the static library file. + let lib_path = libdir_path.join("libcjson.a"); + + // Tell cargo to look for shared libraries in the specified directory + println!("cargo:rustc-link-search={}", libdir_path.to_str().unwrap()); + + // Tell cargo to tell rustc to link our `hello` library. Cargo will + // automatically know it must look for a `libhello.a` file. + println!("cargo:rustc-link-lib=cjson"); + + // Tell cargo to invalidate the built crate whenever the header changes. + println!("cargo:rerun-if-changed={}", headers_path_str); + + // Run `clang` to compile the `hello.c` file into a `hello.o` object file. + // Unwrap if it is not possible to spawn the process. + if !std::process::Command::new("clang") + .arg("-c") + .arg("-o") + .arg(&obj_path) + .arg(libdir_path.join("cJSON.c")) + .output() + .expect("could not spawn `clang`") + .status + .success() + { + // Panic if the command was not successful. + panic!("could not compile object file"); + } + + // Run `ar` to generate the `libhello.a` file from the `hello.o` file. + // Unwrap if it is not possible to spawn the process. + if !std::process::Command::new("ar") + .arg("rcs") + .arg(lib_path) + .arg(obj_path) + .output() + .expect("could not spawn `ar`") + .status + .success() + { + // Panic if the command was not successful. + panic!("could not emit library file"); + } + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header(headers_path_str) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + bindings + .write_to_file(out_path) + .expect("Couldn't write bindings!"); +} + diff --git a/c_fuzzing/cJSON b/c_fuzzing/cJSON new file mode 160000 index 00000000..cb8693b0 --- /dev/null +++ b/c_fuzzing/cJSON @@ -0,0 +1 @@ +Subproject commit cb8693b058ba302f4829ec6d03f609ac6f848546 diff --git a/c_fuzzing/src/c_json.rs b/c_fuzzing/src/c_json.rs new file mode 100644 index 00000000..f7e8ce61 --- /dev/null +++ b/c_fuzzing/src/c_json.rs @@ -0,0 +1,520 @@ +/* automatically generated by rust-bindgen 0.66.1 */ + +pub const CJSON_VERSION_MAJOR: u32 = 1; +pub const CJSON_VERSION_MINOR: u32 = 7; +pub const CJSON_VERSION_PATCH: u32 = 16; +pub const cJSON_Invalid: u32 = 0; +pub const cJSON_False: u32 = 1; +pub const cJSON_True: u32 = 2; +pub const cJSON_NULL: u32 = 4; +pub const cJSON_Number: u32 = 8; +pub const cJSON_String: u32 = 16; +pub const cJSON_Array: u32 = 32; +pub const cJSON_Object: u32 = 64; +pub const cJSON_Raw: u32 = 128; +pub const cJSON_IsReference: u32 = 256; +pub const cJSON_StringIsConst: u32 = 512; +pub const CJSON_NESTING_LIMIT: u32 = 1000; +pub type wchar_t = ::std::os::raw::c_int; +pub type max_align_t = f64; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct cJSON { + pub next: *mut cJSON, + pub prev: *mut cJSON, + pub child: *mut cJSON, + pub type_: ::std::os::raw::c_int, + pub valuestring: *mut ::std::os::raw::c_char, + pub valueint: ::std::os::raw::c_int, + pub valuedouble: f64, + pub string: *mut ::std::os::raw::c_char, +} +#[test] +fn bindgen_test_layout_cJSON() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 64usize, + concat!("Size of: ", stringify!(cJSON)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(cJSON)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).next) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(next) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).prev) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(prev) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).child) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(child) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).type_) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(type_) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).valuestring) as usize - ptr as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(valuestring) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).valueint) as usize - ptr as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(valueint) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).valuedouble) as usize - ptr as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(valuedouble) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).string) as usize - ptr as usize }, + 56usize, + concat!( + "Offset of field: ", + stringify!(cJSON), + "::", + stringify!(string) + ) + ); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct cJSON_Hooks { + pub malloc_fn: + ::std::option::Option *mut ::std::os::raw::c_void>, + pub free_fn: ::std::option::Option, +} +#[test] +fn bindgen_test_layout_cJSON_Hooks() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(cJSON_Hooks)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(cJSON_Hooks)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).malloc_fn) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(cJSON_Hooks), + "::", + stringify!(malloc_fn) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).free_fn) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(cJSON_Hooks), + "::", + stringify!(free_fn) + ) + ); +} +pub type cJSON_bool = ::std::os::raw::c_int; +extern "C" { + pub fn cJSON_Version() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_InitHooks(hooks: *mut cJSON_Hooks); +} +extern "C" { + pub fn cJSON_Parse(value: *const ::std::os::raw::c_char) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_ParseWithLength( + value: *const ::std::os::raw::c_char, + buffer_length: usize, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_ParseWithOpts( + value: *const ::std::os::raw::c_char, + return_parse_end: *mut *const ::std::os::raw::c_char, + require_null_terminated: cJSON_bool, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_ParseWithLengthOpts( + value: *const ::std::os::raw::c_char, + buffer_length: usize, + return_parse_end: *mut *const ::std::os::raw::c_char, + require_null_terminated: cJSON_bool, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_Print(item: *const cJSON) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_PrintUnformatted(item: *const cJSON) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_PrintBuffered( + item: *const cJSON, + prebuffer: ::std::os::raw::c_int, + fmt: cJSON_bool, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_PrintPreallocated( + item: *mut cJSON, + buffer: *mut ::std::os::raw::c_char, + length: ::std::os::raw::c_int, + format: cJSON_bool, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_Delete(item: *mut cJSON); +} +extern "C" { + pub fn cJSON_GetArraySize(array: *const cJSON) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn cJSON_GetArrayItem(array: *const cJSON, index: ::std::os::raw::c_int) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_GetObjectItem( + object: *const cJSON, + string: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_GetObjectItemCaseSensitive( + object: *const cJSON, + string: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_HasObjectItem( + object: *const cJSON, + string: *const ::std::os::raw::c_char, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_GetErrorPtr() -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_GetStringValue(item: *const cJSON) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_GetNumberValue(item: *const cJSON) -> f64; +} +extern "C" { + pub fn cJSON_IsInvalid(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsFalse(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsTrue(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsBool(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsNull(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsNumber(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsString(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsArray(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsObject(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_IsRaw(item: *const cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_CreateNull() -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateTrue() -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateFalse() -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateBool(boolean: cJSON_bool) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateNumber(num: f64) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateString(string: *const ::std::os::raw::c_char) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateRaw(raw: *const ::std::os::raw::c_char) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateArray() -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateObject() -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateStringReference(string: *const ::std::os::raw::c_char) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateObjectReference(child: *const cJSON) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateArrayReference(child: *const cJSON) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateIntArray( + numbers: *const ::std::os::raw::c_int, + count: ::std::os::raw::c_int, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateFloatArray(numbers: *const f32, count: ::std::os::raw::c_int) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateDoubleArray(numbers: *const f64, count: ::std::os::raw::c_int) + -> *mut cJSON; +} +extern "C" { + pub fn cJSON_CreateStringArray( + strings: *const *const ::std::os::raw::c_char, + count: ::std::os::raw::c_int, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddItemToArray(array: *mut cJSON, item: *mut cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_AddItemToObject( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + item: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_AddItemToObjectCS( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + item: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_AddItemReferenceToArray(array: *mut cJSON, item: *mut cJSON) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_AddItemReferenceToObject( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + item: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_DetachItemViaPointer(parent: *mut cJSON, item: *mut cJSON) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_DetachItemFromArray(array: *mut cJSON, which: ::std::os::raw::c_int) + -> *mut cJSON; +} +extern "C" { + pub fn cJSON_DeleteItemFromArray(array: *mut cJSON, which: ::std::os::raw::c_int); +} +extern "C" { + pub fn cJSON_DetachItemFromObject( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_DetachItemFromObjectCaseSensitive( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_DeleteItemFromObject(object: *mut cJSON, string: *const ::std::os::raw::c_char); +} +extern "C" { + pub fn cJSON_DeleteItemFromObjectCaseSensitive( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + ); +} +extern "C" { + pub fn cJSON_InsertItemInArray( + array: *mut cJSON, + which: ::std::os::raw::c_int, + newitem: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_ReplaceItemViaPointer( + parent: *mut cJSON, + item: *mut cJSON, + replacement: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_ReplaceItemInArray( + array: *mut cJSON, + which: ::std::os::raw::c_int, + newitem: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_ReplaceItemInObject( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + newitem: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_ReplaceItemInObjectCaseSensitive( + object: *mut cJSON, + string: *const ::std::os::raw::c_char, + newitem: *mut cJSON, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_Duplicate(item: *const cJSON, recurse: cJSON_bool) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_Compare( + a: *const cJSON, + b: *const cJSON, + case_sensitive: cJSON_bool, + ) -> cJSON_bool; +} +extern "C" { + pub fn cJSON_Minify(json: *mut ::std::os::raw::c_char); +} +extern "C" { + pub fn cJSON_AddNullToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddTrueToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddFalseToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddBoolToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + boolean: cJSON_bool, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddNumberToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + number: f64, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddStringToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + string: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddRawToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + raw: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddObjectToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_AddArrayToObject( + object: *mut cJSON, + name: *const ::std::os::raw::c_char, + ) -> *mut cJSON; +} +extern "C" { + pub fn cJSON_SetNumberHelper(object: *mut cJSON, number: f64) -> f64; +} +extern "C" { + pub fn cJSON_SetValuestring( + object: *mut cJSON, + valuestring: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +extern "C" { + pub fn cJSON_malloc(size: usize) -> *mut ::std::os::raw::c_void; +} +extern "C" { + pub fn cJSON_free(object: *mut ::std::os::raw::c_void); +} diff --git a/c_fuzzing/src/main.rs b/c_fuzzing/src/main.rs new file mode 100644 index 00000000..3fb84989 --- /dev/null +++ b/c_fuzzing/src/main.rs @@ -0,0 +1,13 @@ +pub mod c_json; +use c_json::{cJSON, cJSON_Parse, cJSON_GetObjectItemCaseSensitive}; +use serde_json::Value; + +fn main() { + let json_string = "{\n\t\"name\":\"John\"\n}"; + let json = unsafe { cJSON_Parse(json_string.as_ptr() as *const i8) }; + let name_ptr: *mut cJSON = unsafe { cJSON_GetObjectItemCaseSensitive(json, "name".as_ptr() as *const i8).into() }; + let name: cJSON = unsafe { *name_ptr }; + let v: Value = serde_json::from_str(json_string).unwrap(); + println!("SERDE: {}", v["name"]); + unsafe { println!("cJSON: {:?}", std::ffi::CStr::from_ptr(name.valuestring)) }; +}