Hi all,

After some private discussion with JeanHeyd, I'd like to *partially*
withdraw my concerns against 'defer', as long as it ends up being as
simple as [[gnu::cleanup()]] (or reasonably similar).

The -fanalyzer is currently not smart enough to warn about strsep(3),
but that's a known bug, and will hopefully be fixed in time for defer.
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118500>.

With an inline definition of strsep(3), the -fanalyzer is indeed able to
warn, both with normal code, and with [[gnu::cleanup()]].  See below.

However, and this is a big however, with [[gnu::cleanup()]], it's a bit
less safe, since we need -O3 to trigger (-O2 is not enough), while the
diagnostic triggers at any optinization level for normal code.  So, I'm
still a bit skeptic about it, but it could be worse.

        alx@devuan:~/tmp/gcc$ cat malloc.c 
        #include <stdlib.h>
        #include <string.h>

        int foo(const void *);
        char *my_strsep(char **sp, const char *delim);
        void freep(void *p);

        void
        f(void)
        {
                [[gnu::cleanup(freep)]] char *p = NULL;

                p = strdup("asd;:qwe");
                my_strsep(&p, ":");

                return;
        }

        void
        g(void)
        {
                char *p;

                p = strdup("qwe:;asd");
                my_strsep(&p, ";");

                free(p);
                return;
        }

        char *my_strsep(char **sp, const char *delim)
        {
                char *s;

                s = *sp;
                if (s == NULL)
                        return NULL;

                *sp = strpbrk(s, delim);
                return s;
        }

        void
        freep(void *p)
        {
                free(*(void **) p);
        }

Compiled with -O2 or lower, only the normal function triggers a
diagnostic:

        alx@devuan:~/tmp/gcc$ gcc-15 -Wall -Wextra -fanalyzer -O2 -S malloc.c 
        malloc.c: In function ‘my_strsep’:
        malloc.c:39:13: warning: leak of ‘s’ [CWE-401] [-Wanalyzer-malloc-leak]
           39 |         *sp = strpbrk(s, delim);
              |         ~~~~^~~~~~~~~~~~~~~~~~~
          ‘g’: events 1-3
            │
            │   20 | g(void)
            │      | ^
            │      | |
            │      | (1) entry to ‘g’
            │......
            │   24 |         p = strdup("qwe:;asd");
            │      |             ~~~~~~~~~~~~~~~~~~
            │      |             |
            │      |             (2) allocated here
            │   25 |         my_strsep(&p, ";");
            │      |         ~~~~~~~~~~~~~~~~~~
            │      |         |
            │      |         (3) calling ‘my_strsep’ from ‘g’
            │
            └──> ‘my_strsep’: events 4-7
                   │
                   │   31 | char *my_strsep(char **sp, const char *delim)
                   │      |       ^~~~~~~~~
                   │      |       |
                   │      |       (4) entry to ‘my_strsep’
                   │......
                   │   36 |         if (s == NULL)
                   │      |            ~
                   │      |            |
                   │      |            (5) assuming ‘s’ is non-NULL
                   │      |            (6) following ‘false’ branch (when ‘s’ 
is non-NULL)... ─>─┐
                   │      |                                                     
                 │
                   │......
                   │      |                                                     
                 │
                   │      
|┌─────────────────────────────────────────────────────────────────────┘
                   │   39 |│        *sp = strpbrk(s, delim);
                   │      |│              ~~~~~~~~~~~~~~~~~
                   │      |│              |
                   │      |└─────────────>(7) ...to here
                   │
                 ‘my_strsep’: event 8
                   │
                   │   39 |         *sp = strpbrk(s, delim);
                   │      |         ~~~~^~~~~~~~~~~~~~~~~~~
                   │      |             |
                   │      |             (8) ⚠️  ‘s’ leaks here; was allocated 
at (2)
                   │

At -O3, both trigger a diagnostic:

        alx@devuan:~/tmp/gcc$ gcc-15 -Wall -Wextra -fanalyzer -O3 -S malloc.c 
        malloc.c: In function ‘f’:
        malloc.c:16:9: warning: leak of ‘<unknown>’ [CWE-401] 
[-Wanalyzer-malloc-leak]
           16 |         return;
              |         ^~~~~~
          ‘f’: events 1-3
            │
            │    9 | f(void)
            │      | ^
            │      | |
            │      | (1) entry to ‘f’
            │......
            │   13 |         p = strdup("asd;:qwe");
            │      |             ~~~~~~~~~~~~~~~~~~
            │      |             |
            │      |             (2) allocated here
            │   14 |         my_strsep(&p, ":");
            │      |         ~
            │      |         |
            │      |         (3) inlined call to ‘my_strsep’ from ‘f’
            │
            └──> ‘my_strsep’: events 4-7
                   │
                   │   36 |         if (s == NULL)
                   │      |            ^
                   │      |            |
                   │      |            (4) assuming ‘strdup("asd;:qwe")’ is 
non-NULL
                   │      |            (5) following ‘false’ branch... ─>─┐
                   │      |                                               │
                   │......
                   │      |                                               │
                   │      |┌──────────────────────────────────────────────┘
                   │   39 |│        *sp = strpbrk(s, delim);
                   │      |│              ~~~~~~~~~~~~~~~~~
                   │      |│              |
                   │      |└─────────────>(6) ...to here
                   │      |               (7) when ‘__builtin_strchr’ returns 
