public inbox for cocci@systeme.lip6.fr
 help / color / mirror / Atom feed
* [cocci] Depends on with negative matches
@ 2024-12-19  7:25 Mats Kindahl
  2024-12-19  7:53 ` Julia Lawall
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Mats Kindahl @ 2024-12-19  7:25 UTC (permalink / raw)
  To: cocci

[-- Attachment #1: Type: text/plain, Size: 5359 bytes --]

Hi all,

I want have a case where I want to find missing resource releases. The 
real case is a different one, so I am creating a similar case using 
malloc() and want to find any malloc() that is either not freeing the 
memory or returning it to the caller. Here are a few cases:

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

    struct something {
       const char *here;
       int ok;
    };

    /* This should match */
    void func_1() {
       struct something *foo;
       foo = malloc(sizeof(struct something));
       if (!foo) {
         printf("Error %s", foo->here);
         return;
       }
       foo->ok = 1;
    }

    /* This should NOT match */
    struct something *func_2() {
       struct something *foo;
       foo = malloc(sizeof(struct something));
       if (foo) {
         foo->ok = 1;
         return foo;
       }
       printf("Error %s", foo->here);
       return NULL;
    }

    /* This should NOT match */
    struct something *func_3() {
       struct something *foo;
       foo = malloc(sizeof(struct something));
       if (!foo) {
         printf("Error %s", foo->here);
         return NULL;
       }
       foo->ok = 1;
       return foo;
    }

    /* This should match */
    void func_4(struct something *foo) {
       if (!foo)
         foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
    }

    /* This should NOT match */
    void func_5(struct something *foo) {
       if (!foo)
         foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
       free(foo);
    }

    /* This should NOT match */
    struct something *func_6(struct something *foo) {
       if (!foo)
         foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
       return foo;
    }

I tried this semantic patch, but unfortunately it does not seem to 
capture the dependencies correctly.

    // Capture any malloc() to a local variable
    @r@
    local idexpression var;
    position p;
    @@
       var@p = malloc(...);

    // Detect cases that either free the variable or return it. Expect an
    // intermediate NULL check with error handling code that can do
    // anything it likes.
    @r1@
    local idexpression r.var;
    position r.p;
    statement S;
    @@
       var@p = malloc(...);
       ...
       if (var == NULL) S
       ...
    (
       free(var);
    |
       return var;
    )

    // Detect cases that either free the variable or return it. Expect an
    // non-NULL check that then either returns or free the variable. We
    // ignore any code that follows this if statement and assume that is
    // error handling code.
    @r2@
    local idexpression r.var;
    position r.p;
    @@
       var@p = malloc(...);
       ...
       if (var != NULL) {
         ...
    (
         free(var);
         ...
    |
         return var;
    )
       }

    // If neither of the above cases match, then we have a bad malloc, so
    // mark it.
    @depends on !r1 && !r2@
    local idexpression r.var;
    position r.p;
    @@
    * var@p = malloc(...);

Unfortunately, it matches correctly in most cases, but some of the cases 
are not detected correctly.

    mats@abzu:~/lang/cocci/tests$ spatch --sp-file missing_free.cocci
    code/malloc-example.c
    init_defs_builtins: /usr/lib/coccinelle/standard.h
    HANDLING: code/malloc-example.c
    diff =
    --- code/malloc-example.c
    +++ /tmp/cocci-output-172050-d31474-malloc-example.c
    @@ -9,7 +9,6 @@ struct something {
    /* This should match */
    void func_1() {
       struct something *foo;
    -  foo = malloc(sizeof(struct something));
       if (!foo) {
         printf("Error %s", foo->here);
         return;
    @@ -44,7 +43,6 @@ struct something *func_3() {
    /* This should match */
    void func_4(struct something *foo) {
       if (!foo)
    -    foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
    }
    @@ -52,7 +50,6 @@ void func_4(struct something *foo) {
    /* This should NOT match */
    void func_5(struct something *foo) {
       if (!foo)
    -    foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
       free(foo);
    @@ -61,7 +58,6 @@ void func_5(struct something *foo) {
    /* This should NOT match */
    struct something *func_6(struct something *foo) {
       if (!foo)
    -    foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
       return foo;

I tried to look at the kmalloc() examples in the coccinellery and also 
some other examples in the source code that seems to do similar things, 
but none have this case with "either free or return" and multiple cases 
with negative dependencies. It seems like you can get it mostly correct 
if you just keep the first rule only (with the NULL check), but I want 
to capture the "positive branch" case as well, where you check for 
non-NULL and then execute some code that should end with either a free 
or a return.

Anybody have some insight into what I've done wrong or if there is a 
better way to solve this problem?

Best wishes,
Mats Kindahl


[-- Attachment #2: Type: text/html, Size: 8384 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-19  7:25 [cocci] Depends on with negative matches Mats Kindahl
@ 2024-12-19  7:53 ` Julia Lawall
  2024-12-19 18:55   ` Mats Kindahl
  2024-12-19  9:52 ` [cocci] Detecting missing resource releases (with SmPL)? Markus Elfring
  2024-12-19 11:32 ` [cocci] Scope for source code analyses " Markus Elfring
  2 siblings, 1 reply; 11+ messages in thread
From: Julia Lawall @ 2024-12-19  7:53 UTC (permalink / raw)
  To: Mats Kindahl; +Cc: cocci

Thanks for the many test cases.

You can try this:

@r@
local idexpression var;
position p;
@@
var@p = malloc(...)

@@
local idexpression r.var;
position r.p;
@@

*var@p = malloc(...)
... when != true var == NULL
    when != false var != NULL
    when != free(var)
    when != return (var);

You may want to add some more when clauses like when != e->f = var

julia

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Detecting missing resource releases (with SmPL)?
  2024-12-19  7:25 [cocci] Depends on with negative matches Mats Kindahl
  2024-12-19  7:53 ` Julia Lawall
