Add zero-length and one-element arrays to the list. While I continue replacing zero-length and one-element arrays with flexible-array members, I need a reference to point people to, so they don't introduce more instances of such arrays. And while here, add a note to the "open-coded arithmetic in allocator arguments" section, on the use of struct_size() and the arrays-to-deprecate mentioned here.
Signed-off-by: Gustavo A. R. Silva <gustavo...@kernel.org> --- Documentation/process/deprecated.rst | 84 ++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst index 652e2aa02a66c..622c8ac72a615 100644 --- a/Documentation/process/deprecated.rst +++ b/Documentation/process/deprecated.rst @@ -85,6 +85,12 @@ Instead, use the helper:: header = kzalloc(struct_size(header, item, count), GFP_KERNEL); +NOTE: If you are using struct_size() on a structure containing a zero-length +or a one-element array as a trailing array member, stop using such arrays +and switch to `flexible arrays +<https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays>`_ +instead. + See array_size(), array3_size(), and struct_size(), for more details as well as the related check_add_overflow() and check_mul_overflow() family of functions. @@ -200,3 +206,81 @@ All switch/case blocks must end in one of: * continue; * goto <label>; * return [expression]; + +Zero-length and one-element arrays +---------------------------------- +Old code in the kernel uses the zero-length and one-element array extensions +to the C90 standard, but the `preferred mechanism to declare variable-length +types +<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_ +such as these ones is a `flexible array member`, introduced in C99:: + + struct something { + int length; + char data[]; + }; + + struct something *instance; + + instance = kmalloc(struct_size(instance, data, size), GFP_KERNEL); + instance->length = size; + memcpy(instance->data, source, size); + +By making use of the mechanism above, we get a compiler error in case the +flexible array does not occur last in the structure, which helps to prevent +some kind of `undefined behavior <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76497732932f15e7323dc805e8ea8dc11bb587cf>`_ bugs from being inadvertently introduced to +the codebase. + +It is also important to notice that zero-length and one-element arrays pose +confusion for things like sizeof() and `CONFIG_FORTIFY_SOURCE`. 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 { + int length; + char data[0]; + }; + + struct something *instance; + + instance = kmalloc(struct_size(instance, data, size), GFP_KERNEL); + instance->length = size; + memcpy(instance->data, source, size); + ... + size = sizeof(instance->data); + +At the last line of code above, ``size`` turns out to be zero --when one might have +thought differently. Here are a couple examples of this issue `link 1 +<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f2cd32a443da694ac4e28fbf4ac6f9d5cc63a539>`_, +`link 2 +<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ab91c2a89f86be2898cee208d492816ec238b2cf>`_. +On the other hand, `flexible array members have incomplete type, and so the sizeof() +operator may not be applied <https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_, +so any misuse of such operator 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 +<https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_, +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 { + int length; + char data[1]; + }; + + struct something *instance; + + instance = kmalloc(struct_size(instance, data, size - 1), GFP_KERNEL); + instance->length = size; + memcpy(instance->data, source, size); + +In the example above, we had to remember to calculate ``size - 1`` when using +the struct_size() helper, otherwise we would have --unintentionally-- allocated +memory for one too many ``data`` objects. The cleanest and least error-prone way +to implement this is through the use of a `flexible array member`, which is +exemplified at the `beginning +<https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays>`_ +of this section. -- 2.27.0