33 using namespace clang;
37 class MIGChecker :
public Checker<check::PostCall, check::PreStmt<ReturnStmt>,
39 BugType BT{
this,
"Use-after-free (MIG calling convention violation)",
47 std::vector<std::pair<CallDescription, unsigned>> Deallocators = {
48 #define CALL(required_args, deallocated_arg, ...) \ 49 {{{__VA_ARGS__}, required_args}, deallocated_arg} 54 CALL(3, 1,
"vm_deallocate"),
55 CALL(3, 1,
"mach_vm_deallocate"),
56 CALL(2, 0,
"mig_deallocate"),
57 CALL(2, 1,
"mach_port_deallocate"),
58 CALL(1, 0,
"device_deallocate"),
59 CALL(1, 0,
"iokit_remove_connect_reference"),
60 CALL(1, 0,
"iokit_remove_reference"),
61 CALL(1, 0,
"iokit_release_port"),
62 CALL(1, 0,
"ipc_port_release"),
63 CALL(1, 0,
"ipc_port_release_sonce"),
64 CALL(1, 0,
"ipc_voucher_attr_control_release"),
65 CALL(1, 0,
"ipc_voucher_release"),
66 CALL(1, 0,
"lock_set_dereference"),
67 CALL(1, 0,
"memory_object_control_deallocate"),
68 CALL(1, 0,
"pset_deallocate"),
69 CALL(1, 0,
"semaphore_dereference"),
70 CALL(1, 0,
"space_deallocate"),
71 CALL(1, 0,
"space_inspect_deallocate"),
72 CALL(1, 0,
"task_deallocate"),
73 CALL(1, 0,
"task_inspect_deallocate"),
74 CALL(1, 0,
"task_name_deallocate"),
75 CALL(1, 0,
"thread_deallocate"),
76 CALL(1, 0,
"thread_inspect_deallocate"),
77 CALL(1, 0,
"upl_deallocate"),
78 CALL(1, 0,
"vm_map_deallocate"),
83 CALL(1, 0,
"IOUserClient",
"releaseAsyncReference64"),
84 CALL(1, 0,
"IOUserClient",
"releaseNotificationPort"),
90 void checkReturnAux(
const ReturnStmt *RS, CheckerContext &C)
const;
93 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
101 void checkPreStmt(
const ReturnStmt *RS, CheckerContext &C)
const {
102 checkReturnAux(RS, C);
104 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &C)
const {
105 checkReturnAux(RS, C);
119 bool IncludeBaseRegions =
false) {
121 SymbolRef Sym = V.getAsSymbol(IncludeBaseRegions);
130 while (
const MemRegion *MR = Sym->getOriginRegion()) {
131 const auto *VR = dyn_cast<VarRegion>(MR);
132 if (VR && VR->hasStackParametersStorage() &&
133 VR->getStackFrame()->inTopFrame())
134 return cast<ParmVarDecl>(VR->getDecl());
136 const SymbolicRegion *SR = MR->getSymbolicBase();
140 Sym = SR->getSymbol();
148 assert(LC &&
"Unknown location context");
165 if (!AC->getReturnType(C.getASTContext())
166 .getCanonicalType()->isSignedIntegerType())
170 if (D->
hasAttr<MIGServerRoutineAttr>())
174 if (
const auto *MD = dyn_cast<CXXMethodDecl>(D))
175 for (
const auto *OMD: MD->overridden_methods())
176 if (OMD->hasAttr<MIGServerRoutineAttr>())
182 void MIGChecker::checkPostCall(
const CallEvent &Call, CheckerContext &C)
const {
183 if (Call.isCalled(OsRefRetain)) {
191 C.addTransition(C.getState()->add<RefCountedParameters>(PVD));
199 auto I = llvm::find_if(Deallocators,
200 [&](
const std::pair<CallDescription, unsigned> &Item) {
201 return Call.isCalled(Item.first);
203 if (I == Deallocators.end())
207 unsigned ArgIdx = I->second;
208 SVal Arg = Call.getArgSVal(ArgIdx);
210 if (!PVD || State->contains<RefCountedParameters>(PVD))
213 const NoteTag *T = C.getNoteTag([
this, PVD](BugReport &BR) -> std::string {
214 if (&BR.getBugType() != &BT)
217 llvm::raw_svector_ostream
OS(Str);
218 OS <<
"Value passed through parameter '" << PVD->
getName()
219 <<
"\' is deallocated";
222 C.addTransition(State->set<ReleasedParameter>(
true), T);
230 if (!State->isNull(V).isConstrainedFalse())
233 SValBuilder &SVB = C.getSValBuilder();
237 static const int MigNoReply = -305;
238 V = SVB.evalEQ(C.getState(),
V, SVB.makeIntVal(MigNoReply, ACtx.
IntTy));
239 if (!State->isNull(V).isConstrainedTrue())
246 void MIGChecker::checkReturnAux(
const ReturnStmt *RS, CheckerContext &C)
const {
267 if (!State->get<ReleasedParameter>())
270 SVal
V = C.getSVal(RS);
274 ExplodedNode *N = C.generateErrorNode();
278 auto R = std::make_unique<PathSensitiveBugReport>(
280 "MIG callback fails with error after deallocating argument value. " 281 "This is a use-after-free vulnerability because the caller will try to " 282 "deallocate it again",
286 bugreporter::trackExpressionValue(N, RS->
getRetValue(), *R,
287 bugreporter::TrackingKind::Thorough,
false);
288 C.emitReport(std::move(R));
291 void ento::registerMIGChecker(CheckerManager &Mgr) {
292 Mgr.registerChecker<MIGChecker>();
295 bool ento::shouldRegisterMIGChecker(
const LangOptions &LO) {
const char *const MemoryError
static const ParmVarDecl * getOriginParam(SVal V, CheckerContext &C, bool IncludeBaseRegions=false)
Decl - This represents one declaration (or definition), e.g.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Represents a parameter to a function.
const SymExpr * SymbolRef
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
This class represents a description of a function call using the number of arguments and the name of ...
const LocationContext * getParent() const
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy...
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Dataflow Directional Tag Classes.
static bool mayBeSuccess(SVal V, CheckerContext &C)
const Decl * getDecl() const
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
const StackFrameContext * getStackFrame() const
#define CALL(required_args, deallocated_arg,...)
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
static bool isInMIGCall(CheckerContext &C)