32 using namespace clang;
36 class MIGChecker :
public Checker<check::PostCall, check::PreStmt<ReturnStmt>,
38 BugType BT{
this,
"Use-after-free (MIG calling convention violation)",
46 std::vector<std::pair<CallDescription, unsigned>> Deallocators = {
47 #define CALL(required_args, deallocated_arg, ...) \ 48 {{{__VA_ARGS__}, required_args}, deallocated_arg} 53 CALL(3, 1,
"vm_deallocate"),
54 CALL(3, 1,
"mach_vm_deallocate"),
55 CALL(2, 0,
"mig_deallocate"),
56 CALL(2, 1,
"mach_port_deallocate"),
57 CALL(1, 0,
"device_deallocate"),
58 CALL(1, 0,
"iokit_remove_connect_reference"),
59 CALL(1, 0,
"iokit_remove_reference"),
60 CALL(1, 0,
"iokit_release_port"),
61 CALL(1, 0,
"ipc_port_release"),
62 CALL(1, 0,
"ipc_port_release_sonce"),
63 CALL(1, 0,
"ipc_voucher_attr_control_release"),
64 CALL(1, 0,
"ipc_voucher_release"),
65 CALL(1, 0,
"lock_set_dereference"),
66 CALL(1, 0,
"memory_object_control_deallocate"),
67 CALL(1, 0,
"pset_deallocate"),
68 CALL(1, 0,
"semaphore_dereference"),
69 CALL(1, 0,
"space_deallocate"),
70 CALL(1, 0,
"space_inspect_deallocate"),
71 CALL(1, 0,
"task_deallocate"),
72 CALL(1, 0,
"task_inspect_deallocate"),
73 CALL(1, 0,
"task_name_deallocate"),
74 CALL(1, 0,
"thread_deallocate"),
75 CALL(1, 0,
"thread_inspect_deallocate"),
76 CALL(1, 0,
"upl_deallocate"),
77 CALL(1, 0,
"vm_map_deallocate"),
82 CALL(1, 0,
"IOUserClient",
"releaseAsyncReference64"),
83 CALL(1, 0,
"IOUserClient",
"releaseNotificationPort"),
89 void checkReturnAux(
const ReturnStmt *RS, CheckerContext &C)
const;
92 void checkPostCall(
const CallEvent &Call, CheckerContext &C)
const;
100 void checkPreStmt(
const ReturnStmt *RS, CheckerContext &C)
const {
101 checkReturnAux(RS, C);
103 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &C)
const {
104 checkReturnAux(RS, C);
118 bool IncludeBaseRegions =
false) {
120 SymbolRef Sym = V.getAsSymbol(IncludeBaseRegions);
129 while (
const MemRegion *MR = Sym->getOriginRegion()) {
130 const auto *VR = dyn_cast<VarRegion>(MR);
131 if (VR && VR->hasStackParametersStorage() &&
132 VR->getStackFrame()->inTopFrame())
133 return cast<ParmVarDecl>(VR->getDecl());
135 const SymbolicRegion *SR = MR->getSymbolicBase();
139 Sym = SR->getSymbol();
147 assert(LC &&
"Unknown location context");
164 if (!AC->getReturnType(C.getASTContext())
165 .getCanonicalType()->isSignedIntegerType())
169 if (D->
hasAttr<MIGServerRoutineAttr>())
173 if (
const auto *MD = dyn_cast<CXXMethodDecl>(D))
174 for (
const auto *OMD: MD->overridden_methods())
175 if (OMD->hasAttr<MIGServerRoutineAttr>())
181 void MIGChecker::checkPostCall(
const CallEvent &Call, CheckerContext &C)
const {
182 if (Call.isCalled(OsRefRetain)) {
190 C.addTransition(C.getState()->add<RefCountedParameters>(PVD));
198 auto I = llvm::find_if(Deallocators,
199 [&](
const std::pair<CallDescription, unsigned> &Item) {
200 return Call.isCalled(Item.first);
202 if (I == Deallocators.end())
206 unsigned ArgIdx = I->second;
207 SVal Arg = Call.getArgSVal(ArgIdx);
209 if (!PVD || State->contains<RefCountedParameters>(PVD))
212 const NoteTag *T = C.getNoteTag([
this, PVD](BugReport &BR) -> std::string {
213 if (&BR.getBugType() != &BT)
216 llvm::raw_svector_ostream
OS(Str);
217 OS <<
"Value passed through parameter '" << PVD->
getName()
218 <<
"\' is deallocated";
221 C.addTransition(State->set<ReleasedParameter>(
true), T);
229 if (!State->isNull(V).isConstrainedFalse())
232 SValBuilder &SVB = C.getSValBuilder();
236 static const int MigNoReply = -305;
237 V = SVB.evalEQ(C.getState(),
V, SVB.makeIntVal(MigNoReply, ACtx.
IntTy));
238 if (!State->isNull(V).isConstrainedTrue())
245 void MIGChecker::checkReturnAux(
const ReturnStmt *RS, CheckerContext &C)
const {
266 if (!State->get<ReleasedParameter>())
269 SVal
V = C.getSVal(RS);
273 ExplodedNode *N = C.generateErrorNode();
277 auto R = llvm::make_unique<BugReport>(
279 "MIG callback fails with error after deallocating argument value. " 280 "This is a use-after-free vulnerability because the caller will try to " 281 "deallocate it again",
285 bugreporter::trackExpressionValue(N, RS->
getRetValue(), *R,
false);
286 C.emitReport(std::move(R));
289 void ento::registerMIGChecker(CheckerManager &Mgr) {
290 Mgr.registerChecker<MIGChecker>();
293 bool ento::shouldRegisterMIGChecker(
const LangOptions &LO) {
const char *const MemoryError
const SymExpr * SymbolRef
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.
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)