[cfe-dev] Analyser - variadic arguments

Artem Dergachev via cfe-dev cfe-dev at lists.llvm.org
Thu Jul 28 12:53:13 PDT 2016


  If i was to write such checker, i'd try the following:

Map va_list-type variables (as memory regions) upon va_start(), map them 
to the number of read arguments (0 so far), and to the current 
StackFrameContext pointer, which represents a particular function call 
within which we are now (CheckerContext::getCurrentStackFrame()) (second 
program state map - or map to structure with two items).

You can retrieve [path-sensitive] function declaration and 
[path-insensitive] call expression from the stack frame context, and 
compute the number of variadic arguments passed based on that.

If va_start() is called upon a variable that is already tracked, warn.

If va_start() is called twice in the same StackFrameContext, warn.

Upon va_arg(), increment the number of reads for the respective 
variable. If the variable is not tracked, then the user has forgotten to 
call va_start(), hence warn. If the number exceeds the remembered total 
number of arguments, warn.

Upon va_copy(), copy the whole map item to the new variable. If the 
source variable is not va_start()'ed, warn.

Upon va_end(), erase the map item, which means that you no longer track 
the variable. If the variable is not va_start()'ed, warn.

Upon checkDeadSymbols, see if any of the tracked va_list variables is 
dead (in the sense of SymbolReaper::isLiveRegion()). If it is dead, then 
va_end() will never be called on it (and it wasn't so far, because the 
variable is still in the map). Hence warn on forgotten va_end().

You don't need to track end of function to warn on forgotten va_end() - 
checkDeadSymbols is already more frequent.

If you expect seeing a lot of passing va_list variables by pointers 
(which is insane but i guess possible), then consider the following.

Upon checkPointerEscape, see if any of the tracked va_list variables 
escapes. If so, mark it in the map as escaped: it might have va_end()'ed 
elsewhere, so we should not warn if it dies. Hence you cannot erase it 
from the program state - instead, you'd essentially need a separate 
trait flag (though you may put some magic constant into the existing trait).

If the memory region of the va_list variable has symbolic base (came to 
us by pointer, rather than variable declaration), then it might have 
already been initialized with va_start. Hence you shouldn't warn if 
va_arg()/va_end()/va_copy() is called upon it without prior va_start().

Hence the checker looks pretty similar to SimpleStreamChecker described 
in the video, and it's a very typical path-sensitive checker. I don't 
expect much problems on this path - it may sound like a lot of info, but 
basically it should be easy.

If you are only interested in argument count overflows, but not in other 
va_* API misuse, then probably you may simplify this "typestate 
machine". But you'd still need to catch va_start() and va_args() and 
most likely va_copy(), and once you do, you get all other checks for 
almost-free.



More information about the cfe-dev mailing list