[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