We locate the origin (i.e., declaration) of all controlled
objects and qualify them as
unchecked. There are
three different kinds of variables that a function can
access: global variables, local variables, and parameters.
Currently we do not consider global variables, which
account for less than 2% of controlled objects.
All locally declared variables of a controlled type are qualified as
unchecked. A special case of this is when reference to a
structure member of a controlled data type is passed as a parameter to
a function (e.g.
f(dentry->d_inode), where field
is of controlled type). It should also be qualified as
unchecked, because it is equivalent to declaring a local variable,
initializing it to be a reference to the structure member, and then
passing the variable to the function. To qualify such cases, we
explicitly cast the parameter to
unchecked at the function
f((struct inode * $unchecked)dentry-> d_inode)).
The task of marking local variables of controlled types is automated
using two tools: one for controlled local variables and one for the
passing of structure member references to functions. First, we
modified GCC to output the location (file and line number) of any
local variable declaration with a controlled type. To achieve this, we
inserted code that traverses the abstract syntax tree (AST) for each
function as it is compiled. The code scans the AST for local
VAR_DECL nodes) and prints the location details
if the type (
TREE_TYPE) of the declaration is a controlled type
(independent of the level of indirection). In the case of structure
member references, our GCC code scans the AST for function calls
CALL_EXPR nodes). If any parameter is a reference to
structure member (
COMPONENT_REF node, see
Section 3.2.2 for more discussion), and the type of
the referenced field is one of the controlled types, then GCC prints
out detailed location and type information about the parameter. Next,
this information is input to a PERL script that inserts appropriate
annotations into the source code.
For parameters in function declarations, we leave their types
unqualified. CQUAL then automatically infers their type during the
analysis process. There are a few exceptions to this rule, where we
manually annotate function prototypes (in two header files) that we
checked type parameters.