[cfe-dev] [analyzer] Looking up previously stored custom state for ParmVarDecls that have gone out of scope

Timothy J. Wood via cfe-dev cfe-dev at lists.llvm.org
Tue May 1 14:09:43 PDT 2018


While working on my outError checker, I'm running into a bunch of false positives that seem to boil down to the same issue.

I *think* my core question is "how do I store state about symbols going out of scope such that I can reliably look it up later", but I'm not 100% sure. Longer version:

Attached below is a diff with a minified version of my checker that just shows this issue, which can be applied to clang on the `release_60` branch. The test case is:

I've annotated a test run of the input and output to make it easier to match up locations, when running like so, :

    % ./bin/clang -cc1 -internal-isystem ./lib/clang/6.0.1/include -nostdsysteminc -analyze -analyzer-checker=debug.ViewExplodedGraph,osx.cocoa.NSErrorWrite -fblocks -analyzer-store=region -Wno-objc-root-class ../llvm/tools/clang/test/Analysis/missing-error.m


Test input:

[B]	static BOOL _fillErrorWithReturn(NSError **_outError)
	{
		if (_outError) {
			*_outError = [NSError make];
		}
[C]		return NO;
	}

[A]	- (BOOL)fillErrorHelperFunctionWithReturn:(NSError **)outError;
	{
[D]		return _fillErrorWithReturn(outError);
	}


Log file:


[A]	checkBeginFunction [0x7ff6038a13f0]
	  Decl 0x7ff60386e130
	  at /Users/bungi/Source/LLVM/llvm/tools/clang/test/Analysis/missing-error.m:26:1
	  OutError ParmVarDecl = 0x7ff60386e1b8
	  StackSlotVal &outError
	  OutErrorVal &SymRegion{reg_$1<NSError ** outError>}
[B]	checkBeginFunction [0x7ff6038a1ea8]
	  Decl 0x7ff60386e020
	  at /Users/bungi/Source/LLVM/llvm/tools/clang/test/Analysis/missing-error.m:18:13
	  OutError ParmVarDecl = 0x7ff60386df20
	  StackSlotVal &_outError
	  OutErrorVal &SymRegion{reg_$1<NSError ** outError>}
[C]	checkPreStmt<ReturnStmt> [0x7ff604800b48]
	  Stmt 0x7ff603898078
	  at /Users/bungi/Source/LLVM/llvm/tools/clang/test/Analysis/missing-error.m:23:2
	  OutError ParmVarDecl = 0x7ff60386df20
	  StackSlotVal &_outError
	  OutErrorVal &SymRegion{reg_$2<NSError ** _outError>}
	  OutErrorVal is not constrained
[D]	checkPreStmt<ReturnStmt> [0x7ff604801188]
	  Stmt 0x7ff60386e438
	  at /Users/bungi/Source/LLVM/llvm/tools/clang/test/Analysis/missing-error.m:28:2
	  OutError ParmVarDecl = 0x7ff60386e1b8
	  StackSlotVal &outError
	  OutErrorVal &SymRegion{reg_$1<NSError ** outError>}
	  OutErrorVal is not constrained
[C]	checkPreStmt<ReturnStmt> [0x7ff604802c18]
	  Stmt 0x7ff603898078
	  at /Users/bungi/Source/LLVM/llvm/tools/clang/test/Analysis/missing-error.m:23:2
	  OutError ParmVarDecl = 0x7ff60386df20
	  StackSlotVal &_outError
	  OutErrorVal &SymRegion{reg_$2<NSError ** _outError>}
	  OutErrorVal is not constrained
[D]	checkPreStmt<ReturnStmt> [0x7ff604802fd0]
	  Stmt 0x7ff60386e438
	  at /Users/bungi/Source/LLVM/llvm/tools/clang/test/Analysis/missing-error.m:28:2
	  OutError ParmVarDecl = 0x7ff60386e1b8
	  StackSlotVal &outError
	  OutErrorVal &SymRegion{reg_$1<NSError ** outError>}
	  OutErrorVal is not constrained


At [B], the beginning of the static function that gets inlined, looking up the _outError parameter has a stack slot value of "StackSlotVal &_outError", and when I get the contents of that location, I get "&SymRegion{reg_$1<NSError ** outError>}". Note the change in name -- this is the variable from the caller, inlined in to the static function. That seems OK.

Later, in the two [C] branches, when about to return from the inlined static function, trying to get the SVal for _outError, I get a new “$2” name, "&SymRegion{reg_$2<NSError ** _outError>}”, and even though there should be two branches where _outError should be constrained alternately to null and not-null, the OutErrorVal returned is marked as non-constrained on both paths.

I'm guessing that both the change in the SVal name, and the lack of constraint are due to _outError going out of scope. Making things more odd, though, is that in the exploded graph dot file (attached below too) if you search for the state "0x7ff604800b48" (from the first [C] / checkPreStmt<ReturnStmt> above), you can see that it still has a constraint "reg_$1<NSError ** outError> : { [0, 0] }". In my real checker, I implement the checkDeadSymbols callback and make notes about whether an outError ParmVarDecl value was definitely null when it went out of scope, or whether it was definitely non-null, but had a valid value written to *outError at the time.

The problem lies how to store this information in my custom state such that checkPreStmt<ReturnStmt> can reliably look it up again. If I use a MemRegion as a key in my REGISTER_MAP_WITH_PROGRAMSTATE, then the lookup at "[C]  checkPreStmt<ReturnStmt>" seems to fail due to the name change. What should I be using as the lookup key in order to deal with sort lookup of out-of-scope symbols coming back with a different name?

I’ve only noticed this sort of renaming happening with inlining, but maybe that’s is just coincidental.

Hopefully this is the right question, but if you see other places I've gone off the rails, let me know. Otherwise, I'll keep reading and see if I can figure out a solution.

Thanks!

-tim

-------------- next part --------------
A non-text attachment was scrubbed...
Name: MinOutError.diff
Type: application/octet-stream
Size: 9505 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180501/86b12303/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ExprEngine-1e6b04.dot
Type: application/msword
Size: 51432 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180501/86b12303/attachment.dot>
-------------- next part --------------





More information about the cfe-dev mailing list