Search code examples
cclang-tidy

Avoid security.ArrayBound warning when using container_of


I have code using container_of macros to get parent structure pointer from inner structure pointer. However, it triggers the clang-tidy security.ArrayBound warning.

For instance the following code

#include <stdio.h>
#include <stddef.h>
#include <assert.h>


#define container_of(ptr, type, member) ({ \
    const void *__mptr = (void *)(ptr); \
    (type *)((char *)__mptr - offsetof(type, member)); \
})

/* Simple inner structure */
struct inner_data {
    int id;
    float value;
};

/* Container structure that includes the inner structure */
struct container {
    double extra_value;
    char name[32];
    int count;
    struct inner_data data;  /* Embedded inner structure */
};

/* Function that works with the inner structure */
void update_data(struct inner_data *data, float new_value) {
    /* Get access to the container using container_of */
    struct container *parent = container_of(data, struct container, data);

    /* Now we can update container fields */
    parent->extra_value = new_value * 2.0;
}

int main()
{
    struct container my_container;
    struct inner_data * mydata = &my_container.data;
    update_data(mydata, 200.0);
}

Generates the following warning :

clang-tidy-21 -checks=clang-analyzer-* test.c -- -std=c11 -O2
1 warning generated.
/tmp/test.c:31:13: warning: Out of bound access to memory preceding 'my_container.data' [clang-analyzer-security.ArrayBound]
   31 |     parent->extra_value = new_value * 2.0;
      |             ^
/tmp/test.c:38:5: note: Calling 'update_data'
   38 |     update_data(mydata, 200.0);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~
/tmp/test.c:31:13: note: Access of 'my_container.data' at negative byte offset -44
   31 |     parent->extra_value = new_value * 2.0;
      |     ~~~~~~~~^~~~~~~~~~~

Solution

  • I used the __clang_analyzer__ macro to trick the analyzer into not computing the pointer offset.

    #ifdef __clang_analyzer__
    #define container_of(ptr, type, member) ({ void *__consume(void *p); (type *)__consume(ptr); })
    #else
    #define container_of(ptr, type, member) ({              \
        const typeof(((type *)0)->member) * __mptr = (ptr); \
        (type *)((char *)__mptr - offsetof(type, member)); })
    #endif
    

    To me, the decoupling possibility offered by container_of are worth the associated misuse risk.