2

Replace one-element arrays with flexible-array members · Issue #79 · KSPP/linux...

 1 year ago
source link: https://github.com/KSPP/linux/issues/79
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Collaborator

GustavoARSilva commented on Jun 3, 2020

edited by kees

List of open issues

There is a regular need in the kernel to provide a way to declare having a
dynamically sized set of trailing elements in a structure. Kernel code should
always use “flexible array members” for these cases. The older style of
one-element or zero-length arrays should no longer be used.

In older C code, dynamically sized trailing elements were done by specifying
a one-element array at the end of a structure:

struct something {
        size_t count;
        struct foo items[1];
};

This led to fragile size calculations via sizeof() (which would need to remove
the size of the single trailing element to get a correct size of the “header”).
A GNU C extension was introduced to allow for zero-length arrays, to avoid
these kinds of size problems:

struct something {
        size_t count;
        struct foo items[0];
};

But this led to other problems, and didn’t solve some problems shared by both
styles, like not being able to detect when such an array is accidentally being
used not at the end of a structure (which could happen directly, or when
such a struct was in unions, structs of structs, etc).

C99 introduced “flexible array members”, which lacks a numeric size for the
array declaration entirely:

struct something {
        size_t count;
        struct foo items[];
};

This is the way the kernel expects dynamically sized trailing elements to be
declared. It allows the compiler to generate errors when the flexible array
does not occur last in the structure, which helps to prevent some kind of
undefined behavior bugs from being inadvertently introduced to the codebase.
It also allows the compiler to correctly analyze array sizes (via sizeof(),
CONFIG_FORTIFY_SOURCE, and CONFIG_UBSAN_BOUNDS). For instance, there is no
mechanism that warns us that the following application of the sizeof() operator
to a zero-length array always results in zero:

struct something {
        size_t count;
        struct foo items[0];
};

struct something *instance;

instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
instance->count = count;

size = sizeof(instance->items) * instance->count;
memcpy(instance->items, source, size);

At the last line of code above, size turns out to be zero, when one might have
thought it represents the total size in bytes of the dynamic memory recently
allocated for the trailing array items. Here are a couple examples of this
issue: link 1, link 2. Instead, flexible array members have incomplete type, and so the
sizeof() operator may not be applied
, so any misuse of such operators will
be immediately noticed at build time.

With respect to one-element arrays, one has to be acutely aware that such
arrays occupy at least as much space as a single object of the type, hence they
contribute to the size of the enclosing structure
. This is prone to error every
time people want to calculate the total size of dynamic memory to allocate for
a structure containing an array of this kind as a member:

struct something {
        size_t count;
        struct foo items[1];
};

struct something *instance;

instance = kmalloc(struct_size(instance, items, count - 1), GFP_KERNEL);
instance->count = count;

size = sizeof(instance->items) * instance->count;
memcpy(instance->items, source, size);

In the example above, we had to remember to calculate count - 1 when using the
struct_size() helper, otherwise we would have –unintentionally– allocated memory
for one too many items objects. The cleanest and least error-prone way to
implement this is through the use of a flexible array member, instead:

struct something {
        size_t count;
        struct foo items[];
};

struct something *instance;

instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
instance->count = count;

memcpy(instance->items, source, flex_array_size(instance, items, instance->count));

Latest version of this document: https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK