Domanda

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 http://i.stack.imgur.com/VvTef.png and http://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);
        }
 }
È stato utile?

Soluzione

I am a total beginner in the usage of Coccinelle and had been meaning to learn about it. The question you have posed is a fairly good requirement of detecting uninitialized variable and this made me research a bit. After doing a little bit of research (& getting a clue from warning: rule3: inherited metavariable p2 not used in the -, +, or context code ) one of ways (there could be other/better ways) to make your coccinelle script work is to combine rules 1 and 2 & use only single inheritance for the metavariable in the final rule. Something on these lines:

@rule1@
position p1;
expression init_expression;
identifier n;
@@

(
int n = init_expression;
|
int n;
...
n = init_expression;
)
...
cartoon_random_generator@p1(&n)

@rule2@
position p2 != rule1.p1;
identifier n;
@@

int n;
...
* cartoon_random_generator@p2(&n)  

The output seen in this case is:

$spatch -sp_file cartoon_random.cocci cartoon_random.c
init_defs_builtins: /usr/share/coccinelle/standard.h
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);
        }

@@ -51,9 +50,6 @@ 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);

This was ran on FC15 with coccinelle packages installed from Fedora repos.
Hope this helps!

Altri suggerimenti

Julia also suggested the following simpler version

@@
idexpression n;
expression E;
@@

... when != n = E
* cartoon_random_generator(&n)

Update: replaced identifier with idexpression since that better handles different block scopes.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top