@ 2024-12-19  9:52 ` Markus Elfring
  2024-12-19 11:32 ` [cocci] Scope for source code analyses " Markus Elfring
  2 siblings, 0 replies; 11+ messages in thread
From: Markus Elfring @ 2024-12-19  9:52 UTC (permalink / raw)
  To: Mats Kindahl, cocci

> Anybody have some insight

Probably, yes.

Which information sources should get more development attention?


> into what I've done wrong

Some circumstances can make your source code analysis attempts more challenging.


> or if there is a better way to solve this problem?

Further software design possibilities can be taken better into account.
How will technical aspects be clarified accordingly?

Regards,
Markus

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Scope for source code analyses (with SmPL)?
  2024-12-19  7:25 [cocci] Depends on with negative matches Mats Kindahl
  2024-12-19  7:53 ` Julia Lawall
  2024-12-19  9:52 ` [cocci] Detecting missing resource releases (with SmPL)? Markus Elfring
@ 2024-12-19 11:32 ` Markus Elfring
  2 siblings, 0 replies; 11+ messages in thread
From: Markus Elfring @ 2024-12-19 11:32 UTC (permalink / raw)
  To: Mats Kindahl, cocci

> I want have a case where I want to find missing resource releases.
…

Does it matter for you if desired resource releases should usually be performed
within the same function implementation (or not)?

Would you like to care for inter-procedural source code analyses?
https://en.wikipedia.org/wiki/Control-flow_graph#Inter-procedural_control-flow_graph

See also:
https://gitlab.inria.fr/coccinelle/coccinelle/-/blob/f0ebfea0e00e844e16c4403d790e9a8df87036a9/docs/manual/cocci_syntax.tex#L1771-1774

Regards,
Markus

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-19  7:53 ` Julia Lawall
@ 2024-12-19 18:55   ` Mats Kindahl
  2024-12-19 19:54     ` Markus Elfring
  2024-12-20 21:54     ` Julia Lawall
  0 siblings, 2 replies; 11+ messages in thread
From: Mats Kindahl @ 2024-12-19 18:55 UTC (permalink / raw)
  To: Julia Lawall; +Cc: cocci

[-- Attachment #1: Type: text/plain, Size: 4554 bytes --]

Thank you Julia,

It worked for the example I gave, but the "real" code has validation 
functions rather than comparisons with NULL, and when I tested with 
that, it did not work. Here is a modified version of the code closer to 
what have:

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

    extern bool PointerIsValid(const void *pointer);

    struct something {
       const char *here;
       int ok;
    };

    /* This should match */
    void func_1() {
       struct something *foo;
       foo = malloc(sizeof(struct something));
       if (!PointerIsValid(foo)) {
         printf("Error %s", foo->here);
         return;
       }
       foo->ok = 1;
    }

    /* This should NOT match */
    struct something *func_2() {
       struct something *foo;
       foo = malloc(sizeof(struct something));
       if (PointerIsValid(foo)) {
         foo->ok = 1;
         return foo;
       }
       printf("Error %s", foo->here);
       return NULL;
    }

    /* This should NOT match */
    struct something *func_3() {
       struct something *foo;
       foo = malloc(sizeof(struct something));
       if (!PointerIsValid(foo)) {
         printf("Error %s", foo->here);
         return NULL;
       }
       foo->ok = 1;
       return foo;
    }

    /* This should match */
    void func_4(struct something *foo) {
       if (!PointerIsValid(foo))
         foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
    }

    /* This should NOT match */
    void func_5(struct something *foo) {
       if (!PointerIsValid(foo))
         foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
       free(foo);
    }

    /* This should NOT match */
    struct something *func_6(struct something *foo) {
       if (!PointerIsValid(foo))
         foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
       return foo;
    }

Here is the new version of the semantic patch:

    // Capture any malloc() to a local variable
    @r@
    local idexpression var;
    position p;
    @@
       var@p = malloc(...)

    @@
    local idexpression r.var;
    position r.p;
    @@
    * var@p = malloc(...);
       ... when != false !PointerIsValid(var)
           when != true PointerIsValid(var)
           when != free(var)
           when != return (var);

And here is the result.

    mats@abzu:~/lang/cocci/tests$ spatch --sp-file missing_free.cocci
    code/malloc-example.c
    init_defs_builtins: /usr/lib/coccinelle/standard.h
    HANDLING: code/malloc-example.c
    diff =
    --- code/malloc-example.c
    +++ /tmp/cocci-output-177912-b5da93-malloc-example.c
    @@ -12,7 +12,6 @@ struct something {
    /* This should match */
    void func_1() {
       struct something *foo;
    -  foo = malloc(sizeof(struct something));
       if (!PointerIsValid(foo)) {
         printf("Error %s", foo->here);
         return;
    @@ -23,7 +22,6 @@ void func_1() {
    /* This should NOT match */
    struct something *func_2() {
       struct something *foo;
    -  foo = malloc(sizeof(struct something));
       if (PointerIsValid(foo)) {
         foo->ok = 1;
         return foo;
    @@ -35,7 +33,6 @@ struct something *func_2() {
    /* This should NOT match */
    struct something *func_3() {
       struct something *foo;
    -  foo = malloc(sizeof(struct something));
       if (!PointerIsValid(foo)) {
         printf("Error %s", foo->here);
         return NULL;
    @@ -47,7 +44,6 @@ struct something *func_3() {
    /* This should match */
    void func_4(struct something *foo) {
       if (!PointerIsValid(foo))
    -    foo = malloc(sizeof(struct something));
       foo->ok = 1;
       printf("Error %s", foo->here);
    }

I was assuming that you could pass in any expression in the "when != 
true expr", since that is what the grammar says. Was that wrong or did I 
make a mistake?

Best wishes,
Mats Kindahl

On 2024-12-19 08:53, Julia Lawall wrote:
> Thanks for the many test cases.
>
> You can try this:
>
> @r@
> local idexpression var;
> position p;
> @@
> var@p = malloc(...)
>
> @@
> local idexpression r.var;
> position r.p;
> @@
>
> *var@p = malloc(...)
> ... when != true var == NULL
>      when != false var != NULL
>      when != free(var)
>      when != return (var);
>
> You may want to add some more when clauses like when != e->f = var
>
> julia

[-- Attachment #2: Type: text/html, Size: 7263 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-19 18:55   ` Mats Kindahl
@ 2024-12-19 19:54     ` Markus Elfring
  2024-12-20 21:54     ` Julia Lawall
  1 sibling, 0 replies; 11+ messages in thread
