C/C++ knowledge base reference
In this topic: |
The C/C++ knowledge base file is a text file with the extension .kb. During every analysis of your code, Klocwork automatically generates a knowledge base, which contains a record for each function that operates in the code. To change the way Klocwork understands your code, you can create your own knowledge base records, place them in a .kb file, and import them into the Klocwork analysis of your code. To see examples of how to create knowledge base records to tune Klocwork analysis, see Tuning C/C++ analysis.
Knowledge base syntax
As well as knowledge base records, a .kb file can contain blank lines and comments (preceded by #).
A knowledge base record contains the following fields separated by spaces:
<function_name> <function_key> <record_kind> <specification>
where
- <function_name> is the fully qualified name of the function or class method to which this knowledge base record applies. You can use wildcards (*) in class or namespace names, but not in method names.
You can also use wildcards to represent types in templates and template functions, up to one level. For example: std::vector<*>::insert (templates) or mytemplatefunction<*> (template functions)
- <function_key> is the function key; it is based on function arguments (signature) and is used to distinguish C++ overloaded functions. The following patterns may also be used:
- "-" (hyphen) for all C functions and any C++ functions that are not overloaded
- nothrow for nothrow operators
- placement for new and delete operators. Note that to avoid confusing the Klocwork analyzer, when manually editing the knowledge base, you must use a special function key for functions named "new" or "delete": a double hyphen (--). Otherwise, the analyzer will interpret these calls as calls to the built-in C++ operators new and delete.
You can also use wildcards for the <function_key> field. You can use a single wildcard (*) to match any signature.
You can specify the number of arguments in a function key by using the syntax "@args(N)". For example:foo @args(2) RET 1 : $$ EQ(0)
This KB record will match any function "foo" in the global namespace that has two arguments of any type.Examples
# <function_name> <function_key> <record_kind> <specification> 1. read_data - DMEM ,MRF,1 2. MyClass::fail - NORET 3. MyClass::new nothrow RET env:EQ(0) 4. MyClass::new placement DMEM ,MRF,2 5. *::read * DMEM ,MRF,1 The easiest way to write a knowledge base record with an explicit function key is to take an automatically generated knowledge base record for the appropriate function or method and modify its <record_kind> and <specification> fields accordingly.
- <record_kind> is the name of the knowledge base record, such as ALLOC or XMRF
- <specification> defines the characteristics you want to apply to the knowledge base record
The <specification> field differs for every knowledge base record, and is defined as a socket expression or a conditional socket expression.
A <socket_expression> is typically defined in the form:
$<arg_number> : <value> | $$ | [1->] .<field_name>
where
- <arg_number> is the number of the argument, or parameter, in the function. Zero (0) designates the 'this' argument for class methods.
- <value> is the value you're assigning to the argument, which can be a single value or a range in square brackets, with some records using range operators EQ, NE, GE, and LE, and others using symbols =, !=, ==, <, >, <=, and >=. For example, $1 EQ(0) $2 [4,16] specifies that argument 1 must be equal to zero, and argument 2 must be between 4 and 16. A socket expression can also include the usual logical (&&, |) and arithmetic (+, -, *) operators.
- $$ specifies the return value of the function.
- <field_name> is the simple or qualified name of a field in a class or structure, and -> indicates a pointer to the field name. For example, $1->x designates field x of the structure pointed to by the first argument ($1) of the function.
Socket expressions can also use properties:
- charlength - string length in characters
- arraysize - allocation size of buffer in array elements
- bytesize - allocation size of buffer in bytes
and functions:
- if (a,b,c) - logical expression a is evaluated, and the result of the function is b if a is true, and c if a is false
- min(a,b) - minimum of two values
- max(a,b) - maximum of two values
- formatted_printf(a, b) - returns the length of the string that would be created by a printf-like function if the format string is an argument a, and printed arguments start from argument b
A conditional socket is expressed in the form:
<precondition> : <socket_expression> : <postcondition>
where
- <precondition> is a condition that must be met for the operation designated by the socket expression, or 1 to indicate that there is no precondition and the operation always happens
- <postcondition> is a condition that is met if the operation succeeds
Using a KB for a virtual method
For virtual methods, you can apply the KB record to a base class and all its children classes, but only through the interface of the base class specified. For example, if you want to apply the KB record:
Base::func * RET 1 : $$ EQ(5)
to the following code:
class Base { public: virtual int func(); }; class Child : public Base { public: virtual int func(); }; void foo() { Child *c = new Child(); Base *b = c; int x = b->func(); // Since this is a pointer of class Base, then the KB will be applied here even if the object is of type Child. Klocwork will know that x == 5 after the call. int y = c->func(); // Since this is a pointer of class Child, then the KB will NOT be applied here even if the class derives from Base. So, Klocwork will not know anything about the value of y. }
Note that the KB is applied only to the Base interface, not the Child interface. You can add a new KB specifically for the Child class (the virtual method) to specify the same behavior (or a different one). This allows you to specify a KB for Child classes; However, it is only applied to the Child class objects.
Specification fields for record kinds
ACQUIRE and RELEASE records
ACQUIRE and RELEASE records specify rules for resource handling. An ACQUIRE record specifies that a function can acquire (allocate) a resource object of the kind identified by its descriptor. A RELEASE record specifies that a function can release (free) the identified resource object.
Specification field syntax
<resource_kind> ':' <conditional_socket> | 'ignore'
where
- <resource_kind> denotes the kind of resource handling to match, such as FILE or pthread_mutex
- <conditional_socket> identifies the value that is used as a resource descriptor for the acquired resource
- ignore can be used to skip the acquire or release function in the code
ACQUIRE and RELEASE function pairs
The RH.LEAK checker searches for the following pairs of functions as ACQUIRE/RELEASE pairs:
pthread_attr_init / pthread_attr_destroy pthread_mutexattr_init / pthread_mutexattr_destroy pthread_condattr_init / pthread_condattr_destroy pthread_barrierattr_init / pthread_barrierattr_destroy pthread_rwlockattr_init / pthread_rwlockattr_destroy posix_trace_attr_init / posix_trace_attr_destroy
These functions relate mainly to the pthread library (pthread.h) and manipulate "attribute" objects of different kinds which are used to initialize corresponding objects such as "thread", "mutex", and "barrier".
Example 1
In the following code snippet, the RH.LEAK checker would normally report two issues at line 37:
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <sys/mman.h> 4 #include <fcntl.h> 5 #include <pthread.h> 6 7 struct semaphore { 8 pthread_mutex_t lock; 10 unsigned count; 11 }; 12 typedef struct semaphore semaphore_t; 13 14 semaphore_t * 15 semaphore_create(char *semaphore_name) 16 { 17 int fd; 18 semaphore_t *semap; 19 pthread_mutexattr_t psharedm; 20 pthread_condattr_t psharedc; 21 22 fd = open(semaphore_name, O_RDWR | O_CREAT | O_EXCL, 0666); 23 if (fd < 0) 24 return NULL; 25 ftruncate(fd, sizeof(semaphore_t)); 26 pthread_mutexattr_init(&psharedm); 27 pthread_mutexattr_setpshared(&psharedm, PTHREAD_PROCESS_SHARED); 28 pthread_condattr_init(&psharedc); 29 pthread_condattr_setpshared(&psharedc, PTHREAD_PROCESS_SHARED); 30 semap = (semaphore_t *) mmap(NULL, sizeof(semaphore_t), 31 PROT_READ | PROT_WRITE, MAP_SHARED, 32 fd, 0); 33 close(fd); 34 pthread_mutex_init(&semap->lock, &psharedm); 35 pthread_cond_init(&semap->nonzero, &psharedc); 36 semap->count = 0; 37 return semap; 38 }
37:Resource acquired to 'psharedm' at line 26 may be lost here. 37:Resource acquired to 'psharedc' at line 28 may be lost here.
To suppress these issues in the Klocwork analysis, you can add the following records to the knowledge base:
pthread_mutexattr_init - ACQUIRE ignore pthread_mutexattr_destroy - RELEASE ignore pthread_condattr_init - ACQUIRE ignore pthread_condattr_destroy - RELEASE ignore
Example 2
File stream manipulations by fopen and fclose functions from the standard C library (stdio.h) can be described by the following records:
fopen - ACQUIRE FILE : 1 : $$ : $$ NE (0) fclose - RELEASE FILE : 1 : $1 : 1
In this example, the first record says that the value returned by a call to 'fopen' designates a resource descriptor, 'FILE'. The function performs its action, file opening, without any preconditions (as indicated by 1), but only if the returned value is not null. The second record says that the 'fclose' function always closes the file by passing its descriptor as the first argument.
Example 3
The following records can be used to describe pthread mutex manipulations by 'pthread_mutex_init' and 'pthread_mutex_destroy' functions from the pthread.h library:
pthread_mutex_init - ACQUIRE pthread_mutex : 1 : *$1 : $$ EQ(0) pthread_mutex_destroy - RELEASE pthread_mutex : 1 : *$1 : 1
These records specify that the 'pthread_mutex_init' function inits a mutex object pointed to by the first argument when the return value is equal to 0, and the 'pthread_mutex_destroy' function always destroys the mutex by the pointer to its descriptor.
ALLOC records
ALLOC records specify that a function allocates memory only under some conditions on its parameters (preconditions), or sets the returned result to specific values if memory allocation was successful (postconditions). To see examples of how to create ALLOC records to tune Klocwork analysis, see Tuning C/C++ analysis.
Specification field syntax
<group> ':' <conditional_socket> | ignore
where
- <group> specifies a memory-function group
- ignore can be used to skip the allocation function in the code
Example 1
The following knowledge base record:
search_data - ALLOC stdc : $1 GE(0), $2 GE(0) : *$4 : $$ GE(0)
says that function 'search_data' allocates memory belonging to the 'stdc' allocation group when its first and second parameters are greater than or equal to zero. The allocated memory is passed through the dereferenced fourth parameter, and if the allocation was successful, the function return value is greater than or equal to zero.
Example 2
In the following snippet, Klocwork would normally report a false positive memory leak for the variable 'p' in the function 'use_precond':
1 void alloc_precond(int a, void** p) 2 { 3 if (a>0) 4 *p = malloc(10); 5 } 6 7 void use_precond(int input_data) 8 { 9 void* p; 10 if (input_data==0) { 11 alloc_precond(input_data, &p); // process it
To avoid this false positive, the ALLOC record automatically generated for the function 'alloc_precond' is:
alloc_precond - ALLOC stdc : $1 GE(1) : *$2 : 1
This record says that the new memory is allocated if the parameter 'a' is greater than or equal to 1. When the function 'alloc_precond' is called in the 'use_precond' example, the value of the argument 'input_data' is zero, so no memory is allocated.
Example 3
In the following snippet, Klocwork would normally report a memory leak for the variable 'ptr' in the function 'use_postcond':
1 int alloc_postcond(int** p) 2 { 3 int* q = malloc(sizeof(int)); 4 5 if (q) { 6 *p = q; 7 return 0; // success 8 } 9 else { 10 return -1; // fail 11 } 12} 13 14 int* use_postcond() 15 { 16 int* ptr; 17 int res = alloc_postcond(&ptr); 18 19 if (res == 0) 20 return ptr; // return for further processing 21 22 return 0; 22 }
The ALLOC record automatically generated for the function 'alloc_postcond' is as follows:
alloc_postcond - ALLOC stdc : 1 : *$1 : *$1 NE(0), $$ EQ(0)
The record says that if the new memory was allocated in 'alloc_postcond', the return value of this function is zero, and that *p is non-zero). When this condition is satisfied ('res == 0'), the newly allocated memory is returned for further processing, and no memory leak occurs in 'use_postcond'.
BAA and IAA records
Bounds of array access (BAA) records describe how a function accesses arrays through pointers passed to it: read, write, read/write, access interval, and access unit size. Internal Array Access (IAA) records describe the name and size of local arrays, as well as how functions access these arrays.
Specification field syntax
BAA:
<ReadWrite> ':' <precondition> ':' <socket-expression> ':' <Interval>[','<UnitSize>] ':' <postcondition>
IAA:
<ReadWrite> ':' <precondition> ':' <ArrayName> ':' <ArraySize> ':' <Interval>[','<UnitSize>]
where
- <ReadWrite> is R, W, or RW
- <Interval> is '['<boundary specification>','<boundary_specification>']'
- <boundary_specification> is a socket expression that specifies the array boundary
- <UnitSize> is a positive number
- <ArrayName> is the identifier or the array
- <ArraySize> is a positive number
Example 1
strdup - BAA R:1:$1:[0,charlength($1)]:1
This record says that strdup reads its first argument in the range from 0 to the string length of its first argument. Essentially, this record specifies that strdup expects a zero-terminated string as its first argument.
Example 2
send_data - BAA R:1:$1:[0,if($2==0,charlength($1),$3+1)]:1
This record says that the function send_data reads a number of bytes from the buffer pointed to by the first argument. The number of bytes is taken from the second argument, unless the second argument is zero, in which case the first argument is treated as a string, and the size is automatically evaluated as the string length of the third argument plus one.
Example 3
store_and_eval - IAA W:1:temp_buffer:4:[0,$1]
This record says that the function store_and_eval accesses an internal buffer named temp_buffer, and the range of this access is from 0 to the value of the first argument.
Example 4
sprintf - BAA W:1:$1:[0,formatted_printf(2,3)]
This record says that sprintf writes as many bytes to the buffer pointed to by its first argument as needed to print its arguments according to formatted output rules.
Byte-order records
The byte-order records specify host-to-network or network-to-host byte-order conversion functions that receive their input data or return their output data through a variable. The byte-order records describing each type of function are:
- BO.HTON.I-host-to-network conversion in (I)
- BO.HTON.O-host-to-network conversion out (O)
- BO.NTOH.I-network-to-host conversion in (I)
- BO.NTOH.O-network-to-host conversion out (O)
- BO.READ-file read
- BO.RECV-network receive
- BO.SEND-network send
- BO.WRITE-file write
Specification field syntax
<conditional_socket>
where <conditional socket> describes the variable used to carry the input or output data
Example 1
hton - BO.HTON.I 1 : $1 : 1
This record says that conversion function hton gets its first argument as input data to convert and expects it to be in host byte order.
Example 2
hton - BO.HTON.O 1 : $$ : 1
This record says that conversion function hton returns a converted value in network byte order.
Example 3
read - BO.READ 1 : *$2 : 1
This record says that function read returns a value read from a file through a variable pointed to by the second argument.
BPS records
Buffer property settings (BPS) records describe how a function changes buffer properties.
Specification field syntax
<property name>'='<boundary specification>
Example 1
strdup - BPS charlength($$)=charlength($1) strdup - BPS bytesize($$)=charlength($1)+1
These records for string-duplication functions say that
- the size of the newly allocated chunk is equal to the length of the string passed as the first argument
- the space occupied by a newly allocated buffer is the length of its first argument plus one (for a zero-byte terminator)
Example 2
strcpy - BPS charlength($1)=charlength($2)
This record says that the buffer passed as the first argument to strcpy has a string of the same length as the string passed as the second argument.
CONC and LOCK records
The CONC and LOCK records relate to functions that lock and unlock variables, threads, mutexes, and handlers, under certain conditions. The CONC and LOCK records are:
- CONC.CONDSIGNAL-specifies that a function unlocks threads that have been locked on a condition variable
- CONC.CONDWAIT-specifies that a function locks the calling thread on a condition variable, and releases the locked mutex
- CONC.LOCK-specifies that a function locks a handler
- CONC.LOCK.TRY-specifies that a function tries to lock a handler
- CONC.UNLOCK-specifies that a function unlocks a handler
-
CREATETHREAD-specifies functions that create threads, for example, pthread_create in Linux, or CreateThread in Windows
- LOCK-specifies that a function locks a variable
-
LOCK_START-specifies that a function locks a variable that corresponds to a single lock on the current thread
-
LOCK_UNLOCK-specifies that a function locks and unlocks a variable
- UNLOCK-specifies that a function unlocks a variable
-
UNLOCK_START-specifies that a function unlocks a variable that corresponds to a single unlock for a function that creates a thread
Specification field syntax
CONC.CONDSIGNAL, CONC.LOCK, CONC.LOCK.TRY, CONC.UNLOCK, LOCK, UNLOCK
<conditional_socket>
where <conditional_socket> specifies the conditions for the lock or unlock
CONC.CONDWAIT
<precondition> ':' <socket_expression> ':' <socket_expression> : <postcondition>
where
- <precondition> and <postcondition> specify the conditions for the operation
- the <socket_expression> fields define the condition variable and the mutex to be unlocked
Example 1
pthread_cond_signal - CONC.CONDSIGNAL 1 : *$1 : 1
This record says that the function pthread_cond_signal always unlocks at least one of the threads that are blocked on the specified condition variable pointed to by its first argument, and returns 0 if the operation is successful.
Example 2
pthread_cond_wait - CONC.CONDWAIT 1 : *$1 : *$2 : 1
This record says that the function always releases the mutex pointed to by its second argument, and causes the calling thread to lock on the condition variable pointed to by its first argument.
Example 3
pthread_mutex_lock - CONC.LOCK 1 : *$1 : $$ EQ(0)
This record says that the function pthread_mutex_lock always tries to lock the object pointed to by its first argument, and returns 0 if the operation is successful. If the mutex is already locked, the calling thread blocks until the mutex becomes available.
DBZ records
- DBZ.SRC - specifies a function call used as a source for division by zero. This means that this function can return a zero constant value either directly or indirectly through output arguments;
- xDBZ - specifies a function that uses an argument as a divisor for a division operation without checking it for the zero constant value.
Specification field syntax
DBZ.SRC
<conditional_socket>
where
- <conditional_socket> identifies the variable that can be assigned a zero constant value and the conditions for the assignment.
xDBZ
<conditional_socket>
where
- <conditional_socket> specifies the conditions for doing a division by zero.
Example 1
foo - DBZ.SRC $1 EQ(0) : $$ : 1 bar - DBZ.SRC $1 LE(-1) : *$2 : 1
The first record of this example shows that the function 'foo' returns a zero constant value as its return value if its first argument is equal to 0.
The second record shows that the function 'bar' writes a zero constant value into the memory referenced by the second argument if its first argument is not greater than -1.
In this code snippet:
int dbz_01(int total) { int x = 0; int count = foo(x); return total / count; }
Klocwork would detect a division by zero when using the variable 'count' as a divisor, because 'x' is equal to 0 when it is passed as the first parameter in the call to the function 'foo'. Function 'foo' returned a zero constant value, which is assigned to variable 'count'.
In this code snippet:
int dbz_02(int x) { int may_be_zero; bar(x, &may_be_zero); if (x > 0) { return x / may_be_zero; } return (-x) / may_be_zero; }
Klocwork would detect a division by zero for the last use of the variable 'may_be_zero', because it would be reached if variable 'x' is less than 0, and if 'x' is less than zero, a call to function 'bar' would write a zero constant value into variable 'may_be_zero'.
Example 2
blah - xDBZ $3 GE(1): $1 : 1
This record says that function 'blah' uses the first argument as a divisor of a division or modulo operation without checking it for the zero constant value if the third argument is greater than or equal to 1.
FREE and SAFE_FREE records
The FREE and SAFE_FREE records relate to functions that free memory. Klocwork normally issues a report if functions from different groups are used to allocate and then free memory-for example, mixing C and C++ memory management functions, or mixing scalar and vector memory management functions. The FREE record is used to define specific allocation and freeing behavior.
Specification field syntax
<alloc_group> <expression> [post: <postcondition>]
where
- <alloc_group> identifies a memory-function group
- <expression> specifies which argument is freed
Example 1
realloc - FREE stdc $1 post: $$ NE(0)
This record says that realloc is an stdc function that frees memory passed with the first argument, and returns a non-null result when memory is freed.
Example 2
hsplit - SAFE_FREE stdc $1->tbl_array
This record says that the first argument of the function hsplit points to a structure. Memory referenced by the field tbl_array of this structure is released, and a new value is assigned to this field.
Hash Salt records
The following hash salt records relate to functions that compute hash or hash-based derived key values so that the salt can be provided as an argument. They can be used to extend the RCA.HASH.SALT.EMPTY checker.
- RCA.HASH.SALT records specify the salt argument of these functions.
- RCA.HASH.SALT.SIZE records specify the salt size or the salt length argument of these functions.
Specification field syntax
RCA.HASH.SALT
<conditional_socket>
where
- <conditional_socket> identifies the argument intended for providing the salt to the function.
RCA.HASH.SALT.SIZE
<conditional_socket>
where
- <conditional_socket> identifies the argument intended for providing the salt size or length to the function.
Example
generateHash - RCA.HASH.SALT 1 : $2 : 1 generateHashWithSaltSize - RCA.HASH.SALT 1 : $2 : 1 generateHashWithSaltSize - RCA.HASH.SALT.SIZE 1 : $3 : 1
These records specify that the generateHash and generateHashWithSaltSize functions use the second argument as a salt for hash computation, and that generateHashWithSaltSize uses the third argument to determine the size of the array or string that is passed as a salt.
NNTS.SRC records
NNTS.SRC records relate to the detection of a non null-terminated string problem, and designate functions that can return non null-terminated strings.
Specification field syntax
<conditional_socket> ':' <subtype> ':' <size> ':' <src_expression>
where
- <conditional_socket> identifies a variable that can be non null-terminated and its conditions
- <subtype> is ncpy for a function that copies memory buffers from a source location (strncpy, memcpy), or size for read-type functions (read, fread)
- for the size subtype, <size> is a <range_condition> that specifies the new size of the possible non null-terminated buffer
- for the ncpy subtype, <src_expression> is a socket expression that identifies variables used as source buffers
Example
strncpy - NNTS.SRC 1 : $1 : 1 : ncpy : [$3] : $2 read - NNTS.SRC 1 : $2 : 1 : size : [$3]
The first record of this example shows that the function 'strncpy' can return a non null-terminated string as its first parameter, if its third parameter is less than or equal in length to its second parameter. The second record shows that the function 'read' can return a non null-terminated string as its second parameter. The size of the buffer is passed in its third argument.
NPD records
NPD records relate to functions that can cause null pointer dereferencing, either returning a null value or writing a null value to a variable pointed to by their arguments. The NPD records are:
- NPD-specifies a function that dereferences an argument without checking them for null (and if the null is passed to the function, a runtime error occurs)
- NPD.SRC-specifies a function call used as a source
- xNPD-specifies a function call used as a sink
Specification field syntax
NPD
<arg_number>
where <arg_number> is the parameter number to dereference
NPD.SRC
<conditional_socket>
where <conditional_socket> identifies the variable that can be assigned to the null value and the conditions for the assignment
xNPD
<range_condition> ':' <arg_number>
where <range_condition> specifies the conditions for dereferencing
Example 1
myElemCopy - NPD 1 myElemCopy - NPD 2
The records in this example show that the function myElemCopy dereferences the first two arguments without checking them for null.
In this code:
tElem *bar(tElem *e1, tElem *e2) { if (!e1 && !e2) return NULL; if (!e1) e1 = createElem(); myElemCopy(e1,e2); return e1; }
Klocwork would detect a possible null pointer dereference for a call to myElemCopy, because if e2 is null and e1 isn't null, the second argument passed to myElemCopy would be null, causing the application to fail.
Example 2
foo - NPD.SRC $1 EQ(0) : $$ : 1 xff - NPD.SRC $2 LE(-1) : *$1 : 1
The first record of this example shows that the function 'foo' returns a null pointer value as its return value if its first argument is equal to 0. The second record shows that the function 'xff' writes a null pointer value into the memory referenced by the first argument if its second argument is not greater than -1.
In this code snippet:
int npd_01(int t) { if (!t) { char *s = foo(t); return *s != '\0'; } return 0; }
Klocwork would detect a null pointer dereference for the dereferencing of variable 's', because 't' is equal to 0 when it's passed as the first parameter in the call to the function foo. Function foo returned null, which is assigned to variable 's'.
In this code snippet:
int npd_02(int w) { int *p; xff(&p, w); if (w > 0) { return *p; } return -*p; }
Klocwork would detect a null pointer dereference for the second dereference of variable 'p', because it would be reached if variable 'w' is less than 0, and if 'w' is less than zero, a call to function xff would write a null value into variable 'p'.
Example 3
bcopy - xNPD $3 GE(1): 1
This record says that function bcopy dereferences the first argument without checking it for null if the third argument is greater than or equal to 1.
PWD_INPUT records
PWD_INPUT records relate to functions that identify password field and take the password as input from user.
-
PWD_INPUT.SRC records specify the functions that identify password field.
-
PWD_INPUT.SINK records specify the functions that accepts password as input from user.
Specification field syntax
<socket-expression>
where <socket_expression> defines the source functions PWD_INPUT.SRC or the sink functions PWD_INPUT.SINK
Example 1:
QLineEdit::setEchoMode QLineEdit*,QLineEdit::EchoMode, PWD_INPUT.SRC $1[1,3] : *$0 :1 QLineEdit::text const\ QLineEdit*, PWD_INPUT.SINK 1: *$0 :1
These records specify the QT framework functions. Argument 1 of setEchoMode is an enum which should have a value between 1 to 3 to be a password field. Hence, it is added as a precondition in PWD_INPUT.SRC KB.
Example 2:
gtk_entry_set_visibility - PWD_INPUT.SRC $2 EQ(0): *$1 :1 gtk_entry_set_input_purpose - PWD_INPUT.SRC $2[8,9] : *$1 :1 gtk_entry_get_text - PWD_INPUT.SINK 1: *$1 :1
These records specify the gtk framework functions. Argument 1 of gtk_entry_set_visibility should have a value of 0 to be a password field. Hence, it is added as a precondition in PWD_INPUT.SRC KB. Similarly, Argument 1 of gtk_entry_set_input_purpose is an enum which should have a value of either 8 or 9 to be a password field. Hence, it is added as a precondition in PWD_INPUT.SRC KB.
Example 3:
wxTextCtrl::\#constructor * PWD_INPUT.SRC $6 EQ(2048) : *$0 : 1 wxTextCtrl::GetValue * PWD_INPUT.SINK 1: *$0 :1
These records specify the wxwidgets framework functions. Argument 6 of constructor should have a decimal value of 2048 to be a password field. Hence, it is added as a precondition in PWD_INPUT.SRC KB.
R and W records
R records specify that a function reads the memory of an argument, part of an argument, or the memory pointed to by an argument or part of an argument. W records specify that a function writes the memory.
Specification field syntax
<simple_condition> ':' <socket_expression> | 'dummy'
where
- <simple_condition> says that the function either always reads or writes it ('1') or might read or write it ('env')
- <socket_expression> designates the value which is read or written
- 'dummy' means that the called function does not read any passed values or memory reachable by them
Example 1
As shown in this code snippet, function point_getXY reads the x and y fields of structure pointed to by the first argument:
struct Point { int x, y; }; int point_getXY(struct Point * p) { return p->x * p->y; }
Here are some corresponding knowledge base records:
point_getXY - R 1:$1 point_getXY - R 1:$1->Point::x point_getXY - R 1:$1->Point::y
Example 2
In this code snippet, function check_buf is a dummy function:
int check_buf(char * buf) { return 1; }
Here is a corresponding knowledge base record:
check_buf - R dummy
RET records
All the RET records specify characteristics related to return values for functions. The RET records include:
- RET- specifies values and symbolic conditions returned by a function either directly (via the return value) or indirectly (via a pointer passed as an argument) whenever the specified preconditions are met
- CHECKRET- specifies the number of times a result was checked for null before the value was returned
- CONDNORET- specifies functions that terminate the execution of a process or thread depending on the value of their parameters
- NORET- specifies functions that never return, such as exit and abort functions
- RETARG- specifies functions that return the value of their arguments, either when a argument is always the return value of a function or when it's returned through another argument
- xRET-specifies a dependency between a returned value and values modified by a function through its arguments
For examples of using NORET and CONDNORET in tuning Klocwork analysis, see Tuning C/C++ analysis.
Specification field syntax
RET
<precondition> : <postcondition>
CHECKRET
<checked> ',' <total>
CONDNORET
<socket_expression>
RETARG
'RETARG' '1' ':' <socket_expression> '=' <socket_expression>
xRET
<range_value-1> ':' <socket_expression> <range_value-2>
where
- <range_value-1> specifies the return value
- <range_value-2> specifies a value returned by reference
Example 1
foo - RET 1: $$ NE(0) foo - RET $1 GE(1), $2 EQ(0) : $$ GE(1), $$==$1, *$3 EQ (-1)
Each RET record declares that if the function receives parameters that match <precondition>, it returns the values and conditions specified by the <postcondition>. The first record says that for any given input this function returns (directly) a non-zero value. The second record says that if the 1st argument is greater than or equal to 1 and the 2nd argument is equal to 0, the function ‘foo’ returns a value that is equal to the value of the 1st argument and also known to be greater or equal to 1, and also the function ‘foo’ sets the value of the variable, pointed by the 3rd argument, to -1.
Example 2
kwapi_cfgparam_getParameterValue - CHECKRET 31,43
This CHECKRET record says that the number of calls to the function when the result is checked for null is 31, and the total number of calls is 43.
Example 3
check_status - CONDNORET ($1!=0)
This CONDNORET record says that check_status aborts program execution if its first argument is not equal to zero.
Example 4
It is important for Klocwork to know that control flow never returns from some calls. For example, in this snippet:
if (p==NULL) myAssertFunction("p == NULL"); strcpy(p,"Some string");
if Klocwork knows that myAssertFunction never returns, then it knows that following a call to strcpy never passes NULL as first argument. Otherwise, Klocwork issues a warning about a possible null pointer dereference.
myAssertFunction - NORET
This NORET record identifies myAssertFunction as a function that doesn't return.
Example 5
RETARG records specify that a function returns the value of its argument. Both of the following scenarios are supported:
- when a value passed through an argument always returned as the return value of the function
- when a value is returned through another argument (written into pointed or referenced memory)
A simple example of the first type of return is shown in the following code. For example, with the following definitions:
1 typedef struct Point { int x, y, z; } Point; 2 int point_getX(Point* p_p) { 3 return p_p->x; 4 } 5 Point p;
In the following statement:
y = point_getX(&p);
the value of p.x is assigned to y.
point_getX - RETARG 1:$$=$1-->Point::x reassign_filestream_1 - RETARG 1:*$1=*$2
The first record in this RETARG example says that point_getX returns field x of its first argument. The second record says that reassign_filestream_1 writes the value of the variable pointed to by the second argument into the variable pointed to by the first.
Example 6
acpi_ex_get_object_reference - XRET EQ(0): *$2 NE(0)
In this example, the xRET record says that acip_ex_get_object_reference returns 0 if it sets the dereferenced second variable to a value other than zero.
SETZERO records
SETZERO records specify that a function writes null bytes to the memory pointed to by an argument or part of an argument.
Specification field syntax
<socket_expression> ':' <Interval>
where
- <socket_expression> identifies the memory that is written by the function
- <Interval> is '['<boundary specification>','<boundary_specification>']'
- <boundary_specification> is a socket expression that specifies the range of written memory that is filled with null bytes
Example
ResetName - SETZERO $1 : [0,$2] initNode - SETZERO $1-><id>Node::name</id> : [160,160]
The first record of this example shows that the function 'ResetName' fills the first bytes of the memory area pointed to by the first parameter with null bytes. The number of written bytes is passed as the second argument. The second record shows that the function 'initNode' writes one null byte to the buffer 'name' (member of the structure passed through the first argument), using the constant 160 as an index.
SLEEP records
SLEEP records specify that a function may block program execution for a significant amount of time.
Specification field syntax
<socket_expression> ':' <Interval>
where
- <precondition> is a condition that must be met for the block operation, or 1 to indicate that there is no precondition and the block always happens
Example
read - SLEEP 1 WaitForSingleObject - SLEEP $2 NE(0)
The first record says that the function 'read' may suspend process execution for a period of time. The second record shows that the function 'WaitForSingleObject' may block process execution if its second argument is not equal to 0.
SQL Injection data records
The SQL Injection data records relate to functions that execute sql, or prepare statement objects that denote sql commands. The data records are:
-
SQLExec specifies a function that executes a SQL query
-
SQLProp specifies a function that propagates SQL queries from one parameter to another (strings, or prepared SQL statement objects)
-
SQLValidate specifies a function that checks a SQL query
Specification field syntax for SQLExec and SQLValidate
<socket_expression>
where
<socket_expression> defines the characteristics of the SQL query.
The following examples use the SQLite C/C++ API to illustrate use of the data records:
Example 1
sqlite3_exec - SQLExec $2
This record says that the second argument passed to sqlite3_exec denotes a SQL command, and will be executed in the call to sqlite3_exec.
Example 2
sqlite3_reset - SQLValidate $1
This record says that the first argument passed to sqlite3_reset denotes a SQL command, and will be validated in the call to sqlite3_reset. The validation, if successful, guarantees that the SQL command denoted by the first argument will be safe for execution subsequently (for example, using sqlite3_exec).
Specification field syntax for SQLProp
<socket-expression> : <socket-expression>
where
<socket_expression> defines the characteristics of the SQL query and the object to which it is propagated.
The following example uses the SQLite C/C++ API to illustrate the use of this data record:
Example 3
sqlite3_prepare_v2 - SQLProp $2 : *$4
This record says that the second argument of sqlite3_prepare_v2 denotes a SQL command which is used to construct the object pointed to by the fourth argument. Effectively, the SQL command stored in the second argument is propagated to the object pointed to by the fourth argument.
Tainted and unsafe data records
The tainted data records relate to functions that may return or use unvalidated data. The tainted data records are:
- TaintedIntData specifies a function that returns an integer
- TSCheckPT specifies a function that checks a string
- TSCheckXSS specifies a function that cleans the query string
- TSFMTsink specifies a function that returns a format string
- TSprop and TSsrc specify a function that returns a string
- TSSinkXSS specifies a function that can write to the stdout stream
- UnsafeAllocSizeAccepter specifies a function that uses data for memory allocation size
- UnsafeArrayIndexAccepter specifies a function that uses data as an array index
- UnsafeBinopAccepter specifies a function that uses data in an arithmetic binary operation
- UnsafeLoopBoundAccepter specifies a function that uses data as a loop bound
Specification field syntax
<socket-expression>
where <socket_expression> defines the characteristics of the tainted data
Example 1
win32_fread - TaintedIntData *$1
This record says that the variable pointed to by the first argument of win32_fread has a tainted value after the call.
Example 2
<function name> - TSCheckPT 1 : $<parameter number> : 1
In this record <function name> is the name of the function used to neutralize the file path and <parameter number> is the number of the parameter that receives the file path that requires neutralization.
Example 3
strcat - TSprop $1 : $1 , $2
This record says that the buffer passed to strcat as the first argument is tainted after returning from the call if the first or second arguments point to buffers with tainted data.
Example 4
str_new - UnsafeAllocSizeAccepter $1
This record says that the function str_new uses the first argument to calculate the size of memory that needs to be allocated, but does not check if the value is valid.
Username and password records
Username and password records relate to functions that perform some kind of authentication and take the specified data as input parameters. These records extend the HCC checker.
- HCC.SINK.USER records specify the username argument of such functions.
- HCC.SINK.PWD records specify the password argument of such functions.
Specification field syntax
<socket-expression>
where <socket_expression> defines the username function argument HCC.SINK.USER or the password function argument HCC.SINK.PWD
Example 1
db_connect - HCC.SINK.USER 1 : $2 : 1 db_connect - HCC.SINK.PWD 1 : $3 : 1
These records specify that the db_connect function uses the second argument as the username and the third argument as the password for authentication.
XMRF records
XMRF records specify if a function retains or transfers ownership of allocated memory by pointers passed into it. To see examples of how to create XMRF records to tune Klocwork analysis, see Tuning C/C++ analysis.
Notes on backwards compatibility
Previously, the knowledge base record for specifying ownership transfer was DMEM MRF/NMRF. Now, the record is called XMRF, and the syntax of the record has changed from the DMEM syntax. However, Klocwork still supports existing DMEM MRF/NMRF records.
Specification field syntax
<socket-expression> ':' <retention_flag>
where <retention_flag> is either 0 to signal that the caller does not retain ownership, or 1 to show that the caller does retain ownership
Example
f_act - XMRF $3 : 1 f_test - XMRF $2 : 0
These records indicate that function calling f_act retains ownership of the third argument, and function calling f_test transfers ownership of the second argument to f_test.