non-NULL
                   │
            <──────┘
            │
          ‘f’: event 8
            │
            │   16 |         return;
            │      |         ^~~~~~
            │      |         |
            │      |         (8) ⚠️  ‘<unknown>’ leaks here; was allocated at 
(2)
            │
        malloc.c: In function ‘g’:
        malloc.c:28:9: warning: leak of ‘<unknown>’ [CWE-401] 
[-Wanalyzer-malloc-leak]
           28 |         return;
              |         ^~~~~~
          ‘g’: events 1-3
            │
            │   20 | g(void)
            │      | ^
            │      | |
            │      | (1) entry to ‘g’
            │......
            │   24 |         p = strdup("qwe:;asd");
            │      |             ~~~~~~~~~~~~~~~~~~
            │      |             |
            │      |             (2) allocated here
            │   25 |         my_strsep(&p, ";");
            │      |         ~
            │      |         |
            │      |         (3) inlined call to ‘my_strsep’ from ‘g’
            │
            └──> ‘my_strsep’: events 4-7
                   │
                   │   36 |         if (s == NULL)
                   │      |            ^
                   │      |            |
                   │      |            (4) assuming ‘strdup("qwe:;asd")’ is 
non-NULL
                   │      |            (5) following ‘false’ branch... ─>─┐
                   │      |                                               │
                   │......
                   │      |                                               │
                   │      |┌──────────────────────────────────────────────┘
                   │   39 |│        *sp = strpbrk(s, delim);
                   │      |│              ~~~~~~~~~~~~~~~~~
                   │      |│              |
                   │      |└─────────────>(6) ...to here
                   │      |               (7) when ‘__builtin_strchr’ returns 
non-NULL
                   │
            <──────┘
            │
          ‘g’: event 8
            │
            │   28 |         return;
            │      |         ^~~~~~
            │      |         |
            │      |         (8) ⚠️  ‘<unknown>’ leaks here; was allocated at 
(2)
            │
        In function ‘my_strsep’,
            inlined from ‘g’ at malloc.c:25:2:
        malloc.c:39:15: warning: leak of ‘strdup("qwe:;asd")’ [CWE-401] 
[-Wanalyzer-malloc-leak]
           39 |         *sp = strpbrk(s, delim);
              |               ^~~~~~~~~~~~~~~~~
          ‘g’: events 1-3
            │
            │   20 | g(void)
            │      | ^
            │      | |
            │      | (1) entry to ‘g’
            │......
            │   24 |         p = strdup("qwe:;asd");
            │      |             ~~~~~~~~~~~~~~~~~~
            │      |             |
            │      |             (2) allocated here
            │   25 |         my_strsep(&p, ";");
            │      |         ~
            │      |         |
            │      |         (3) inlined call to ‘my_strsep’ from ‘g’
            │
            └──> ‘my_strsep’: events 4-8
                   │
                   │   36 |         if (s == NULL)
                   │      |            ^
                   │      |            |
                   │      |            (4) assuming ‘strdup("qwe:;asd")’ is 
non-NULL
                   │      |            (5) following ‘false’ branch... ─>─┐
                   │      |                                               │
                   │......
                   │      |                                               │
                   │      |┌──────────────────────────────────────────────┘
                   │   39 |│        *sp = strpbrk(s, delim);
                   │      |│              ~~~~~~~~~~~~~~~~~
                   │      |│              |
                   │      |└─────────────>(6) ...to here
                   │      |               (7) when ‘__builtin_strchr’ returns 
NULL
                   │      |               (8) ⚠️  ‘strdup("qwe:;asd")’ leaks 
here; was allocated at (2)
                   │
        In function ‘my_strsep’,
            inlined from ‘f’ at malloc.c:14:2:
        malloc.c:39:15: warning: leak of ‘strdup("asd;:qwe")’ [CWE-401] 
[-Wanalyzer-malloc-leak]
           39 |         *sp = strpbrk(s, delim);
              |               ^~~~~~~~~~~~~~~~~
          ‘f’: events 1-3
            │
            │    9 | f(void)
            │      | ^
            │      | |
            │      | (1) entry to ‘f’
            │......
            │   13 |         p = strdup("asd;:qwe");
            │      |             ~~~~~~~~~~~~~~~~~~
            │      |             |
            │      |             (2) allocated here
            │   14 |         my_strsep(&p, ":");
            │      |         ~
            │      |         |
            │      |         (3) inlined call to ‘my_strsep’ from ‘f’
            │
            └──> ‘my_strsep’: events 4-8
                   │
                   │   36 |         if (s == NULL)
                   │      |            ^
                   │      |            |
                   │      |            (4) assuming ‘strdup("asd;:qwe")’ is 
non-NULL
                   │      |            (5) following ‘false’ branch... ─>─┐
                   │      |                                               │
                   │......
                   │      |                                               │
                   │      |┌──────────────────────────────────────────────┘
                   │   39 |│        *sp = strpbrk(s, delim);
                   │      |│              ~~~~~~~~~~~~~~~~~
                   │      |│              |
                   │      |└─────────────>(6) ...to here
                   │      |               (7) when ‘__builtin_strchr’ returns 
NULL
                   │      |               (8) ⚠️  ‘strdup("asd;:qwe")’ leaks 
here; was allocated at (2)
                   │


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es/>

Attachment: signature.asc
Description: PGP signature

Reply via email to