From: Markus Elfring @ 2024-12-19 19:54 UTC (permalink / raw)
  To: Mats Kindahl, cocci; +Cc: Julia Lawall

> It worked for the example I gave,

Nice …


> but the "real" code

Is this viewable by any public interfaces?


> has validation functions rather than comparisons with NULL,

How much will such an implementation detail matter?


> and when I tested with that, it did not work.

Will corresponding case distinctions be reconsidered?


…
> Here is the new version of the semantic patch:
>
>     // Capture any malloc() to a local variable
>     @r@
>     local idexpression var;
>     position p;
>     @@
>       var@p = malloc(...)
>
>     @@
>     local idexpression r.var;
>     position r.p;
>     @@
>     * var@p = malloc(...);
>       ... when != false !PointerIsValid(var)
>           when != true PointerIsValid(var)
>           when != free(var)
>           when != return (var);
…

I would appreciate further clarifications of previously mentioned development concerns.

Regards,
Markus

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-19 18:55   ` Mats Kindahl
  2024-12-19 19:54     ` Markus Elfring
@ 2024-12-20 21:54     ` Julia Lawall
  2024-12-21  9:08       ` Markus Elfring
  2024-12-21 14:24       ` Mats Kindahl
  1 sibling, 2 replies; 11+ messages in thread
From: Julia Lawall @ 2024-12-20 21:54 UTC (permalink / raw)
  To: Mats Kindahl; +Cc: cocci

I think you just had true and false backwards:

// Capture any malloc() to a local variable
@r@
local idexpression var;
position p;
@@
  var@p = malloc(...)

@@
local idexpression r.var;
position r.p;
@@
* var@p = malloc(...);
  ... when != true !PointerIsValid(var)
      when != false PointerIsValid(var)
      when != free(var)
      when != return (var);

For example, you don't want to complain if there is a path with no free,
but that path goes through a failing pointer is valid test.

julia

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-20 21:54     ` Julia Lawall
@ 2024-12-21  9:08       ` Markus Elfring
  2024-12-21 14:24       ` Mats Kindahl
  1 sibling, 0 replies; 11+ messages in thread
