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
|