From 67f732b292d88367308f98dfe7f8bee01564abea Mon Sep 17 00:00:00 2001 From: Owen Anderson Date: Fri, 19 Dec 2025 01:03:59 -0600 Subject: [PATCH] [CHERIoT] Implement a basic checker for CHERIoT heap APIs. It currently enforces three rules: - Heap claims created with heap_claim() must be released. - No use-after-release of a heap claim, including ephermeral claims that are implicitly released by cross-compartment calls. - No calls to check_pointer without first holding a claim. --- .../clang/StaticAnalyzer/Checkers/Checkers.td | 8 + clang/lib/Driver/ToolChains/Arch/RISCV.cpp | 6 + clang/lib/Driver/ToolChains/Arch/RISCV.h | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 3 + .../Checkers/CHERI/CheriotHeapChecker.cpp | 402 ++++++++++++++++++ .../StaticAnalyzer/Checkers/CMakeLists.txt | 1 + .../Analysis/Checkers/CHERI/cheriot-heap.c | 192 +++++++++ 7 files changed, 613 insertions(+) create mode 100644 clang/lib/StaticAnalyzer/Checkers/CHERI/CheriotHeapChecker.cpp create mode 100644 clang/test/Analysis/Checkers/CHERI/cheriot-heap.c diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 40b7a134794ce..8065727d61448 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -126,6 +126,7 @@ def WebKitAlpha : Package<"webkit">, ParentPackage; def CHERI : Package<"cheri">; def CHERIAlpha : Package<"cheri">, ParentPackage; +def CHERIoT : Package<"cheriot">; //===----------------------------------------------------------------------===// // Core Checkers. @@ -1862,3 +1863,10 @@ let ParentPackage = CHERIAlpha in { Documentation; } // end alpha.cheri + +let ParentPackage = CHERIoT in { + def CheriotHeapChecker + : Checker<"CheriotHeap">, + HelpText<"Check use of CHERIoT heap allocations APIs">, + Documentation; +} // end cheriot diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp index 177af5473de7d..a5f85fec776bc 100644 --- a/clang/lib/Driver/ToolChains/Arch/RISCV.cpp +++ b/clang/lib/Driver/ToolChains/Arch/RISCV.cpp @@ -72,6 +72,12 @@ bool riscv::isCheriPurecap(const llvm::opt::ArgList &Args, return isCheriPurecapABIName(getRISCVABI(Args, Triple)); } +bool riscv::isCheriot(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple) { + auto ABI = getRISCVABI(Args, Triple); + return (Triple.getOS() == llvm::Triple::CheriotRTOS || + (ABI == "cheriot" || ABI == "cheriot-baremetal")); +} // Get features except standard extension feature static void getRISCFeaturesFromMcpu(const Driver &D, const Arg *A, diff --git a/clang/lib/Driver/ToolChains/Arch/RISCV.h b/clang/lib/Driver/ToolChains/Arch/RISCV.h index 905ac97c6d6e0..63aa725056569 100644 --- a/clang/lib/Driver/ToolChains/Arch/RISCV.h +++ b/clang/lib/Driver/ToolChains/Arch/RISCV.h @@ -27,6 +27,7 @@ StringRef getRISCVABI(const llvm::opt::ArgList &Args, std::string getRISCVArch(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); bool isCheriPurecap(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); +bool isCheriot(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); std::string getRISCVTargetCPU(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); } // end namespace riscv diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 869b0635adad1..a595cfca39d20 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3518,6 +3518,9 @@ static void RenderAnalyzerOptions(const ArgList &Args, ArgStringList &CmdArgs, CmdArgs.push_back("-analyzer-checker=optin.portability.PointerAlignment"); CmdArgs.push_back("-analyzer-checker=alpha.core.PointerSub"); + + if (tools::riscv::isCheriot(Args, Triple)) + CmdArgs.push_back("-analyzer-checker=cheriot"); } // Default nullability checks. diff --git a/clang/lib/StaticAnalyzer/Checkers/CHERI/CheriotHeapChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CHERI/CheriotHeapChecker.cpp new file mode 100644 index 0000000000000..cb0a4ab78eb68 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/CHERI/CheriotHeapChecker.cpp @@ -0,0 +1,402 @@ +//===--- CheriotHeapChecker.cpp - Heap management checker ------*- C++ -*-===// +// +// Clang Static Analyzer checker for CHERIoT heap management +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { + +// State for tracking heap pointers from heap_claim and heap_claim_ephemeral +struct HeapPtrState { + enum Kind : unsigned char { + Unclaimed, // Pointer passed to a compartment call that is unclaimed + Claimed, // Claimed via heap_claim + Ephemeral, // Claimed via heap_claim_ephemeral + InvalidatedEphemeral, // Implicitly release by a cross-compartment call + Escaped, // Escaped or otherwise unknown + }; + + Kind K; + + HeapPtrState(Kind K) : K(K) {} + + bool isUnclaimed() const { return K == Unclaimed; } + bool isClaimed() const { return K == Claimed; } + bool isEphemeral() const { return K == Ephemeral; } + bool isInvalidatedEphemeral() const { return K == InvalidatedEphemeral; } + bool isEffectivelyClaimed() const { + return K == Claimed || K == Ephemeral || K == Escaped; + } + + bool operator==(const HeapPtrState &X) const { return K == X.K; } + + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; + +class CheriotHeapChecker + : public Checker { + const BugType LeakBugType{this, "Heap claim leak", "CHERIoT heap management"}; + const BugType InvalidUseBugType{this, "Invalid pointer use", + "CHERIoT heap management"}; + + using CheckFn = std::function; + + const CallDescriptionMap PreFnMap{ + {{CDM::SimpleFunc, {"check_pointer"}, 4}, + &CheriotHeapChecker::preCheckPointer}, + }; + + const CallDescriptionMap PostFnMap{ + {{CDM::SimpleFunc, {"heap_address_is_valid"}, 1}, + &CheriotHeapChecker::postHeapAddressIsValid}, + {{CDM::SimpleFunc, {"heap_claim"}, 2}, + &CheriotHeapChecker::postHeapClaim}, + {{CDM::SimpleFunc, {"heap_claim_ephemeral"}, 3}, + &CheriotHeapChecker::postHeapClaimEphemeral}, + {{CDM::SimpleFunc, {"heap_free"}, 2}, &CheriotHeapChecker::postHeapFree}, + {{CDM::SimpleFunc, {"heap_free_all"}, 1}, + &CheriotHeapChecker::postHeapFreeAll}, + }; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + + void preCheckPointer(const CallEvent &Call, CheckerContext &C) const; + void postHeapAddressIsValid(const CallEvent &Call, CheckerContext &C) const; + void postHeapClaim(const CallEvent &Call, CheckerContext &C) const; + void postHeapClaimEphemeral(const CallEvent &Call, CheckerContext &C) const; + void postHeapFree(const CallEvent &Call, CheckerContext &C) const; + void postHeapFreeAll(const CallEvent &Call, CheckerContext &C) const; + +private: + void reportLeak(SymbolRef Sym, CheckerContext &C) const; +}; + +} // anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(HeapPointers, SymbolRef, HeapPtrState) + +bool isCrossCompartmentCall(const CallEvent &Call, const CheckerContext &C) { + const Decl *D = Call.getDecl(); + if (!D) + return false; + + const auto *FD = dyn_cast(D); + if (!FD) + return false; + if (!FD->hasAttr()) + return false; + + const auto *Attr = FD->getAttr(); + StringRef CalleeCompartment = Attr->getCompartmentName(); + StringRef CallerCompartment = + C.getASTContext().getLangOpts().CheriCompartmentName; + + return CalleeCompartment != CallerCompartment; +} + +void CheriotHeapChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const auto *PreFN = PreFnMap.lookup(Call)) + (*PreFN)(this, Call, C); +} + +void CheriotHeapChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + if (const auto *PostFN = PostFnMap.lookup(Call)) + (*PostFN)(this, Call, C); + + if (isCrossCompartmentCall(Call, C)) { + // All ephemeral claims are implicitly released at each cross-compartment + // call. + ProgramStateRef State = C.getState(); + for (const auto &[Sym, HPS] : State->get()) { + if (!HPS.isEphemeral()) + continue; + + State = State->set(Sym, HeapPtrState::InvalidatedEphemeral); + } + C.addTransition(State); + } +} + +static void printSymbolNameForError(llvm::raw_ostream &os, SymbolRef Sym) { + if (const auto *SymName = dyn_cast(Sym)) { + if (const VarRegion *VR = + dyn_cast_or_null(SymName->getOriginRegion())) { + if (const VarDecl *VD = VR->getDecl()) + os << "'" << VD->getNameAsString() << "' "; + } + } +} + +void CheriotHeapChecker::preCheckPointer(const CallEvent &Call, + CheckerContext &C) const { + // If the pointer argument points to memory that could be heap memory, + // check that it is in Claimed state, and report an error if not. This should + // make sure to include arguments that are pointers to known stack or constant + // memory. + SVal PtrVal = Call.getArgSVal(0); + SymbolRef Sym = PtrVal.getAsLocSymbol(); + if (!Sym) + return; + + const HeapPtrState *HPS = C.getState()->get(Sym); + if (!HPS || HPS->isEffectivelyClaimed()) + return; + + // Generate an error if the pointer doesn't have a valid claim + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "check_pointer called on potential heap pointer "; + printSymbolNameForError(os, Sym); + + if (HPS->isUnclaimed()) + os << "without a valid claim."; + else if (HPS->isInvalidatedEphemeral()) + os << "after its ephemeral claim was released by a cross-compartment call."; + + auto Report = + std::make_unique(InvalidUseBugType, os.str(), N); + Report->markInteresting(Sym); + C.emitReport(std::move(Report)); +} + +void CheriotHeapChecker::postHeapAddressIsValid(const CallEvent &Call, + CheckerContext &C) const { + SymbolRef Sym = Call.getArgSVal(0).getAsLocSymbol(); + if (!Sym) + return; + + SVal RetVal = Call.getReturnValue(); + ProgramStateRef State = C.getState(); + const HeapPtrState *HPS = State->get(Sym); + if (!HPS) + return; + + ProgramStateRef StateTrue, StateFalse; + std::tie(StateTrue, StateFalse) = + State->assume(RetVal.castAs()); + + if (StateTrue) + C.addTransition(StateTrue); + + if (StateFalse) { + StateFalse = StateFalse->remove(Sym); + C.addTransition(StateFalse); + } +} + +void CheriotHeapChecker::postHeapClaim(const CallEvent &Call, + CheckerContext &C) const { + SymbolRef Sym = Call.getArgSVal(1).getAsLocSymbol(); + if (!Sym) + return; + + ProgramStateRef State = + C.getState()->set(Sym, HeapPtrState::Claimed); + C.addTransition(State); +} + +void CheriotHeapChecker::postHeapClaimEphemeral(const CallEvent &Call, + CheckerContext &C) const { + SymbolRef Sym1 = Call.getArgSVal(1).getAsLocSymbol(); + SymbolRef Sym2 = Call.getArgSVal(2).getAsLocSymbol(); + + ProgramStateRef State = C.getState(); + + // All existing ephemeral claims are released + for (const auto &[Sym, HPS] : State->get()) { + if (HPS.isEphemeral()) + State = State->set(Sym, HeapPtrState::Unclaimed); + } + + if (Sym1) + State = State->set(Sym1, HeapPtrState::Ephemeral); + if (Sym2) + State = State->set(Sym2, HeapPtrState::Ephemeral); + + C.addTransition(State); +} + +void CheriotHeapChecker::postHeapFree(const CallEvent &Call, + CheckerContext &C) const { + SymbolRef Sym = Call.getArgSVal(1).getAsLocSymbol(); + if (!Sym) + return; + + ProgramStateRef State = C.getState(); + const HeapPtrState *HPS = State->get(Sym); + if (!HPS || !HPS->isEffectivelyClaimed()) + return; + + State = State->set(Sym, HeapPtrState::Unclaimed); + C.addTransition(State); +} + +void CheriotHeapChecker::postHeapFreeAll(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + for (const auto &[Sym, HPS] : State->get()) { + State = State->set(Sym, HeapPtrState::Unclaimed); + } + C.addTransition(State); +} + +void CheriotHeapChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + SymbolRef Sym = Loc.getLocSymbolInBase(); + if (!Sym) + return; + + // This is a dereference of some form, so this is a bug if the + // claim has already been released either by freeing or invalidation. + const HeapPtrState *HPS = C.getState()->get(Sym); + if (!HPS || HPS->isEffectivelyClaimed()) + return; + + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + if (IsLoad) + os << "Read of heap pointer "; + else + os << "Store through heap pointer "; + printSymbolNameForError(os, Sym); + if (HPS->isUnclaimed()) + os << "without a valid claim."; + else if (HPS->isInvalidatedEphemeral()) + os << "after its ephemeral claim was released by a cross-compartment call."; + + auto Report = + std::make_unique(InvalidUseBugType, os.str(), N); + if (S) + Report->addRange(S->getSourceRange()); + Report->markInteresting(Sym); + C.emitReport(std::move(Report)); +} + +ProgramStateRef CheriotHeapChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + // FIXME: Unsure why this is needed. + if (Kind == PointerEscapeKind::PSK_EscapeOnBind) + return State; + if (Call && PostFnMap.lookup(*Call)) + return State; + for (SymbolRef Sym : Escaped) { + if (State->get(Sym)) + State = State->set(Sym, HeapPtrState::Escaped); + } + return State; +} + +void CheriotHeapChecker::checkBeginFunction(CheckerContext &C) const { + const LocationContext *LC = C.getLocationContext(); + const Decl *D = C.getLocationContext()->getDecl(); + const FunctionDecl *FD = dyn_cast_or_null(D); + + // Only start analysis paths at functions + if (LC->inTopFrame() && (!FD || !FD->hasAttr())) { + C.addSink(); + return; + } + + // Mark all pointer arguments as initially unclaimed + ProgramStateRef State = C.getState(); + bool Modified = false; + for (unsigned i = 0; i < FD->getNumParams(); ++i) { + const ParmVarDecl *Param = FD->getParamDecl(i); + if (!Param->getType()->isPointerType()) + continue; + + const VarRegion *VR = State->getRegion(Param, LC); + if (SymbolRef Sym = State->getSVal(VR).getAsLocSymbol()) { + State = State->set(Sym, HeapPtrState::Unclaimed); + Modified = true; + } + } + + // Only proceed with the analysis if at least one argument was a pointer. + if (Modified) + C.addTransition(State); + else + C.addSink(); +} + +void CheriotHeapChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &C) const { + // Don't report leaks on returns from intra-compartment helper functions, + // since the caller might contain the relevant releases. + const Decl *D = C.getLocationContext()->getDecl(); + const FunctionDecl *FD = dyn_cast_or_null(D); + if (!FD || !FD->hasAttr()) + return; + + ProgramStateRef State = C.getState(); + for (const auto &[Sym, HPS] : State->get()) { + if (HPS.isClaimed()) + reportLeak(Sym, C); + } +} + +void CheriotHeapChecker::reportLeak(SymbolRef Sym, CheckerContext &C) const { + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + std::string Name = ""; + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "Claim on pointer "; + printSymbolNameForError(os, Sym); + os << "must be released with heap_free or heap_free_all before returning " + "from a compartment call."; + + auto Report = + std::make_unique(LeakBugType, os.str(), N); + Report->markInteresting(Sym); + C.emitReport(std::move(Report)); +} + +// Registration +void ento::registerCheriotHeapChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterCheriotHeapChecker(const CheckerManager &mgr) { + const auto &TI = mgr.getASTContext().getTargetInfo(); + return TI.getTriple().getArch() == llvm::Triple::riscv32 && + TI.hasFeature("xcheriot"); +} diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 934941c5fceb9..9dd8664f10f56 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -28,6 +28,7 @@ add_clang_library(clangStaticAnalyzerCheckers CHERI/AllocationChecker.cpp CHERI/CapabilityCopyChecker.cpp CHERI/CheriAPIModelling.cpp + CHERI/CheriotHeapChecker.cpp CHERI/CHERIUtils.cpp CHERI/PointerSizeAssumptionsChecker.cpp CHERI/ProvenanceSourceChecker.cpp diff --git a/clang/test/Analysis/Checkers/CHERI/cheriot-heap.c b/clang/test/Analysis/Checkers/CHERI/cheriot-heap.c new file mode 100644 index 0000000000000..c9a5879235aa2 --- /dev/null +++ b/clang/test/Analysis/Checkers/CHERI/cheriot-heap.c @@ -0,0 +1,192 @@ +// RUN: %clang_cc1 -triple riscv32e-none-cheriotrtos -target-feature +xcheriot -target-abi cheriot -cheri-compartment=test -analyze -verify %s \ +// RUN: -analyzer-checker=cheriot.CheriotHeap + +int heap_claim(void* heapCapability, void* pointer); +int heap_free(void* heapCapability, void* ptr); +int heap_free_all(void* heapCapability); +int heap_claim_ephemeral(void* timeout, const void* ptr, const void* ptr2); +char check_pointer(const volatile void* ptr, int space, + unsigned int rawPermissions, char checkStackNeeded); +_Bool heap_address_is_valid(void *); + +__attribute__((cheri_compartment("bar"))) void crossCompartmentCall(void); + +__attribute__((cheri_compartment("test"))) +void test_1(void* p) { + heap_claim(0, p); // expected-warning{{Claim on pointer 'p' must be released with heap_free or heap_free_all before returning from a compartment call}} +} + +__attribute__((cheri_compartment("test"))) +void test_2(void* p) { + heap_claim(0, p); + heap_free(0, p); // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_3(void* p) { + heap_claim(0, p); + heap_free_all(0); // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_4(void* p1, void *p2) { + heap_claim(0, p1); + heap_claim(0, p2); + heap_free(0, p1); // expected-warning{{Claim on pointer 'p2' must be released with heap_free or heap_free_all before returning from a compartment call}} +} + +__attribute__((cheri_compartment("test"))) +void test_5(int* p) { + heap_claim(0, p); + *p = 0; + heap_free(0, p); + *p = 1; // expected-warning{{Store through heap pointer 'p' without a valid claim}} +} + +__attribute__((cheri_compartment("test"))) +void test_6(int* p) { + heap_claim(0, p); + *p = 0; + heap_free_all(0); + *p = 1; // expected-warning{{Store through heap pointer 'p' without a valid claim}} +} + +__attribute__((cheri_compartment("test"))) +void test_7(int* p) { + heap_claim_ephemeral(0, p, 0); + *p = 0; + heap_free(0, p); + *p = 1; // expected-warning{{Store through heap pointer 'p' without a valid claim}} +} + +__attribute__((cheri_compartment("test"))) +void test_8(int* p) { + heap_claim_ephemeral(0, p, 0); + *p = 0; + heap_free_all(0); + *p = 1; // expected-warning{{Store through heap pointer 'p' without a valid claim}} +} + +__attribute__((cheri_compartment("test"))) +void test_9(int* p) { + heap_claim_ephemeral(0, p, 0); + *p = 0; // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_10(int* p) { + heap_claim_ephemeral(0, p, 0); + *p = 0; // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_11(int* p) { + heap_claim_ephemeral(0, p, 0); + crossCompartmentCall(); + *p = 0; // expected-warning{{Store through heap pointer 'p' after its ephemeral claim was released by a cross-compartment call}} +} + +static void test_12_helper(int *p) { heap_claim(0, p); } +__attribute__((cheri_compartment("test"))) +void test_12(int* p) { + test_12_helper(p); // expected-warning{{Claim on pointer 'p' must be released with heap_free or heap_free_all before returning from a compartment call}} +} + +static void test_13_helper(int *p) { heap_claim(0, p); } +__attribute__((cheri_compartment("test"))) +void test_13(int* p) { + test_13_helper(p); + heap_free(0, p); // no warn +} + +static void test_14_helper(int *p) { heap_claim(0, p); } +__attribute__((cheri_compartment("test"))) +void test_14(int* p) { + test_14_helper(p); + heap_free_all(0); // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_15(int* p) { + check_pointer(p, 0, 0, 0); // expected-warning{{check_pointer called on potential heap pointer 'p' without a valid claim}} +} + +__attribute__((cheri_compartment("test"))) +void test_16(int* p) { + int i = 0; + check_pointer(&i, 0, 0, 0); // no warn +} + +int test_17_global = 1; +__attribute__((cheri_compartment("test"))) +void test_17(int* p) { + check_pointer(&test_17_global, 0, 0, 0); // no warn +} + +static const int test_18_cst = 1; +__attribute__((cheri_compartment("test"))) +void test_18(int* p) { + check_pointer(&test_18_cst, 0, 0, 0); // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_20(int* p) { + if (!heap_address_is_valid(p)) + check_pointer(p, 0, 0, 0); // no warn +} + +__attribute__((cheri_compartment("test"))) +void test_21(int* p) { + if (heap_address_is_valid(p)) + check_pointer(p, 0, 0, 0); // expected-warning{{check_pointer called on potential heap pointer 'p' without a valid claim}} +} + +__attribute__((cheri_compartment("test"))) +void test_22(int* p) { + *p = 1; // expected-warning{{Store through heap pointer 'p' without a valid claim}} + heap_claim(0, p); + *p = 0; + heap_free(0, p); +} + +__attribute__((cheri_compartment("test"))) +void test_23(int* p) { + if (!heap_address_is_valid(p)) + *p = 1; // no warn + heap_claim(0, p); + *p = 0; + heap_free(0, p); +} + +__attribute__((cheri_compartment("test"))) +void test_24(int* p) { + if (!heap_address_is_valid(p)) + *p = 1; // no warn + else + heap_claim(0, p); // no warn + check_pointer(p, 0, 0, 0); + *p = 0; + heap_free(0, p); +} + +__attribute__((cheri_compartment("test"))) +void test_25(int* p) { + if (!heap_address_is_valid(p)) + *p = 1; // no warn + else + heap_claim(0, p); // no warn + check_pointer(p, 0, 0, 0); + *p = 0; // expected-warning{{Claim on pointer 'p' must be released with heap_free or heap_free_all before returning from a compartment call}} +} + +__attribute__((cheri_compartment("test"))) +void test_26(int* p) { + if (!heap_address_is_valid(p)) + *p = 1; // no warn + else + heap_claim(0, p); // no warn + check_pointer(p, 0, 0, 0); + *p = 0; + if (heap_address_is_valid(p)) + heap_free(0, p); // no warn +}