Some functions have a pointer argument that points to where a result from calling the function should be stored, but the function also require that when calling the function this pointer points to some value used as input value (e.g. an in/out parameter).
I want to detect cases where such functions are called pointing to uninitialized variables. Coccinelle should be able to do this, however I struggle a little to achieve this.
Example target code:
#include <string.h>
#include <stdio.h>
static void cartoon_random_generator(int *n)
{
switch (*n) {
case 4:
*n = 4; /* http://xkcd.com/221/ */
break;
case 9:
*n = 9; /* http://dilbert.com/strips/comic/2001-10-25/ */
break;
default:
fprintf(stderr, "*n was not initialized before calling this function\n");
break;
}
}
/* alternative links https://i.stack.imgur.com/VvTef.png and https://i.stack.imgur.com/u0iJ7.gif */
static void test(const char *cartoon)
{
// not ok, missing
{
int n1;
cartoon_random_generator(&n1);
printf("Random number = %d\n", n1);
}
// ok, declaration
{
int n2 = 4;
cartoon_random_generator(&n2);
printf("Random number = %d\n", n2);
}
// ok, statement
{
int n3;
n3 = 9;
cartoon_random_generator(&n3);
printf("Random number = %d\n", n3);
}
// both ok and not ok
{
int n4, n9;
n9 = 9;
//strcmp(cartoon, "XKCD") == 0 ? cartoon_random_generator(&n4) : cartoon_random_generator(&n9);
if (strcmp(cartoon, "XKCD") == 0)
cartoon_random_generator(&n4);
else
cartoon_random_generator(&n9);
printf("Random numbers = %d, %d\n", n4, n9);
}
}
I have written the following coccinelle script
/* It is an error to call cartoon_random_generator with an uninitialized
variable. Detect this. */
/*
* This rule matches an OK case where the in variable is initialized when
* declared. No action is performed for this rule other than giving p1 a value.
*/
@rule1@
position p1;
expression init_expression;
identifier n;
@@
int n = init_expression;
...
cartoon_random_generator@p1(&n)
/*
* This rule matches an OK case where the in variable is initialized in a
* separate statement. No action is performed for this rule other than
* giving p2 a value.
*/
@rule2@
position p2;
expression init_expression;
identifier n;
@@
int n;
...
n = init_expression;
...
cartoon_random_generator@p2(&n)
/* If neither rule1 or rule2 have matched so far,
* we have a variable that is uninitialized. */
@rule3@
position p3 != rule1.p1, rule2.p2;
identifier n;
@@
int n;
...
* cartoon_random_generator@p3(&n)
but rule2 is not taken into account and I do not understand why. Running it gives:
$ /opt/coccinelle/bin/spatch -sp_file cartoon_random.cocci cartoon_random.c
init_defs_builtins: /opt/coccinelle/share/coccinelle/standard.h
warning: rule3: inherited metavariable p2 not used in the -, +, or context code
HANDLING: cartoon_random.c
diff =
--- cartoon_random.c
+++ /tmp/cocci-output-7916-8df75b-cartoon_random.c
@@ -23,7 +23,6 @@ static void test(const char *cartoon)
{
int n1;
- cartoon_random_generator(&n1);
printf("Random number = %d\n", n1);
}
@@ -40,7 +39,6 @@ static void test(const char *cartoon)
int n3;
n3 = 9;
- cartoon_random_generator(&n3);
printf("Random number = %d\n", n3);
}
@@ -51,9 +49,7 @@ static void test(const char *cartoon)
n9 = 9;
//strcmp(cartoon, "XKCD") == 0 ? cartoon_random_generator(&n4) : cartoon_random_generator(&n9);
if (strcmp(cartoon, "XKCD") == 0)
- cartoon_random_generator(&n4);
else
- cartoon_random_generator(&n9);
printf("Random numbers = %d, %d\n", n4, n9);
}
}