1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 /**
6 * This checker implements the "can run script" analysis. The idea is to detect
7 * functions that can run script that are being passed reference-counted
8 * arguments (including "this") whose refcount might go to zero as a result of
9 * the script running. We want to prevent that.
10 *
11 * The approach is to attempt to enforce the following invariants on the call
12 * graph:
13 *
14 * 1) Any caller of a MOZ_CAN_RUN_SCRIPT function is itself MOZ_CAN_RUN_SCRIPT.
15 * 2) If a virtual MOZ_CAN_RUN_SCRIPT method overrides a base class method,
16 * that base class method is also MOZ_CAN_RUN_SCRIPT.
17 *
18 * Invariant 2 ensures that we don't accidentally call a MOZ_CAN_RUN_SCRIPT
19 * function via a base-class virtual call. Invariant 1 ensures that
20 * the property of being able to run script propagates up the callstack. There
21 * is an opt-out for invariant 1: A function (declaration _or_ implementation)
22 * can be decorated with MOZ_CAN_RUN_SCRIPT_BOUNDARY to indicate that we do not
23 * require it or any of its callers to be MOZ_CAN_RUN_SCRIPT even if it calls
24 * MOZ_CAN_RUN_SCRIPT functions.
25 *
26 * There are two known holes in invariant 1, apart from the
27 * MOZ_CAN_RUN_SCRIPT_BOUNDARY opt-out:
28 *
29 * - Functions called via function pointers can be MOZ_CAN_RUN_SCRIPT even if
30 * their caller is not, because we have no way to determine from the function
31 * pointer what function is being called.
32 * - MOZ_CAN_RUN_SCRIPT destructors can happen in functions that are not
33 * MOZ_CAN_RUN_SCRIPT.
34 * https://bugzilla.mozilla.org/show_bug.cgi?id=1535523 tracks this.
35 *
36 * Given those invariants we then require that when calling a MOZ_CAN_RUN_SCRIPT
37 * function all refcounted arguments (including "this") satisfy one of these
38 * conditions:
39 * a) The argument is held via a strong pointer on the stack.
40 * b) The argument is a const strong pointer member of "this". We know "this"
41 * is being kept alive, and a const strong pointer member can't drop its ref
42 * until "this" dies.
43 * c) The argument is an argument of the caller (and hence held by a strong
44 * pointer somewhere higher up the callstack).
45 * d) The argument is explicitly annotated with MOZ_KnownLive, which indicates
46 * that something is guaranteed to keep it alive (e.g. it's rooted via a JS
47 * reflector).
48 * e) The argument is constexpr and therefore cannot disappear.
49 */
50
51 #include "CanRunScriptChecker.h"
52 #include "CustomMatchers.h"
53 #include "clang/Lex/Lexer.h"
54
registerMatchers(MatchFinder * AstMatcher)55 void CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) {
56 auto Refcounted = qualType(hasDeclaration(cxxRecordDecl(isRefCounted())));
57 auto StackSmartPtr = ignoreTrivials(declRefExpr(to(varDecl(
58 hasAutomaticStorageDuration(), hasType(isSmartPtrToRefCounted())))));
59 auto ConstMemberOfThisSmartPtr =
60 memberExpr(hasType(isSmartPtrToRefCounted()), hasType(isConstQualified()),
61 hasObjectExpression(cxxThisExpr()));
62 // A smartptr can be known-live for three reasons:
63 // 1) It's declared on the stack.
64 // 2) It's a const member of "this". We know "this" is alive (recursively)
65 // and const members can't change their value hence can't drop their
66 // reference until "this" gets destroyed.
67 // 3) It's an immediate temporary being constructed at the point where the
68 // call is happening.
69 auto KnownLiveSmartPtr = anyOf(
70 StackSmartPtr, ConstMemberOfThisSmartPtr,
71 ignoreTrivials(cxxConstructExpr(hasType(isSmartPtrToRefCounted()))));
72
73 auto MozKnownLiveCall =
74 ignoreTrivials(callExpr(callee(functionDecl(hasName("MOZ_KnownLive")))));
75
76 // Params of the calling function are presumed live, because it itself should
77 // be MOZ_CAN_RUN_SCRIPT. Note that this is subject to
78 // https://bugzilla.mozilla.org/show_bug.cgi?id=1537656 at the moment.
79 auto KnownLiveParam = anyOf(
80 // "this" is OK
81 cxxThisExpr(),
82 // A parameter of the calling function is OK.
83 declRefExpr(to(parmVarDecl())));
84
85 auto KnownLiveMemberOfParam =
86 memberExpr(hasKnownLiveAnnotation(),
87 hasObjectExpression(ignoreTrivials(KnownLiveParam)));
88
89 // A matcher that matches various things that are known to be live directly,
90 // without making any assumptions about operators.
91 auto KnownLiveBase = anyOf(
92 // Things that are known to be a stack or immutable refptr.
93 KnownLiveSmartPtr,
94 // MOZ_KnownLive() calls.
95 MozKnownLiveCall,
96 // Params of the caller function.
97 KnownLiveParam,
98 // Members of the params that are marked as MOZ_KNOWN_LIVE
99 KnownLiveMemberOfParam,
100 // Constexpr things.
101 declRefExpr(to(varDecl(isConstexpr()))));
102
103 // A matcher that matches various known-live things that don't involve
104 // non-unary operators.
105 auto KnownLiveSimple = anyOf(
106 // Things that are just known live.
107 KnownLiveBase,
108 // Method calls on a live things that are smart ptrs. Note that we don't
109 // want to allow general method calls on live things, because those can
110 // return non-live objects (e.g. consider "live_pointer->foo()" as an
111 // example). For purposes of this analysis we are assuming the method
112 // calls on smart ptrs all just return the pointer inside,
113 cxxMemberCallExpr(
114 on(allOf(hasType(isSmartPtrToRefCounted()), KnownLiveBase))),
115 // operator* or operator-> on a thing that is already known to be live.
116 cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("*"),
117 hasOverloadedOperatorName("->")),
118 hasAnyArgument(KnownLiveBase), argumentCountIs(1)),
119 // A dereference on a thing that is known to be live. This is _not_
120 // caught by the "operator* or operator->" clause above, because
121 // cxxOperatorCallExpr() only catches cases when a class defines
122 // operator*. The default (built-in) operator* matches unaryOperator()
123 // instead.),
124 unaryOperator(
125 unaryDereferenceOperator(),
126 hasUnaryOperand(
127 // If we're doing *someArg, the argument of the dereference is an
128 // ImplicitCastExpr LValueToRValue which has the DeclRefExpr as an
129 // argument. We could try to match that explicitly with a custom
130 // matcher (none of the built-in matchers seem to match on the
131 // thing being cast for an implicitCastExpr), but it's simpler to
132 // just use ignoreTrivials to strip off the cast.
133 ignoreTrivials(KnownLiveBase))),
134 // Taking a pointer to a live reference. We explicitly want to exclude
135 // things that are not of type reference-to-refcounted or type refcounted,
136 // because if someone takes a pointer to a pointer to refcounted or a
137 // pointer to a smart ptr and passes those in to a callee that definitely
138 // does not guarantee liveness; in fact the callee could modify those
139 // things! In practice they would be the wrong type anyway, though, so
140 // it's hard to add a test for this.
141 unaryOperator(hasOperatorName("&"),
142 hasUnaryOperand(allOf(anyOf(hasType(references(Refcounted)),
143 hasType(Refcounted)),
144 ignoreTrivials(KnownLiveBase)))));
145
146 auto KnownLive = anyOf(
147 // Anything above, of course.
148 KnownLiveSimple,
149 // Conditional operators where both arms are live.
150 conditionalOperator(hasFalseExpression(ignoreTrivials(KnownLiveSimple)),
151 hasTrueExpression(ignoreTrivials(KnownLiveSimple)))
152 // We're not handling cases like a dereference of a conditional operator,
153 // mostly because handling a dereference in general is so ugly. I
154 // _really_ wish I could just write a recursive matcher here easily.
155 );
156
157 auto InvalidArg = ignoreTrivialsConditional(
158 // We want to consider things if there is anything refcounted involved,
159 // including in any of the trivials that we otherwise strip off.
160 anyOf(hasType(Refcounted), hasType(pointsTo(Refcounted)),
161 hasType(references(Refcounted)), hasType(isSmartPtrToRefCounted())),
162 // We want to find any expression,
163 expr(
164 // which is not known live,
165 unless(KnownLive),
166 // and which is not a default arg with value nullptr, since those are
167 // always safe,
168 unless(cxxDefaultArgExpr(isNullDefaultArg())),
169 // and which is not a literal nullptr,
170 unless(cxxNullPtrLiteralExpr()), expr().bind("invalidArg")));
171
172 // A matcher which will mark the first invalid argument it finds invalid, but
173 // will always match, even if it finds no invalid arguments, so it doesn't
174 // preclude other matchers from running and maybe finding invalid args.
175 auto OptionalInvalidExplicitArg = anyOf(
176 // We want to find any argument which is invalid.
177 hasAnyArgument(InvalidArg),
178
179 // This makes this matcher optional.
180 anything());
181
182 // Please note that the hasCanRunScriptAnnotation() matchers are not present
183 // directly in the cxxMemberCallExpr, callExpr and constructExpr matchers
184 // because we check that the corresponding functions can run script later in
185 // the checker code.
186 AstMatcher->addMatcher(
187 expr(
188 anyOf(
189 // We want to match a method call expression,
190 cxxMemberCallExpr(
191 // which optionally has an invalid arg,
192 OptionalInvalidExplicitArg,
193 // or which optionally has an invalid this argument,
194 anyOf(on(InvalidArg), anything()), expr().bind("callExpr")),
195 // or a regular call expression,
196 callExpr(
197 // which optionally has an invalid arg.
198 OptionalInvalidExplicitArg, expr().bind("callExpr")),
199 // or a construct expression,
200 cxxConstructExpr(
201 // which optionally has an invalid arg.
202 OptionalInvalidExplicitArg, expr().bind("constructExpr"))),
203
204 anyOf(
205 // We want to match the parent function.
206 forFunction(functionDecl().bind("nonCanRunScriptParentFunction")),
207
208 // ... optionally.
209 anything())),
210 this);
211 }
212
onStartOfTranslationUnit()213 void CanRunScriptChecker::onStartOfTranslationUnit() {
214 IsFuncSetBuilt = false;
215 CanRunScriptFuncs.clear();
216 }
217
218 namespace {
219 /// This class is a callback used internally to match function declarations with
220 /// the MOZ_CAN_RUN_SCRIPT annotation, adding these functions to the
221 /// can-run-script function set and making sure the functions they override (if
222 /// any) also have the annotation.
223 class FuncSetCallback : public MatchFinder::MatchCallback {
224 public:
FuncSetCallback(CanRunScriptChecker & Checker,std::unordered_set<const FunctionDecl * > & FuncSet)225 FuncSetCallback(CanRunScriptChecker &Checker,
226 std::unordered_set<const FunctionDecl *> &FuncSet)
227 : CanRunScriptFuncs(FuncSet), Checker(Checker) {}
228
229 void run(const MatchFinder::MatchResult &Result) override;
230
231 private:
232 /// This method checks the methods overriden by the given parameter.
233 void checkOverriddenMethods(const CXXMethodDecl *Method);
234
235 std::unordered_set<const FunctionDecl *> &CanRunScriptFuncs;
236 CanRunScriptChecker &Checker;
237 };
238
run(const MatchFinder::MatchResult & Result)239 void FuncSetCallback::run(const MatchFinder::MatchResult &Result) {
240 const FunctionDecl *Func;
241 if (auto *Lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda")) {
242 Func = Lambda->getCallOperator();
243 if (!Func || !hasCustomAttribute<moz_can_run_script>(Func))
244 return;
245 } else {
246 Func = Result.Nodes.getNodeAs<FunctionDecl>("canRunScriptFunction");
247 }
248
249 CanRunScriptFuncs.insert(Func);
250
251 // If this is a method, we check the methods it overrides.
252 if (auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
253 checkOverriddenMethods(Method);
254 }
255 }
256
checkOverriddenMethods(const CXXMethodDecl * Method)257 void FuncSetCallback::checkOverriddenMethods(const CXXMethodDecl *Method) {
258 for (auto OverriddenMethod : Method->overridden_methods()) {
259 if (!hasCustomAttribute<moz_can_run_script>(OverriddenMethod)) {
260 const char *ErrorNonCanRunScriptOverridden =
261 "functions marked as MOZ_CAN_RUN_SCRIPT cannot override functions "
262 "that are not marked MOZ_CAN_RUN_SCRIPT";
263 const char *NoteNonCanRunScriptOverridden =
264 "overridden function declared here";
265
266 Checker.diag(Method->getLocation(), ErrorNonCanRunScriptOverridden,
267 DiagnosticIDs::Error);
268 Checker.diag(OverriddenMethod->getLocation(),
269 NoteNonCanRunScriptOverridden, DiagnosticIDs::Note);
270 }
271 }
272 }
273 } // namespace
274
buildFuncSet(ASTContext * Context)275 void CanRunScriptChecker::buildFuncSet(ASTContext *Context) {
276 // We create a match finder.
277 MatchFinder Finder;
278 // We create the callback which will be called when we find a function with
279 // a MOZ_CAN_RUN_SCRIPT annotation.
280 FuncSetCallback Callback(*this, CanRunScriptFuncs);
281 // We add the matcher to the finder, linking it to our callback.
282 Finder.addMatcher(
283 functionDecl(hasCanRunScriptAnnotation()).bind("canRunScriptFunction"),
284 &Callback);
285 Finder.addMatcher(lambdaExpr().bind("lambda"), &Callback);
286 // We start the analysis, given the ASTContext our main checker is in.
287 Finder.matchAST(*Context);
288 }
289
check(const MatchFinder::MatchResult & Result)290 void CanRunScriptChecker::check(const MatchFinder::MatchResult &Result) {
291
292 // If the set of functions which can run script is not yet built, then build
293 // it.
294 if (!IsFuncSetBuilt) {
295 buildFuncSet(Result.Context);
296 IsFuncSetBuilt = true;
297 }
298
299 const char *ErrorInvalidArg =
300 "arguments must all be strong refs or caller's parameters when calling a "
301 "function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object "
302 "argument). '%0' is neither.";
303
304 const char *ErrorNonCanRunScriptParent =
305 "functions marked as MOZ_CAN_RUN_SCRIPT can only be called from "
306 "functions also marked as MOZ_CAN_RUN_SCRIPT";
307 const char *NoteNonCanRunScriptParent = "caller function declared here";
308
309 const Expr *InvalidArg;
310 if (const CXXDefaultArgExpr *defaultArg =
311 Result.Nodes.getNodeAs<CXXDefaultArgExpr>("invalidArg")) {
312 InvalidArg = defaultArg->getExpr();
313 } else {
314 InvalidArg = Result.Nodes.getNodeAs<Expr>("invalidArg");
315 }
316
317 const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("callExpr");
318 // If we don't find the FunctionDecl linked to this call or if it's not marked
319 // as can-run-script, consider that we didn't find a match.
320 if (Call && (!Call->getDirectCallee() ||
321 !CanRunScriptFuncs.count(Call->getDirectCallee()))) {
322 Call = nullptr;
323 }
324
325 const CXXConstructExpr *Construct =
326 Result.Nodes.getNodeAs<CXXConstructExpr>("constructExpr");
327
328 // If we don't find the CXXConstructorDecl linked to this construct expression
329 // or if it's not marked as can-run-script, consider that we didn't find a
330 // match.
331 if (Construct && (!Construct->getConstructor() ||
332 !CanRunScriptFuncs.count(Construct->getConstructor()))) {
333 Construct = nullptr;
334 }
335
336 const FunctionDecl *ParentFunction =
337 Result.Nodes.getNodeAs<FunctionDecl>("nonCanRunScriptParentFunction");
338 // If the parent function can run script, consider that we didn't find a match
339 // because we only care about parent functions which can't run script.
340 //
341 // In addition, If the parent function is annotated as a
342 // CAN_RUN_SCRIPT_BOUNDARY, we don't want to complain about it calling a
343 // CAN_RUN_SCRIPT function. This is a mechanism to opt out of the infectious
344 // nature of CAN_RUN_SCRIPT which is necessary in some tricky code like
345 // Bindings.
346 if (ParentFunction &&
347 (CanRunScriptFuncs.count(ParentFunction) ||
348 hasCustomAttribute<moz_can_run_script_boundary>(ParentFunction))) {
349 ParentFunction = nullptr;
350 }
351
352 // Get the call range from either the CallExpr or the ConstructExpr.
353 SourceRange CallRange;
354 if (Call) {
355 CallRange = Call->getSourceRange();
356 } else if (Construct) {
357 CallRange = Construct->getSourceRange();
358 } else {
359 // If we have neither a Call nor a Construct, we have nothing do to here.
360 return;
361 }
362
363 // If we have an invalid argument in the call, we emit the diagnostic to
364 // signal it.
365 if (InvalidArg) {
366 const StringRef invalidArgText = Lexer::getSourceText(
367 CharSourceRange::getTokenRange(InvalidArg->getSourceRange()),
368 Result.Context->getSourceManager(), Result.Context->getLangOpts());
369 diag(InvalidArg->getExprLoc(), ErrorInvalidArg, DiagnosticIDs::Error)
370 << InvalidArg->getSourceRange() << invalidArgText;
371 }
372
373 // If the parent function is not marked as MOZ_CAN_RUN_SCRIPT, we emit an
374 // error and a not indicating it.
375 if (ParentFunction) {
376 assert(!hasCustomAttribute<moz_can_run_script>(ParentFunction) &&
377 "Matcher missed something");
378
379 diag(CallRange.getBegin(), ErrorNonCanRunScriptParent, DiagnosticIDs::Error)
380 << CallRange;
381
382 diag(ParentFunction->getCanonicalDecl()->getLocation(),
383 NoteNonCanRunScriptParent, DiagnosticIDs::Note);
384 }
385 }
386