Identifying Structs in Embedded C

We know what we are, but know not what we may be

Shakespeare

Most of us are familiar with the idea of metadata tags on things. Photographs, Kubernetes resources, magic numbers on files, etc.

One place I use metadata tags in the form of magic numbers is when developing embedded systems code in C. This has a number of advantages as it can detect invalid arguments, use-after-free and a couple of other annoyingly hard to track down bugs.

During some recent development I caught two cases with this technique: Firstly a bug in the actual ESP-IDF component for handling a knob which called an event handler with the wrong usr_data, secondly a case where I called release for an event handler with an incorrect argument, causing the event handler to fire after the data it was going to operate on was free-ed. (see below for a screenshot)

All you need is this small header file (customized for ESP32 IDF):

#pragma once

#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_err.h"

// This flag is used to enable magic number checking on the usr_data.
// You can make this a compile time option in SDK config
#define STRUCT_MAGIC

#ifdef STRUCT_MAGIC
typedef struct magic {
    uint16_t magic;
} magic_t;

#define STRUCT_MAGIC_NUM 0xF3
#define STRUCT_MAKE_MAGIC(m) ((m << 8) | STRUCT_MAGIC_NUM)
#define STRUCT_GET_MAGIC(m) (m & 0xFF)
inline void STRUCT_CHECK_MAGIC(void *v, uint16_t m, const char *tag, const char *e) {
    if(v) {
        if(!((magic_t *)v)->magic) {
            ESP_LOGE(tag,"Uninitialized or use after free: (0x%04x) %s",((magic_t *)v)->magic,e);
            ESP_ERROR_CHECK(ESP_ERR_INVALID_STATE);
        }
        if(((magic_t*)v)->magic!=(m)) {
            ESP_LOGE(tag,"Invalid magic: (0x%04x)!=(0x%04x) %s",((magic_t *)v)->magic,m,e);
            ESP_ERROR_CHECK(ESP_ERR_INVALID_VERSION);
        }
    } else {
        ESP_LOGE(tag,"Null pointer: %s",e);
        ESP_ERROR_CHECK(ESP_ERR_INVALID_ARG);
    }
}
#define MAGIC_FIELD uint16_t magic
inline void STRUCT_INIT_MAGIC(void *v, uint16_t m) { (((magic_t*)v)->magic=m); }

// Call this before freeing a struct to trigger asserts on use after free
inline void STRUCT_INVALIDATE(void *v) {((magic_t *)v)->magic = 0x0000;}
#else
#define MAGIC_FIELD
inline void STRUCT_CHECK_MAGIC(void *v, uint16_t m, const char *tag,const char *e) {}
inline void STRUCT_INVALIDATE(void *v) {}
inline void STRUCT_INIT_MAGIC(void *v, uint16_t m) {}
#endif

So how does this all work? Firstly, you need to know something about C Structs, in that they are laid out in memory in such a way that you can use the first field of a struct without knowing the rest of the fields. We use this trick to define a struct with just a single field, magic All our other structs now include this field as the first element in their field list like so:

typedef struct my_struct {
    MAGIC_FIELD;
    ... other fields ...
} my_struct_t;

The magic number library expands the macro MAGIC_FIELD to either nothing (if disabled) or a placeholder for a number unique to this type of struct. The check code can cast the pointer to the magic_t struct type and see only the magic field,

We then initialize the struct as follows:

#ifdef STRUCT_MAGIC
#define MY_STRUCT_MAGIC STRUCT_MAKE_MAGIC(0xF0)  <--- Choose a unique number!
#endif


    my_struct_t *mine = calloc(1, sizeof(my_struct_t));
    STRUCT_INIT_MAGIC(mine, MY_STRUCT_MAGIC);
    ... initialize rest of fields ...

Now every time you get passed a void *pointer which should be to this struct, you can check the magic number, for example in an event handler:

static void my_struct_event(void *data, event_t event) {
    my_struct_t *mine = (my_struct_t *)data;
    // TAG here is just a const string for the module name
    STRUCT_CHECK_MAGIC(mine, MY_STRUCT_MAGIC, TAG, "event");
    ... Now we know this struct is safe to use, do stuff with it ...
}

Finally, when we are done with it we mark is as invalid

static void my_struct_free(void *data) {
    my_struct_t *mine = (my_struct_t *)data;
    STRUCT_CHECK_MAGIC(mine, MY_STRUCT_MAGIC, TAG, "free");
    ... free other fields ...
    STRUCT_INVALIDATE(mine);  <--- Any check after this will fail
    free(mine);
}

One response to “Identifying Structs in Embedded C”

  1. And to think that as recently as a decade ago, there were companies charging thousands of dollars for libraries which basically did exactly this.