https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101386

            Bug ID: 101386
           Summary: Analysis of attribute malloc's deallocator function is
                    not capable of abstraction
           Product: gcc
           Version: 11.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: analyzer
          Assignee: dmalcolm at gcc dot gnu.org
          Reporter: morrison.levi at gmail dot com
  Target Milestone: ---

Consider the program string_allocator.c below. It creates reference counted
strings. The analyzer will trigger `-Wanalyzer-mismatching-deallocation`, but
it is incorrect.

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

struct string_s {
  _Atomic size_t refcount;
  size_t len;
  char data[]; // flexible array member
};

void string_delete(struct string_s *string) {
  if (string && --string->refcount == 0) {
    free(string);
  }
}

__attribute__((malloc, malloc(string_delete)))
struct string_s *
string_alloc(size_t len) {
  struct string_s *obj = calloc(1, offsetof(struct string_s, data) + len + 1);
  if (obj) {
    obj->refcount = 1;
    obj->len = len;
  }
  return obj;
}

int main() {
  const char *hello_world = "Hello, World!";
  size_t size = strlen(hello_world);
  struct string_s *str = string_alloc(size);
  if (str) {
    memcpy(str->data + 0, hello_world, str->len);
    printf("%s\n", str->data + 0);
  }
  string_delete(str);
  return 0;
}
```

When compiling with:
$ gcc -g -O2 -std=c11 -Wall -Wextra -fanalyzer -fsanitize=address
string_allocator.c -o string_allocator

I get the warning:

string_allocator.c: In function 'main':
string_allocator.c:37:3: warning: 'str' should have been deallocated with
'free' but was deallocated with 'string_delete' [CWE-762]
[-Wanalyzer-mismatching-deallocation]
   37 |   string_delete(str);
      |   ^~~~~~~~~~~~~~~~~~
  'main': events 1-2
    |
    |   29 | int main() {
    |      |     ^~~~
    |      |     |
    |      |     (1) entry to 'main'
    |......
    |   32 |   struct string_s *str = string_alloc(size);
    |      |                          ~~~~~~~~~~~~~~~~~~
    |      |                          |
    |      |                          (2) calling 'string_alloc' from 'main'
    |
    +--> 'string_alloc': events 3-7
           |
           |   20 | string_alloc(size_t len) {
           |      | ^~~~~~~~~~~~
           |      | |
           |      | (3) entry to 'string_alloc'
           |   21 |   struct string_s *obj = calloc(1, offsetof(struct
string_s, data) + len + 1);
           |      |                         
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           |      |                          |
           |      |                          (4) allocated here (expects
deallocation with 'free')
           |   22 |   if (obj) {
           |      |      ~
           |      |      |
           |      |      (5) assuming 'obj' is non-NULL
           |      |      (6) following 'true' branch (when 'obj' is
non-NULL)...
           |   23 |     obj->refcount = 1;
           |      |     ~~~
           |      |     |
           |      |     (7) ...to here
           |
    <------+
    |
  'main': events 8-11
    |
    |   32 |   struct string_s *str = string_alloc(size);
    |      |                          ^~~~~~~~~~~~~~~~~~
    |      |                          |
    |      |                          (8) returning to 'main' from
'string_alloc'
    |   33 |   if (str) {
    |      |      ~                    
    |      |      |
    |      |      (9) following 'true' branch (when 'str' is non-NULL)...
    |   34 |     memcpy(str->data + 0, hello_world, str->len);
    |      |     ~~~~~~                
    |      |     |
    |      |     (10) ...to here
    |......
    |   37 |   string_delete(str);
    |      |   ~~~~~~~~~~~~~~~~~~      
    |      |   |
    |      |   (11) deallocated with 'string_delete' here; allocation at (4)
expects deallocation with 'free'
    |

Note that I also get this warning, which either I don't understand or it is
bogus:

string_allocator.c: In function 'string_alloc':
string_allocator.c:26:10: warning: leak of 'str' [CWE-401]
[-Wanalyzer-malloc-leak]
   26 |   return obj;
      |          ^~~
  'main': events 1-3
    |
    |   29 | int main() {
    |      |     ^~~~
    |      |     |
    |      |     (1) entry to 'main'
    |......
    |   32 |   struct string_s *str = string_alloc(size);
    |      |                          ~~~~~~~~~~~~~~~~~~
    |      |                          |
    |      |                          (2) allocated here
    |      |                          (3) calling 'string_alloc' from 'main'
    |
    +--> 'string_alloc': events 4-7
           |
           |   20 | string_alloc(size_t len) {
           |      | ^~~~~~~~~~~~
           |      | |
           |      | (4) entry to 'string_alloc'
           |   21 |   struct string_s *obj = calloc(1, offsetof(struct
string_s, data) + len + 1);
           |   22 |   if (obj) {
           |      |      ~
           |      |      |
           |      |      (5) following 'false' branch (when 'obj' is NULL)...
           |......
           |   26 |   return obj;
           |      |   ~~~~~~ ~~~
           |      |   |      |
           |      |   |      (7) 'str' leaks here; was allocated at (2)
           |      |   (6) ...to here
           |
  • [Bug analyzer/101386] New: Ana... morrison.levi at gmail dot com via Gcc-bugs

Reply via email to