[clang] [BoundsSafety] Allow 'counted_by' attribute on pointers in structs in C (PR #90786)

Dan Liew via cfe-commits cfe-commits at lists.llvm.org
Fri May 10 16:56:18 PDT 2024


delcypher wrote:

> It's not a lie, because the contents of a pointer don't contribute to the size of the struct containing that pointer.

Consider this example. It tries to illustrate why putting `__counted_by()` on a pointer to a structs containing flexible array members doesn't make sense. 

```
struct HasFAM {
    int count;
    char buffer[] __counted_by(count); // This is OK
};

struct BufferOfFAMS {
    int count;
    struct HasFAM* fams __counted_by(count); // This is invalid
};

struct BufferOfFAMS* setup(void) {
    const int numFams = 2;
    const int famSizes[] = { 8, 16};

    struct BufferOfFAMS *f = (struct BufferOfFAMS *)malloc(
        sizeof(struct BufferOfFAMS) + (sizeof(struct HasFam *) * numFams));

    f->count = numFams;

    size_t famsBufferSize = 0;
    for (int i=0; i < numFams; ++i) {
        famsBufferSize += sizeof(struct HasFAM) + sizeof(char)* famSizes[i];
    }
    f->fams = (struct HasFAM*) malloc(famsBufferSize);
    memset(f->fams, 0, famsBufferSize);

    size_t byteOffset = 0;
    for (int i=0; i < numFams; ++i) {
        struct HasFAM* cur = (struct HasFAM*) (((char*)f->fams) + byteOffset);
        cur->count = famSizes[i];
        byteOffset = sizeof(struct HasFAM) + (sizeof(char) * famSizes[i]);
    }
    return f;
}

int main(void) {
    // How would we compute the bounds of f::fams here??
    // `sizeof(struct HasFAM) * f->count` is WRONG. It's too small but this
    // is what writing `__counted_by(count)` on `HasFAM::buffer` means.
    //
    // It's actually
    // `(sizeof(struct HasFAM) * f->count) + (sizeof(char) * (8 + 16))`
    //
    // This means we can't use the `__counted_by()` attribute on
    // `BufferOfFAMS::fams` to compute its bounds. That's the whole
    // point of the attribute.
    struct BufferOfFAMS* f = setup();

    // Similary doing any kind of indexing on the pointer 
    // is extremely problematic for exactly the same reason.
    // 
    // The address is completely wrong because the offset is computed using
    // `sizeof(struct HasFAM)` which won't include the array at the end.
    //
    // So `bogus_ptr` points to the first byte of the first `HasFAM::buffer`,
    // instead of the second `struct HasFAM`.
    struct HasFAM* bogus_ptr = &f->fams[1];
    return 0;
}
```

https://github.com/llvm/llvm-project/pull/90786


More information about the cfe-commits mailing list