diff --git a/resolve-cveassert/CMakeLists.txt b/resolve-cveassert/CMakeLists.txt index ec818b1e..5395df93 100644 --- a/resolve-cveassert/CMakeLists.txt +++ b/resolve-cveassert/CMakeLists.txt @@ -25,6 +25,8 @@ add_library(CVEAssert SHARED src/OperationMasking.cpp ) +target_compile_definitions(CVEAssert PRIVATE LLVM_FORCE_ENABLE_STATS=1) + # LLVM is normally built without RTTI. Be consistent with that. if(NOT LLVM_ENABLE_RTTI) target_compile_options(CVEAssert PRIVATE -fno-rtti) diff --git a/resolve-cveassert/libresolve/Cargo.lock b/resolve-cveassert/libresolve/Cargo.lock index a8530c12..eb9c1bf7 100644 --- a/resolve-cveassert/libresolve/Cargo.lock +++ b/resolve-cveassert/libresolve/Cargo.lock @@ -61,6 +61,18 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + [[package]] name = "colorchoice" version = "1.0.4" @@ -126,6 +138,15 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -144,11 +165,34 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" @@ -177,6 +221,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.2" @@ -213,8 +266,15 @@ dependencies = [ "env_logger", "libc", "log", + "parking_lot", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde_core" version = "1.0.228" @@ -235,6 +295,12 @@ dependencies = [ "syn", ] +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + [[package]] name = "syn" version = "2.0.111" diff --git a/resolve-cveassert/libresolve/Cargo.toml b/resolve-cveassert/libresolve/Cargo.toml index d70283d1..47c09c4b 100644 --- a/resolve-cveassert/libresolve/Cargo.toml +++ b/resolve-cveassert/libresolve/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] env_logger = "0.11.8" libc = "0.2.174" log = "0.4.29" +parking_lot = "0.12.5" [profile.release] panic = "abort" diff --git a/resolve-cveassert/libresolve/src/lib.rs b/resolve-cveassert/libresolve/src/lib.rs index 4b7fe7a1..c143b56c 100644 --- a/resolve-cveassert/libresolve/src/lib.rs +++ b/resolve-cveassert/libresolve/src/lib.rs @@ -13,6 +13,7 @@ use std::fmt::Display; use std::fs::{self, File}; use std::path::PathBuf; use std::io::{self, Seek, Write}; +use parking_lot::{Mutex, MutexGuard}; use std::sync::{LazyLock, Mutex}; use std::{env, process}; @@ -28,8 +29,8 @@ impl MutexWrap { } // Abort if the mutex is poisoned - pub fn lock(&self) -> std::sync::MutexGuard<'_, T> { - self.mutex.lock().expect("Not poisoned") + pub fn lock(&self) -> MutexGuard<'_, T> { + self.mutex.lock() } } diff --git a/resolve-cveassert/libresolve/src/remediate.rs b/resolve-cveassert/libresolve/src/remediate.rs index 06efde99..b7b77eb4 100644 --- a/resolve-cveassert/libresolve/src/remediate.rs +++ b/resolve-cveassert/libresolve/src/remediate.rs @@ -22,7 +22,7 @@ pub extern "C" fn __resolve_alloca(ptr: *mut c_void, size: usize) -> () { ss.add_shadow_object(base, size) ); - info!("[STACK] Object allocated with size: {size}, address: 0x{base:x}"); + // info!("[STACK] Object allocated with size: {size}, address: 0x{base:x}"); } #[unsafe(no_mangle)] @@ -39,7 +39,7 @@ pub extern "C" fn __resolve_invalidate_stack_range(base: *mut c_void, size: usiz ss.invalidate_at(base, size) ); - info!("[STACK] Free addr 0x{base:x} size {size}"); + // info!("[STACK] Free addr 0x{base:x} size {size}"); } /** @@ -60,10 +60,10 @@ pub extern "C" fn __resolve_malloc(size: usize) -> *mut c_void { obj_list.add_shadow_object(AllocType::Heap, ptr as Vaddr, size); } - info!( - "[HEAP] Object allocated with size: {size}, address: 0x{:x}", - ptr as Vaddr - ); + // info!( + // "[HEAP] Object allocated with size: {size}, address: 0x{:x}", + // ptr as Vaddr + // ); ptr } @@ -78,10 +78,10 @@ pub extern "C" fn __resolve_free(ptr: *mut c_void) -> () { // Insert a function to find the object and return the pointer size // Do I need to handle if the sobj cannot be found? - info!( - "[FREE] Allocated object freed at address: 0x{:x}", - ptr as Vaddr - ); + // info!( + // "[FREE] Allocated object freed at address: 0x{:x}", + // ptr as Vaddr + // ); let ptr_size = { let mut obj_list = ALIVE_OBJ_LIST.lock(); @@ -93,20 +93,20 @@ pub extern "C" fn __resolve_free(ptr: *mut c_void) -> () { }; // Check if the shadow object exists - match ptr_size { - Some(size) => { - info!( - "[FREE] Found shadow object for allocated object, 0x{:x}, size = {size}", - ptr as Vaddr, - ); - } - None => { - warn!( - "[FREE] No shadow object found for allocated object: 0x{:x}", - ptr as Vaddr - ); - } - } + // match ptr_size { + // Some(size) => { + // info!( + // "[FREE] Found shadow object for allocated object, 0x{:x}, size = {size}", + // ptr as Vaddr, + // ); + // } + // None => { + // warn!( + // "[FREE] No shadow object found for allocated object: 0x{:x}", + // ptr as Vaddr + // ); + // } + // } { // Insert shadow object into freed object list @@ -147,10 +147,10 @@ pub extern "C" fn __resolve_realloc(ptr: *mut c_void, size: usize) -> *mut c_voi obj_list.add_shadow_object(AllocType::Heap, realloc_ptr as Vaddr, size); } - info!( - "[HEAP] Allocated object reallocated mem from src: {ptr:?}, size: {size}, dst ptr: 0x{:x}", - realloc_ptr as Vaddr - ); + // info!( + // "[HEAP] Allocated object reallocated mem from src: {ptr:?}, size: {size}, dst ptr: 0x{:x}", + // realloc_ptr as Vaddr + // ); realloc_ptr } @@ -176,10 +176,10 @@ pub extern "C" fn __resolve_calloc(n_items: usize, item_size: usize) -> *mut c_v obj_list.add_shadow_object(AllocType::Heap, ptr as Vaddr, size); } - info!( - "[HEAP] Logging allocation with {n_items} items, size (bytes): {size}, dst ptr: 0x{:x}", - ptr as Vaddr - ); + // info!( + // "[HEAP] Logging allocation with {n_items} items, size (bytes): {size}, dst ptr: 0x{:x}", + // ptr as Vaddr + // ); ptr } @@ -207,10 +207,10 @@ pub extern "C" fn __resolve_strdup(ptr: *mut c_char) -> *mut c_char { obj_list.add_shadow_object(AllocType::Heap, string_ptr as Vaddr, sizeofstr); } - info!( - "[HEAP] Logging 'strdup' function call with dst ptr: 0x{:x}", - string_ptr as Vaddr - ); + // info!( + // "[HEAP] Logging 'strdup' function call with dst ptr: 0x{:x}", + // string_ptr as Vaddr + // ); string_ptr } @@ -243,10 +243,10 @@ pub extern "C" fn __resolve_strndup(ptr: *mut c_char, size: usize) -> *mut c_cha obj_list.add_shadow_object(AllocType::Heap, string_ptr as Vaddr, sizeofstr); } - info!( - "[HEAP] Logging 'strndup' function call with size (bytes): {size}, dst ptr: {:?}", - string_ptr as Vaddr - ); + // info!( + // "[HEAP] Logging 'strndup' function call with size (bytes): {size}, dst ptr: {:?}", + // string_ptr as Vaddr + // ); string_ptr } @@ -357,7 +357,7 @@ pub extern "C" fn resolve_obj_type(base_ptr: *mut c_void) -> AllocType { */ #[unsafe(no_mangle)] pub extern "C" fn __resolve_report_violation() -> () { - info!("[RESOLVE] sanitizer triggered"); + // info!("[RESOLVE] sanitizer triggered"); } #[cfg(test)] diff --git a/resolve-cveassert/src/CVEAssert.cpp b/resolve-cveassert/src/CVEAssert.cpp index 3fa215b6..b58674ce 100644 --- a/resolve-cveassert/src/CVEAssert.cpp +++ b/resolve-cveassert/src/CVEAssert.cpp @@ -189,10 +189,10 @@ struct LabelCVEPass : public PassInfoMixin { out << F; out << "[CVEAssert] === Inserted Sanitizer Helpers === \n"; - if (vuln.UndesirableFunction.has_value()) { + if (vuln.Operation.has_value()) { /* NOTE: We are using '0' as a temporary this will be updated future PRs */ - sanitizeUndesirableOperationInFunction(&F, *vuln.UndesirableFunction, 0); + sanitizeContract(&F, *vuln.Operation, 0); result = PreservedAnalyses::none(); out << "[CVEAssert] === Post Sanitization of Undesirable Operation IR " "=== \n"; diff --git a/resolve-cveassert/src/OperationMasking.cpp b/resolve-cveassert/src/OperationMasking.cpp index d13532c9..f876d871 100644 --- a/resolve-cveassert/src/OperationMasking.cpp +++ b/resolve-cveassert/src/OperationMasking.cpp @@ -11,6 +11,7 @@ #include "llvm/Support/raw_ostream.h" #include "IRUtils.hpp" +#include "Vulnerability.hpp" #include #include @@ -18,12 +19,24 @@ using namespace llvm; -enum Cond { // Maybe adding an enum for all the possible conditions - EQ = 1, - GT = 2, - GT_EQ = 3, - LT = 4, - LT_EQ = 5 +enum class PredicateKind { + InBounds, + NotEqual, + NotNull, + NonZero, +}; + +// Predicates tell the compiler what +// must be true before executing the operation +struct Predicate { + PredicateKind kind; + unsigned arg0; + unsigned arg1; +}; + +struct Contract { + std::vector predicates; + Vulnerability::RemediationStrategies strategy; }; // Parameters @@ -33,37 +46,52 @@ enum Cond { // Maybe adding an enum for all the possible conditions // We will continue generalizing this following eval-2 // Change this function name to be "replaceUndesirableOperation" more // generalized name -static Function *replaceUndesirableFunction(Module *M, CallInst *call, +static Function *getOrCreateContractWrapper(Module *M, CallInst *call, unsigned int argNum) { LLVMContext &Ctx = M->getContext(); IRBuilder<> builder(Ctx); - std::string handlerName = - "resolve_sanitized_" + call->getCalledFunction()->getName().str(); + Function *originalFn = call->getCalledFunction(); + + std::string handlerName = "__cve_contract_" + originalFn->getName().str(); - FunctionType *resolveSanitizedFnTy = - call->getCalledFunction()->getFunctionType(); + FunctionType *wrapperTy = originalFn->getFunctionType(); - Function *resolveSanitizedFn = - getOrCreateResolveHelper(M, handlerName, resolveSanitizedFnTy); + Function *resolveWrapperFn = + getOrCreateResolveHelper(M, handlerName, wrapperTy); - if (!resolveSanitizedFn->empty()) { - recordPatchFunction(resolveSanitizedFn); - return resolveSanitizedFn; + SmallVector Args; + for (Argument &arg : resolveWrapperFn->args()) { + Args.push_back(&arg); } - BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", resolveSanitizedFn); + if (!resolveWrapperFn->empty()) { + recordPatchFunction(resolveWrapperFn); + return resolveWrapperFn; + } + + // TODO: Create 3 basic blocks + // 1. Preconditions + // 2. Valid path + // 3. Recovery path + BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", resolveWrapperFn); // Insert a return instruction here. builder.SetInsertPoint(EntryBB); - builder.CreateRet(resolveSanitizedFn->getArg(argNum)); - validateIR(resolveSanitizedFn); - recordPatchFunction(resolveSanitizedFn); - return resolveSanitizedFn; + // TODO: Create helper to generate the llvm-ir for preconditions + // TODO: Create helper to generate valid path (call original operation + // contract) + // TODO: Create helper to generate recovery path + + builder.CreateCall(originalFn, Args); + builder.CreateRet(resolveWrapperFn->getArg(argNum)); + + validateIR(resolveWrapperFn); + recordPatchFunction(resolveWrapperFn); + return resolveWrapperFn; } -void sanitizeUndesirableOperationInFunction(Function *F, std::string fnName, - unsigned int argNum) { +void sanitizeContract(Function *F, std::string fnName, unsigned int argNum) { Module *M = F->getParent(); LLVMContext &Ctx = M->getContext(); IRBuilder<> builder(Ctx); @@ -93,24 +121,19 @@ void sanitizeUndesirableOperationInFunction(Function *F, std::string fnName, return; } - // Construct the resolve_sanitize_func function - Function *resolveSanitizedFn = - replaceUndesirableFunction(M, callsToReplace.front(), argNum); + Function *resolveWrapperFn = + getOrCreateContractWrapper(M, callsToReplace.front(), argNum); - // Replace calls at all callsites in the module for (auto call : callsToReplace) { builder.SetInsertPoint(call); - // Get the arguments for the vulnerable function SmallVector fnArgs; for (unsigned int i = 0; i < call->arg_size(); ++i) { fnArgs.push_back(call->getOperand(i)); } - auto sanitizedCall = builder.CreateCall(resolveSanitizedFn, fnArgs); - - // replace all callsites - call->replaceAllUsesWith(sanitizedCall); + auto resolveWrapperCall = builder.CreateCall(resolveWrapperFn, fnArgs); + call->replaceAllUsesWith(resolveWrapperCall); call->eraseFromParent(); } } diff --git a/resolve-cveassert/src/OperationMasking.hpp b/resolve-cveassert/src/OperationMasking.hpp index b637d894..91276c5d 100644 --- a/resolve-cveassert/src/OperationMasking.hpp +++ b/resolve-cveassert/src/OperationMasking.hpp @@ -7,6 +7,5 @@ #include "llvm/IR/Function.h" #include -void sanitizeUndesirableOperationInFunction(llvm::Function *F, - std::string fnName, - unsigned int argNum); +void sanitizeContract(llvm::Function *F, std::string fnName, + unsigned int argNum); diff --git a/resolve-cveassert/src/Vulnerability.hpp b/resolve-cveassert/src/Vulnerability.hpp index d169d4ef..8924fbca 100644 --- a/resolve-cveassert/src/Vulnerability.hpp +++ b/resolve-cveassert/src/Vulnerability.hpp @@ -35,7 +35,7 @@ struct Vulnerability { std::string TargetFileName; std::string TargetFunctionName; uint32_t WeaknessID; - std::optional UndesirableFunction; + std::optional Operation; RemediationStrategies Strategy; RemediationOutput Output; bool Gated; @@ -84,9 +84,9 @@ struct Vulnerability { return std::nullopt; } - std::optional undesirableFunction = std::nullopt; - if (auto uf = getKey("undesirable-function")) { - undesirableFunction = uf->str(); + std::optional operation = std::nullopt; + if (auto op = getKey("operation")) { + operation = op->str(); } auto remediation = getKey("remediation-strategy"); @@ -148,7 +148,7 @@ struct Vulnerability { targetFile->str(), targetFunction->str(), static_cast(std::stoi(vulnID->str())), - undesirableFunction, + operation, strategy, output, gated,