Question

Je veux écrire un patch sémantique pour coccinelle, de sorte qu'il ajoutera des contrôles de if (ptr == NULL) ... après les appels à malloc où ils manquent.

Disons que j'ai le code source d'entrée suivante:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// memory leaks ignored

static void OK_simple(void)
{
 char *ptr;
 ptr = malloc(100);
 if (ptr == NULL) {
  return;
 }
 strcpy(ptr, "abcd");
}

static void NOT_OK_missing_NULL_check(void)
{
 char *ptr;
 ptr = malloc(100);
 strcpy(ptr, "abcd");
}

static void NOT_OK_NULL_check_too_late(void)
{
 char *ptr;
 ptr = malloc(100);
 strcpy(ptr, "abcd");
 if (ptr == NULL) {
  return;
 }
}

static void OK_code_between_allocation_and_NULL_check(void)
{
 char *ptr;
 ptr = malloc(100);
 printf("The NULL test does not have to be put immediately after\n");
 if (ptr == NULL) {
  return;
 }
 strcpy(ptr, "abcd");
}

static void OK_two_allocations(void)
{
 char *ptr1, *ptr2;
 ptr1 = malloc(100);
 ptr2 = malloc(100);
 if (ptr1 == NULL) {
  return;
 }
 if (ptr2 == NULL) {
  return;
 }
 strcpy(ptr1, "abcd");
 strcpy(ptr2, "abcd");
}

static void NOT_OK_two_allocations_with_one_missing_NULL_check(void)
{
 char *ptr1, *ptr2;
 ptr1 = malloc(100);
 ptr2 = malloc(100);
 if (ptr1 == NULL) {
  return;
 }
 strcpy(ptr1, "abcd");
 strcpy(ptr2, "abcd");
}

int main(int argc, char *argv[])
{
 (void)argc;
 (void)argv;
 OK_simple();
 NOT_OK_missing_NULL_check();
 NOT_OK_NULL_check_too_late();
 OK_code_between_allocation_and_NULL_check();
 OK_two_allocations();
 NOT_OK_two_allocations_with_one_missing_NULL_check();
 return 0;
}

Je suis en train de trouver un patch sémantique qui fait cela, mais j'ai du mal à obtenir à faire ce que je veux. Il suffit d'ajouter des tests NULL est sans condition aucun problème, le problème est de ne pas le faire lorsqu'ils ne sont pas nécessaires. Ce qui suit est ce que j'ai actuellement:

// this rule matches code that already have a NULL test

@already_have_proper_check@
statement S;
type T;
T* ptr;
expression E;
@@

ptr = malloc(E);
... when != ptr
if (ptr == NULL) S
//+dummy_change_just_to_verify_that_this_rule_matches();


// this rule adds NULL tests where missing

//@add_NULL_check depends on !already_have_proper_check@
@add_NULL_check@
type T;
//T* ptr != already_have_proper_check.ptr;
T* ptr;
expression E;
@@

ptr = malloc(E);
+if (ptr == NULL) {
+ insert_error_handling_here();
+}

Le problème avec ceci est que cela n'exclut pas les cas OK, et je ne comprends pas comment connecter les deux règles. Quelqu'un peut-il me aider?

Juste pour être clair à 100%, la sortie que je veux après l'exécution est la suivante coccinelle:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// memory leaks ignored

static void OK_simple(void)
{
 char *ptr;
 ptr = malloc(100);
 if (ptr == NULL) {
  return;
 }
 strcpy(ptr, "abcd");
}

static void NOT_OK_missing_NULL_check(void)
{
 char *ptr;
 ptr = malloc(100);
 if (ptr == NULL) {
  insert_error_handling_here();
 }
 strcpy(ptr, "abcd");
}

static void NOT_OK_NULL_check_too_late(void)
{
 char *ptr;
 ptr = malloc(100);
 if (ptr == NULL) {
  insert_error_handling_here();
 }
 strcpy(ptr, "abcd");
 if (ptr == NULL) {
  return;
 }
}

static void OK_code_between_allocation_and_NULL_check(void)
{
 char *ptr;
 ptr = malloc(100);
 printf("The NULL test does not have to be put immediately after\n");
 if (ptr == NULL) {
  return;
 }
 strcpy(ptr, "abcd");
}

static void OK_two_allocations(void)
{
 char *ptr1, *ptr2;
 ptr1 = malloc(100);
 ptr2 = malloc(100);
 if (ptr1 == NULL) {
  return;
 }
 if (ptr2 == NULL) {
  return;
 }
 strcpy(ptr1, "abcd");
 strcpy(ptr2, "abcd");
}

static void NOT_OK_two_allocations_with_one_missing_NULL_check(void)
{
 char *ptr1, *ptr2;
 ptr1 = malloc(100);
 ptr2 = malloc(100);
 if (ptr2 == NULL) {
  insert_error_handling_here();
 }
 if (ptr1 == NULL) {
  return;
 }
 strcpy(ptr1, "abcd");
 strcpy(ptr2, "abcd");
}

int main(int argc, char *argv[])
{
 (void)argc;
 (void)argv;
 OK_simple();
 NOT_OK_missing_NULL_check();
 NOT_OK_NULL_check_too_late();
 OK_code_between_allocation_and_NULL_check();
 OK_two_allocations();
 NOT_OK_two_allocations_with_one_missing_NULL_check();
 return 0;
}
Était-ce utile?

La solution

// find calls to malloc
@call@
expression ptr;
position p;
@@

ptr@p = malloc(...);

// find ok calls to malloc
@ok@
expression ptr;
position call.p;
@@

ptr@p = malloc(...);
... when != ptr
(
 (ptr == NULL || ...)
|
 (ptr != NULL || ...)
)

// fix bad calls to malloc
@depends on !ok@
expression ptr;
position call.p;
@@

ptr@p = malloc(...);
+ if (ptr == NULL) return;
scroll top