C/C++ knowledge base referenceC/C++ knowledge base referenceThe 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 syntaxAs 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
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
Socket expressions can also use properties:
and functions:
A conditional socket is expressed in the form: <precondition> : <socket_expression> : <postcondition> where
Specification fields for record kinds
ACQUIRE and RELEASE recordsACQUIRE 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
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 recordsALLOC 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
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 recordsBounds 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
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 recordsThe 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:
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 recordsBuffer 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
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 recordsThe 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:
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
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 records relate to functions that can cause division by zero, either returning a zero constant value, writing a zero constant value to a variable pointed to by their arguments or doing a division operation depending on input arguments. The DBZ records are:
Specification field syntax DBZ.SRC <conditional_socket> where
xDBZ <conditional_socket> where
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 recordsThe 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
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 recordsThe 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.
Specification field syntax RCA.HASH.SALT <conditional_socket> where
RCA.HASH.SALT.SIZE <conditional_socket> where
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 recordsNNTS.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
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 recordsNPD 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:
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.
R and W recordsR 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
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 recordsAll the RET records specify characteristics related to return values for functions. The RET records include:
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
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:
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 recordsSETZERO 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
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 recordsSLEEP records specify that a function may block program execution for a significant amount of time. Specification field syntax <socket_expression> ':' <Interval> where
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.
Tainted and unsafe data recordsThe tainted data records relate to functions that may return or use unvalidated data. The tainted data records are:
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 recordsUsername 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.
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 recordsXMRF 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. |