From: "Gustavo A. R. Silva" <gustavoars@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>, Kees Cook <keescook@chromium.org>
Cc: linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
"Gustavo A. R. Silva" <gustavo@embeddedor.com>
Subject: [PATCH v3] docs: deprecated.rst: Add zero-length and one-element arrays
Date: Mon, 8 Jun 2020 16:37:11 -0500 [thread overview]
Message-ID: <20200608213711.GA22271@embeddedor> (raw)
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.
Co-developed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
---
Changes in v3:
- Add changes written by Kees.
- Add Co-developed-by tag to include Kees in the changelog text.
Changes in v2:
- Adjust some markup links for readability.
Documentation/process/deprecated.rst | 118 +++++++++++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst
index 652e2aa02a66c..bd4c92244de31 100644
--- a/Documentation/process/deprecated.rst
+++ b/Documentation/process/deprecated.rst
@@ -85,6 +85,11 @@ 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, please refactor such
+ array usage and switch to a `flexible array member
+ <#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 +205,116 @@ All switch/case blocks must end in one of:
* continue;
* goto <label>;
* return [expression];
+
+Zero-length and one-element arrays
+----------------------------------
+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" <https://en.wikipedia.org/wiki/Flexible_array_member>`_
+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 <https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>`_
+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
+<https://git.kernel.org/linus/76497732932f15e7323dc805e8ea8dc11bb587cf>`_
+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
+<https://git.kernel.org/linus/f2cd32a443da694ac4e28fbf4ac6f9d5cc63a539>`_,
+`link 2
+<https://git.kernel.org/linus/ab91c2a89f86be2898cee208d492816ec238b2cf>`_.
+Instead, `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 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
+<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 {
+ 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`::
+
+ struct something {
+ size_t count;
+ struct foo items[];
+ };
+
+ struct something *instance;
+
+ instance = kmalloc(struct_size(instance, items, count), GFP_KERNEL);
+ instance->count = count;
+
+ size = sizeof(instance->items[0]) * instance->count;
+ memcpy(instance->items, source, size);
--
2.27.0
next reply other threads:[~2020-06-08 21:32 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-06-08 21:37 Gustavo A. R. Silva [this message]
2020-06-10 5:56 ` [PATCH v3] docs: deprecated.rst: Add zero-length and one-element arrays Kees Cook
2020-06-19 19:39 ` Jonathan Corbet
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200608213711.GA22271@embeddedor \
--to=gustavoars@kernel.org \
--cc=corbet@lwn.net \
--cc=gustavo@embeddedor.com \
--cc=keescook@chromium.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.