Integer Set Library using retain-count based allocation which is not
modeled in MallocChecker.
Details
Diff Detail
Event Timeline
Oh damn, i just realized that this way we track much more pointers than before, because we cannot restrict ourselves to pointers that have been explicitly malloc()ed during analysis. After all, we don't need to see the allocation site to diagnose use-after-free.
I'm afraid that it's going to be too many pointers.
Change of plans: let's suppress the warning when our free() is done within the function that has __isl_take in its definition. So, like, ascend the chain of location contexts and check your callers when you're about to mark the pointer as released. If any of the callers contain __isl_take, mark it as escaped instead.
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
56–58 | We already have Escaped, it's the same thing in practice. | |
2937–2942 |
I think if the __isl_* macro is in use it should be used in the immediate StackFrame. Btw: some magic happened and with the previous approach we did not suppress some reports, now we do. Thanks!
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
56–58 | It is more strict than Escaped, also it made for the purpose of PSK_EscapeOther to force out we lost the entire pointer and do not make false warnings of use-after-free. | |
2937–2942 | I wanted to make it bulletproof, but your meme-proof is way more better. |
Here is an example of the mentioned use-after-free by pointer-escaping as an argument:
https://llvm.org/reports/scan-build/report-DeclBase.cpp-getFromVoidPointer-0-1.html#EndPath
Not sure how is this false positive related to that report, but this false positive looks super weird and i'd love to debug it more.
P.S. I think you should attach the report to Phabricator directly, as the link will expire as soon as these reports get regenerated.
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
56–58 | How exactly is it more strict? I.e., what warnings are getting suppressed by you that aren't going to be suppressed if you use Escaped instead? |
Luckily the stable scan-build namings are stable, so that is why I picked that handy option.
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
56–58 | After some measurements the previously attached report has nothing to do with strictness, just we really miss some escaping. Reverted that. |
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
2560–2561 | const auto *FD = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()). | |
2562–2565 | I'm slightly worried that it'll crash when free() is being called from within a body farm. For now it probably cannot happen because none of the bodyfarmed functions can call free() directly, but i'd anyway rather add a check that the source locations we're taking are valid. | |
2567–2572 | If the string is empty, it clearly cannot contain __isl_, so the first check is redundant. | |
2577–2579 | C.getSVal(Arg). | |
2582 | remove is unnecessary, we overwrite it anyway. |
- Fix.
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
2562–2565 | Oh, I missed that, thanks! I wanted to check for everything, yes. | |
2567–2572 | The first check is: "We got a body and its decl?", the second check is: "We got an ISL macro?". Yea, it is kind of redundant, just I like to pack one check in one IfStmt, and now that two question merges. Also I like to make them one-liners so they are self-explanatory. Here is an example why I like it: // Escape pointers passed into the list, unless it's an ObjC boxed // expression which is not a boxable C structure. if (!(isa<ObjCBoxedExpr>(Ex) && !cast<ObjCBoxedExpr>(Ex)->getSubExpr() ->getType()->isRecordType()))
| |
2582 | I believe in so as well, just the official code base has this semantic. I have rewritten that, see below in checkPointerEscapeAux(). |
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
2562–2565 | I think this is not fixed yet. I'm thinking of something like if (!Body->getBeginLoc().isValid()) { ... }. | |
2582 | Yeah, and the official codebase is wrong :) | |
2583 | The check is unnecessary. addTransition automatically takes care of the "no changes have happened" case and throws away the transition. Moreover, in the current patch you aren't really checking if any changes have happened: the symbol may already be escaped, so in this case the flag is set to true but the state remains the same. |
- More fix.
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
2562–2565 | Ugh, silly mistake, thanks! |
Great, thanks!
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | ||
---|---|---|
372–373 | One last thing: let's make it obvious what does the function do.
I suggest something like this: /// See if deallocation happens in a suspicious context. If so, escape the pointers /// that otherwise would have been deallocated and return true. bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE, CheckerContext &C) const; |
We already have Escaped, it's the same thing in practice.