[cfe-dev] [analyzer] Evaluating a call to operator bool()

Artem Dergachev via cfe-dev cfe-dev at lists.llvm.org
Thu Aug 1 14:40:11 PDT 2019


On 7/31/19 2:01 PM, via cfe-dev wrote:
> Hi list,
>
> I have the following code to analyze:
>
> struct BoolConvertibleStruct {
>     int n;
>     BoolConvertibleStruct(int m) : n(m) {}
>     operator bool() const { return n != 0; }
> };
>
> BoolConvertibleStruct StructFunc() {
>     return 1;
> }
>
> I have reduced my problem to wanting to analyze StructFunc() to figure 
> out the truth value of its return value. (The actual problem is more 
> complicated and you might recognize that BoolConvertibleStruct is a 
> stand-in for std::unique_ptr<T>, among other things :-P)

That's a very important detail. If it's a struct that you've implemented 
yourself, then all you need to do is extract the value from a field of 
the structure (it's not a problem when you know the name of the field).

However, if it's something in the C++ standard library and it's 
implemented differently depending on the particular implementation of 
the standard library that you may be using, then you can't access the 
field because you've no idea what meaning does every field carry, so 
you'll have to treat the structure as an opaque object and reason about 
its contents by modeling every method of the structure. I.e.:

1. Subscribe to the constructor of the structure and map (as in 
REGISTER_MAP_WITH_PROGRAMSTATE) the region of the structure to the value 
with which it was constructed.
2. Subscribe to the copy/move constructor of the structure and map the 
region into which it's copied/moved to the same value.
3. Subscribe to any method that mutates the value and update your maps.
4. Once you do all of this, you would be able to simply retrieve the 
value from your map when you need to model operator bool.

This approach is costly and annoying and easy to get wrong and i wish we 
had better tools for implementing it but for now it assumes a lot of 
boilerplate. If you want examples, see how the experimental 
IteratorChecker tries to model iterators (which is a harder problem).

Generally, i'll be pretty excited to accept patches that improve 
modeling of smart pointers in this manner. If that aligns with your 
interests, please extend our fairly minimal SmartPtrChecker and put your 
work to Phabricator (on an as early of a stage as possible) so that we 
could merge it!
> I have the following sample checker:
>
> class Analyzer : public Checker<check::EndFunction> {
>  public:
>     void checkEndFunction(const ReturnStmt* ret, CheckerContext& cx) 
> const {

checkEndFunction isn't the right place for this sort of stuff. It's 
already too late to see what integer was stuffed into the returned 
BoolConvertibleStruct, but it's too early to actually model the effect 
of the call (what if it wasn't inlined in the first place?).

Regardless of what approach you take, you most likely want to stick to 
checkPostCall.

>         const auto* func = 
> cast<FunctionDecl>(cx.getStackFrame()->getDecl());
>         if (func->getQualifiedNameAsString() != "StructFunc")
>             return;
>
>         ProgramStateRef state = cx.getState();
>         SValBuilder& builder = cx.getSValBuilder();
>         ASTContext& ast = cx.getASTContext();
>
>         SVal returnValue = cx.getSVal(ret->getRetValue());
>         SVal falseValue = builder.makeZeroVal(ast.BoolTy);
>         SVal returnedFalse = builder.evalEQ(state, returnValue, 
> falseValue);
>
>         errs() << "Evaluating (" << returnValue << " == " << falseValue
>             << ") -> " << returnedFalse << "\n";
>     }
> };
>
> However when I run it on my sample code I get this output:
>
> Evaluating 
> (lazyCompoundVal{0x7f98f1871c70,Element{SymRegion{conj_$0{struct 
> BoolConvertibleStruct *, LC1, S973, #1}},0 S64b,struct 
> BoolConvertibleStruct}} == 0 U1b) -> Unknown

lazyCompoundVal is a snapshot of the structure as a whole. You can 
extract values of particular fields from it with the following procedure:

- Take the lazyCompoundVal's parent region 
(`LazyCompoundVal::getRegion()`, in your case it's 
`Element{SymRegion{conj_$0{struct BoolConvertibleStruct *, LC1, S973, 
#1}},0 S64b,struct BoolConvertibleStruct}`).
- Construct a FieldRegion as a sub-region of the parent region with the 
FieldDecl of the field (i.e., State->getLValue(fieldDecl, parentRegion)).
- Ask StoreManager to do a getBinding() for that region from the 
lazyCompoundVal's Store (`LazyCompoundVal::getStore()`, in your case 
it's `0x7f98f1871c70`).

See also my old workbook at 
https://github.com/haoNoQ/clang-analyzer-guide/releases/download/v0.1/clang-analyzer-guide-v0.1.pdf


> It seems to me that the result should be able to be easily modeled, 
> but I'm not sure how to go about it. (I don't even see the "1" stored 
> in the field showing up in the LazyCompoundVal.)

I.e., in order to "see" "1" stored in the field, you need to dump the 
parent Store of the LazyCompoundVal. If you dump the Exploded Graph, you 
can easily search it by the Store pointer (it would, of course, change 
with every run).

> Looking at the source code, it looks like I will have to somehow 
> transform the LazyCompoundVal to something else because evalBinOp will 
> always return Unknown if either side is a LazyCompoundVal. I have 
> tried these things so far:
>
> - Getting the CXXConversionDecl for BoolConvertibleStruct::operator 
> bool() and creating a CXXMemberCallExpr, then calling cx.getSVal() on 
> that; the result is Unknown
> - Getting the FieldDecl for BoolConvertibleStruct::n and calling 
> state->getLValue(field, returnValue); the resulting SVal crashes when 
> I try to print it
> - Using builder.evalCast() to cast returnValue to ast.BoolTy; the 
> result of the cast is also Unknown
> - Using state->isNull() to query the truth value directly; the result 
> is underconstrained
>
> Some suggestion about what to try next (or even "this definitely won't 
> work") would be appreciated.
>
> Regards,
> -- 
> Philip
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20190801/e0edd134/attachment.html>


More information about the cfe-dev mailing list