From: Markus Elfring @ 2024-12-21  9:08 UTC (permalink / raw)
  To: Julia Lawall, Mats Kindahl, cocci

…
> For example, you don't want to complain if there is a path with no free,
> but that path goes through a failing pointer is valid test.

Would you get into the mood to discuss algorithmic properties any more?

Regards,
Markus

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-20 21:54     ` Julia Lawall
  2024-12-21  9:08       ` Markus Elfring
@ 2024-12-21 14:24       ` Mats Kindahl
  2024-12-21 14:25         ` Julia Lawall
  2024-12-21 16:14         ` Markus Elfring
  1 sibling, 2 replies; 11+ messages in thread
From: Mats Kindahl @ 2024-12-21 14:24 UTC (permalink / raw)
  To: Julia Lawall; +Cc: cocci

Hi Julia,

Thanks you! I feel really stupid, but now it works. My only excuse is 
that the documentation for "when != true <expr>" is not very elaborate. :)

Thanks again Julia,
Mats Kindahl

On 2024-12-20 22:54, Julia Lawall wrote:
> I think you just had true and false backwards:
>
> // Capture any malloc() to a local variable
> @r@
> local idexpression var;
> position p;
> @@
>    var@p = malloc(...)
>
> @@
> local idexpression r.var;
> position r.p;
> @@
> * var@p = malloc(...);
>    ... when != true !PointerIsValid(var)
>        when != false PointerIsValid(var)
>        when != free(var)
>        when != return (var);
>
> For example, you don't want to complain if there is a path with no free,
> but that path goes through a failing pointer is valid test.
>
> julia

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-21 14:24       ` Mats Kindahl
@ 2024-12-21 14:25         ` Julia Lawall
  2024-12-21 16:14         ` Markus Elfring
  1 sibling, 0 replies; 11+ messages in thread
From: Julia Lawall @ 2024-12-21 14:25 UTC (permalink / raw)
  To: Mats Kindahl; +Cc: cocci



On Sat, 21 Dec 2024, Mats Kindahl wrote:

> Hi Julia,
>
> Thanks you! I feel really stupid, but now it works. My only excuse is that the
> documentation for "when != true <expr>" is not very elaborate. :)

Great that it works :)

julia

>
> Thanks again Julia,
> Mats Kindahl
>
> On 2024-12-20 22:54, Julia Lawall wrote:
> > I think you just had true and false backwards:
> >
> > // Capture any malloc() to a local variable
> > @r@
> > local idexpression var;
> > position p;
> > @@
> >    var@p = malloc(...)
> >
> > @@
> > local idexpression r.var;
> > position r.p;
> > @@
> > * var@p = malloc(...);
> >    ... when != true !PointerIsValid(var)
> >        when != false PointerIsValid(var)
> >        when != free(var)
> >        when != return (var);
> >
> > For example, you don't want to complain if there is a path with no free,
> > but that path goes through a failing pointer is valid test.
> >
> > julia
>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [cocci] Depends on with negative matches
  2024-12-21 14:24       ` Mats Kindahl
  2024-12-21 14:25         ` Julia Lawall
@ 2024-12-21 16:14         ` Markus Elfring
  1 sibling, 0 replies; 11+ messages in thread
From: Markus Elfring @ 2024-12-21 16:14 UTC (permalink / raw)
  To: Mats Kindahl, cocci

> I feel really stupid, but now it works.

Will any solution approaches become more interesting for known software limitations?

Regards,
Markus

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2024-12-21 16:14 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-19  7:25 [cocci] Depends on with negative matches Mats Kindahl
2024-12-19  7:53 ` Julia Lawall
2024-12-19 18:55   ` Mats Kindahl
2024-12-19 19:54     ` Markus Elfring
2024-12-20 21:54     ` Julia Lawall
2024-12-21  9:08       ` Markus Elfring
2024-12-21 14:24       ` Mats Kindahl
2024-12-21 14:25         ` Julia Lawall
2024-12-21 16:14         ` Markus Elfring
2024-12-19  9:52 ` [cocci] Detecting missing resource releases (with SmPL)? Markus Elfring
2024-12-19 11:32 ` [cocci] Scope for source code analyses " Markus Elfring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox