Обнаружение передающего указателя на неинициализированную переменную

#c #pointers #initialization #coccinelle

#c #указатели #инициализация #coccinelle

Вопрос:

Некоторые функции имеют аргумент указателя, который указывает, где должен быть сохранен результат вызова функции, но функция также требует, чтобы при вызове функции этот указатель указывал на некоторое значение, используемое в качестве входного значения (например, параметр ввода / вывода).

Я хочу обнаружить случаи, когда такие функции вызываются, указывая на неинициализированные переменные. Coccinelle должен быть в состоянии сделать это, однако я немного борюсь за это.

Пример целевого кода:

 #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 functionn");
        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(amp;n1);
        printf("Random number = %dn", n1);
    }

    // ok, declaration
    {
        int n2 = 4;

        cartoon_random_generator(amp;n2);
        printf("Random number = %dn", n2);
    }

    // ok, statement
    {
        int n3;

        n3 = 9;
        cartoon_random_generator(amp;n3);
        printf("Random number = %dn", n3);
    }

    // both ok and not ok
    {
        int n4, n9;

        n9 = 9;
        //strcmp(cartoon, "XKCD") == 0 ? cartoon_random_generator(amp;n4) : cartoon_random_generator(amp;n9);
        if (strcmp(cartoon, "XKCD") == 0)
            cartoon_random_generator(amp;n4);
        else
            cartoon_random_generator(amp;n9);
        printf("Random numbers = %d, %dn", n4, n9);
    }
}
  

Я написал следующий сценарий coccinelle

 /* 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(amp;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(amp;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(amp;n)
  

но правило 2 не учитывается, и я не понимаю, почему. Его запуск дает:

 $ /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(amp;n1);
                printf("Random number = %dn", n1);
        }

@@ -40,7  39,6 @@ static void test(const char *cartoon)
                int n3;

                n3 = 9;
-               cartoon_random_generator(amp;n3);
                printf("Random number = %dn", n3);
        }

@@ -51,9  49,7 @@ static void test(const char *cartoon)
                n9 = 9;
                //strcmp(cartoon, "XKCD") == 0 ? cartoon_random_generator(amp;n4) : cartoon_random_generator(amp;n9);
                if (strcmp(cartoon, "XKCD") == 0)
-                       cartoon_random_generator(amp;n4);
                else
-                       cartoon_random_generator(amp;n9);
                printf("Random numbers = %d, %dn", n4, n9);
        }
 }
  

Ответ №1:

Я новичок в использовании Coccinelle и хотел узнать об этом. Вопрос, который вы задали, является довольно хорошим требованием для обнаружения неинициализированной переменной, и это заставило меня немного исследовать. После небольшого исследования (и получения подсказки warning: rule3: inherited metavariable p2 not used in the -, , or context code ) один из способов (могут быть другие / лучшие способы) заставить ваш сценарий coccinelle работать — это объединить правила 1 и 2 и использовать только одно наследование для метапеременной в конечном правиле. Что-то в этих строках:

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

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

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

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

Результат, видимый в этом случае,:

 $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(amp;n1);
                printf("Random number = %dn", n1);
        }

@@ -51,9  50,6 @@ static void test(const char *cartoon)
                n9 = 9;
                //strcmp(cartoon, "XKCD") == 0 ? cartoon_random_generator(amp;n4) : cartoon_random_generator(amp;n9);
                if (strcmp(cartoon, "XKCD") == 0)
-                       cartoon_random_generator(amp;n4);
                else
                        cartoon_random_generator(amp;n9);
                printf("Random numbers = %d, %dn", n4, n9);
  

Это было запущено на FC15 с пакетами coccinelle, установленными из репозиториев Fedora.
Надеюсь, это поможет!

Комментарии:

1. Отлично. Мне очень понравилось, как rule1 теперь гораздо более четко выражает намерение.

Ответ №2:

Джулия также предложила следующую более простую версию

 @@
idexpression n;
expression E;
@@

... when != n = E
* cartoon_random_generator(amp;n)
  

Обновление: заменен идентификатор на idexpression, поскольку это лучше обрабатывает разные области блока.

Комментарии:

1. Большое спасибо за это! Это поможет мне узнать немного больше