diff mbox series

[v2,1/2] Introduce flexible array struct helpers

Message ID 20221024172058.534477-1-keescook@chromium.org (mailing list archive)
State Deferred, archived
Headers show
Series Introduce flexible array struct helpers | expand

Commit Message

Kees Cook Oct. 24, 2022, 5:20 p.m. UTC
The compiler is not able to automatically perform bounds checking on
structures that end in flexible arrays: __builtin_object_size() is
compile-time only, and __builtin_dynamic_object_size() may not always
be able to figure it out. Any possible run-time checks are currently
short-circuited (or trigger false positives) because there isn't
an obvious common way to figure out the bounds of such a structure.
C has no way (yet[1]) to signify which struct member holds the number
of allocated flexible array elements (like exists in other languages).

As a result, the kernel (and C projects generally) need to manually
check the bounds, check the element size calculations, and perform sanity
checking on all the associated variable types in between (e.g. 260
cannot be stored in a u8). This is extremely fragile.

However, even if we could do all this through a magic memcpy(), the API
itself doesn't provide meaningful feedback, which forces the kernel
into an "all or nothing" approach: either do the copy or panic the
system. Any failure conditions should be _detectable_, with API users
able to gracefully recover.

To deal with these needs, create the first of a set of helper functions
that do the work of memcpy() but perform the needed bounds checking
based on the arguments given: flex_cpy().

This API will be expanded in the future to also include the common
pattern of "allocate and copy": flex_dup(), "deserialize and copy":
mem_to_flex(), and "deserialize, allocate, and copy": mem_to_flex_dup().

The concept of a "flexible array structure" is introduced, which is a
struct that has both a trailing flexible array member _and_ an element
count member. If a struct lacks the element count member, it's just a
blob: there are no bounds associated with it.

The most common style of flexible array struct in the kernel is a
"simple" one, where both the flex-array and element-count are present:

    struct flex_array_struct_example {
        ...		/* arbitrary members */
        u16 part_count;	/* count of elements stored in "parts" below. */
        ...		/* arbitrary members */
        u32 parts[];	/* flexible array with elements of type u32. */
    };

Next are "encapsulating flexible array structs", which is just a struct
that contains a flexible array struct as its final member:

    struct encapsulating_example {
        ...		/* arbitrary members */
        struct flex_array_struct_example fas;
    };

There are also "split" flex array structs, which have the element-count
member in a separate struct level than the flex-array member:

    struct split_example {
        ...		/* arbitrary members */
        u16 part_count;	/* count of elements stored in "parts" below. */
        ...		/* arbitrary members */
        struct blob_example {
            ...		/* other blob members */
            u32 parts[];/* flexible array with elements of type u32. */
        } blob;
    };

To have the helpers deal with these arbitrary layouts, the names of the
flex-array and element-count members need to be specified with each use
(since C lacks the array-with-length syntax[1] so the compiler cannot
automatically determine them). However, for the "simple" (most common)
case, we can get close to "automatic" by explicitly declaring common
member aliases "__flex_array_elements", and "__flex_array_elements_count"
respectively. The regular helpers use these members, but extended helpers
exist to cover the other two code patterns.

For example, using the newly introduced flex_cpy():

    /* Flexible array struct with members identified. */
    struct simple {
        int mode;
        DECLARE_FAS_COUNT(int, how_many);
        unsigned long flags;
        DECLARE_FAS_ARRAY(u32, value);
    };
    ...

    int do_simple(struct simple *src) {
        struct simple *instance = NULL;
        size_t bytes;
        int rc;

        /* Allocate */
        if (fas_bytes(src, &bytes))
            return -E2BIG;
        instance = kmalloc(bytes, GFP_KERNEL);
        if (!instance)
            return -ENOMEM;
        instance->how_many = src->how_many;
        /* Copy */
        rc = flex_cpy(instance, src);
        if (rc) {
            kfree(instance);
            return rc;
        }
        return 0;
    }

If anything goes wrong, it returns a negative errno. Note that the
"allocate" pattern above will be the basis of the future flex_dup()
helper.

With these helpers the kernel can move away from many of the open-coded
patterns of using memcpy() with a dynamically-sized destination buffer.

[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1990.htm

Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
Cc: Keith Packard <keithp@keithp.com>
Cc: Francis Laniel <laniel_francis@privacyrequired.com>
Cc: Daniel Axtens <dja@axtens.net>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Tadeusz Struk <tadeusz.struk@linaro.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220504014440.3697851-3-keescook@chromium.org
---
 include/linux/flex_array.h  | 325 ++++++++++++++++++++++++++++++++++++
 include/linux/string.h      |   1 +
 include/uapi/linux/stddef.h |  14 ++
 3 files changed, 340 insertions(+)
 create mode 100644 include/linux/flex_array.h

Comments

Keith Packard Oct. 24, 2022, 6:35 p.m. UTC | #1
Kees Cook <keescook@chromium.org> writes:

> + * struct flex_array_struct_example {
> + *	...			 // arbitrary members
> + *	bounded_flex_array(
> + *		u16, part_count, // count of elements stored in "parts" below.
> + *		u32, parts	 // flexible array with elements of type u32.
> + *	);
> + * );

> + * struct flex_array_struct_example {
> + *	...		// position-sensitive members
> + *	// count of elements stored in "parts" below.
> + *	DECLARE_FAS_COUNT(u16, part_count);
> + *	..		// position-sensitive members
> + *	// flexible array with elements of type u32.
> + *	DECLARE_FAS_ARRAY(u32, parts);
> + * };

I'm sure there's a good reason, but these two macros appear to be doing
similar things and yet have very different naming conventions. Maybe:

        FAS_DECLARE_COUNT(type, name)
        FAS_DECLARE_ARRAY(type, name)
        FAS_DECLARE(size_type, size_name, array_type, array_name)

> +/* For use with flexible array structure helpers, in <linux/flex_array.h> */
> +#define __DECLARE_FAS_COUNT(TYPE, NAME)					\
> +	union {								\
> +		TYPE __flex_array_elements_count;			\
> +		TYPE NAME;						\
> +	}

How often could that second "public" member be 'const'? That would catch
places which accidentally assign to this field.

For code which does want to write to this field, is it mostly trimming
data from the end, or does it actually smash in arbitrary values? For
the former case, would it be helpful to have a test to make sure the
assigned size isn't larger than the real size (yeah, that would probably
take an extra field holding the real size), or larger than the current size?
kernel test robot Oct. 25, 2022, 6:12 a.m. UTC | #2
Hi Kees,

I love your patch! Perhaps something to improve:

[auto build test WARNING on xfs-linux/for-next]
[also build test WARNING on kees/for-next/pstore drm-tip/drm-tip linus/master v6.1-rc2 next-20221025]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Kees-Cook/Introduce-flexible-array-struct-helpers/20221025-031100
base:   https://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git for-next
patch link:    https://lore.kernel.org/r/20221024172058.534477-1-keescook%40chromium.org
patch subject: [PATCH v2 1/2] Introduce flexible array struct helpers
config: arm-allyesconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/04402c9f14fd381697b7aa744713a81aca0077cc
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Kees-Cook/Introduce-flexible-array-struct-helpers/20221025-031100
        git checkout 04402c9f14fd381697b7aa744713a81aca0077cc
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash drivers/crypto/caam/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from include/linux/string.h:259,
                    from include/linux/bitmap.h:11,
                    from include/linux/cpumask.h:12,
                    from include/linux/mm_types_task.h:14,
                    from include/linux/mm_types.h:5,
                    from include/linux/buildid.h:5,
                    from include/linux/module.h:14,
                    from drivers/crypto/caam/compat.h:10,
                    from drivers/crypto/caam/key_gen.c:8:
   drivers/crypto/caam/desc_constr.h: In function 'append_data.constprop':
>> include/linux/flex_array.h:9:33: warning: argument 2 null where non-null expected [-Wnonnull]
       9 | #define __underlying_memcpy     __builtin_memcpy
   include/linux/fortify-string.h:451:9: note: in expansion of macro '__underlying_memcpy'
     451 |         __underlying_##op(p, q, __fortify_size);                        \
         |         ^~~~~~~~~~~~~
   include/linux/fortify-string.h:496:26: note: in expansion of macro '__fortify_memcpy_chk'
     496 | #define memcpy(p, q, s)  __fortify_memcpy_chk(p, q, s,                  \
         |                          ^~~~~~~~~~~~~~~~~~~~
   drivers/crypto/caam/desc_constr.h:167:17: note: in expansion of macro 'memcpy'
     167 |                 memcpy(offset, data, len);
         |                 ^~~~~~
   include/linux/flex_array.h:9:33: note: in a call to built-in function '__builtin_memcpy'
       9 | #define __underlying_memcpy     __builtin_memcpy
   include/linux/fortify-string.h:451:9: note: in expansion of macro '__underlying_memcpy'
     451 |         __underlying_##op(p, q, __fortify_size);                        \
         |         ^~~~~~~~~~~~~
   include/linux/fortify-string.h:496:26: note: in expansion of macro '__fortify_memcpy_chk'
     496 | #define memcpy(p, q, s)  __fortify_memcpy_chk(p, q, s,                  \
         |                          ^~~~~~~~~~~~~~~~~~~~
   drivers/crypto/caam/desc_constr.h:167:17: note: in expansion of macro 'memcpy'
     167 |                 memcpy(offset, data, len);
         |                 ^~~~~~


vim +9 include/linux/flex_array.h

     6	
     7	/* Make sure we compose correctly with KASAN. */
     8	#ifndef __underlying_memcpy
   > 9	#define __underlying_memcpy     __builtin_memcpy
    10	#endif
    11	#ifndef __underlying_memset
    12	#define __underlying_memset	__builtin_memset
    13	#endif
    14
Andy Shevchenko Oct. 25, 2022, 8:57 a.m. UTC | #3
On Mon, Oct 24, 2022 at 10:20:57AM -0700, Kees Cook wrote:
> The compiler is not able to automatically perform bounds checking on
> structures that end in flexible arrays: __builtin_object_size() is
> compile-time only, and __builtin_dynamic_object_size() may not always
> be able to figure it out. Any possible run-time checks are currently
> short-circuited (or trigger false positives) because there isn't
> an obvious common way to figure out the bounds of such a structure.
> C has no way (yet[1]) to signify which struct member holds the number
> of allocated flexible array elements (like exists in other languages).
> 
> As a result, the kernel (and C projects generally) need to manually
> check the bounds, check the element size calculations, and perform sanity
> checking on all the associated variable types in between (e.g. 260
> cannot be stored in a u8). This is extremely fragile.
> 
> However, even if we could do all this through a magic memcpy(), the API
> itself doesn't provide meaningful feedback, which forces the kernel
> into an "all or nothing" approach: either do the copy or panic the
> system. Any failure conditions should be _detectable_, with API users
> able to gracefully recover.
> 
> To deal with these needs, create the first of a set of helper functions
> that do the work of memcpy() but perform the needed bounds checking
> based on the arguments given: flex_cpy().
> 
> This API will be expanded in the future to also include the common
> pattern of "allocate and copy": flex_dup(), "deserialize and copy":
> mem_to_flex(), and "deserialize, allocate, and copy": mem_to_flex_dup().
> 
> The concept of a "flexible array structure" is introduced, which is a
> struct that has both a trailing flexible array member _and_ an element
> count member. If a struct lacks the element count member, it's just a
> blob: there are no bounds associated with it.
> 
> The most common style of flexible array struct in the kernel is a
> "simple" one, where both the flex-array and element-count are present:
> 
>     struct flex_array_struct_example {
>         ...		/* arbitrary members */
>         u16 part_count;	/* count of elements stored in "parts" below. */
>         ...		/* arbitrary members */
>         u32 parts[];	/* flexible array with elements of type u32. */
>     };
> 
> Next are "encapsulating flexible array structs", which is just a struct
> that contains a flexible array struct as its final member:
> 
>     struct encapsulating_example {
>         ...		/* arbitrary members */
>         struct flex_array_struct_example fas;
>     };
> 
> There are also "split" flex array structs, which have the element-count
> member in a separate struct level than the flex-array member:
> 
>     struct split_example {
>         ...		/* arbitrary members */
>         u16 part_count;	/* count of elements stored in "parts" below. */
>         ...		/* arbitrary members */
>         struct blob_example {
>             ...		/* other blob members */
>             u32 parts[];/* flexible array with elements of type u32. */
>         } blob;
>     };
> 
> To have the helpers deal with these arbitrary layouts, the names of the
> flex-array and element-count members need to be specified with each use
> (since C lacks the array-with-length syntax[1] so the compiler cannot
> automatically determine them). However, for the "simple" (most common)
> case, we can get close to "automatic" by explicitly declaring common
> member aliases "__flex_array_elements", and "__flex_array_elements_count"
> respectively. The regular helpers use these members, but extended helpers
> exist to cover the other two code patterns.
> 
> For example, using the newly introduced flex_cpy():
> 
>     /* Flexible array struct with members identified. */
>     struct simple {
>         int mode;
>         DECLARE_FAS_COUNT(int, how_many);
>         unsigned long flags;
>         DECLARE_FAS_ARRAY(u32, value);
>     };
>     ...
> 
>     int do_simple(struct simple *src) {
>         struct simple *instance = NULL;
>         size_t bytes;
>         int rc;
> 
>         /* Allocate */
>         if (fas_bytes(src, &bytes))
>             return -E2BIG;
>         instance = kmalloc(bytes, GFP_KERNEL);
>         if (!instance)
>             return -ENOMEM;
>         instance->how_many = src->how_many;
>         /* Copy */
>         rc = flex_cpy(instance, src);
>         if (rc) {
>             kfree(instance);
>             return rc;
>         }
>         return 0;
>     }
> 
> If anything goes wrong, it returns a negative errno. Note that the
> "allocate" pattern above will be the basis of the future flex_dup()
> helper.
> 
> With these helpers the kernel can move away from many of the open-coded
> patterns of using memcpy() with a dynamically-sized destination buffer.
> 
> [1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1990.htm
> 
> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org>
> Cc: Keith Packard <keithp@keithp.com>
> Cc: Francis Laniel <laniel_francis@privacyrequired.com>
> Cc: Daniel Axtens <dja@axtens.net>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
> Cc: Guenter Roeck <linux@roeck-us.net>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Cc: Tadeusz Struk <tadeusz.struk@linaro.org>
> Signed-off-by: Kees Cook <keescook@chromium.org>
> Link: https://lore.kernel.org/r/20220504014440.3697851-3-keescook@chromium.org
> ---
>  include/linux/flex_array.h  | 325 ++++++++++++++++++++++++++++++++++++
>  include/linux/string.h      |   1 +
>  include/uapi/linux/stddef.h |  14 ++
>  3 files changed, 340 insertions(+)
>  create mode 100644 include/linux/flex_array.h
> 
> diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h
> new file mode 100644
> index 000000000000..ebdbf0e5f722
> --- /dev/null
> +++ b/include/linux/flex_array.h
> @@ -0,0 +1,325 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LINUX_FLEX_ARRAY_H_
> +#define _LINUX_FLEX_ARRAY_H_

You missed at least overflow.h and errno.h here.

> +#include <linux/string.h>

And this...

> +/* Make sure we compose correctly with KASAN. */
> +#ifndef __underlying_memcpy
> +#define __underlying_memcpy     __builtin_memcpy
> +#endif
> +#ifndef __underlying_memset
> +#define __underlying_memset	__builtin_memset
> +#endif
> +
> +/*
> + * A "flexible array structure" (FAS) is a structure which ends with a
> + * flexible array member (FAM) _and_ contains another member that represents
> + * how many array elements are present in the struct instance's flexible
> + * array member:
> + *
> + * struct flex_array_struct_example {
> + *	...		// arbitrary members
> + *	u16 part_count;	// count of elements stored in "parts" below.
> + *	...		// arbitrary members
> + *	u32 parts[];	// flexible array member containing u32 elements.
> + * };
> + *
> + * Without the "count of elements" member, a structure ending with a
> + * flexible array has no way to check its own size, and should be
> + * considered just a blob of memory that is length-checked through some
> + * other means. Kernel structures with flexible arrays should strive to
> + * always be true flexible array structures so that they can be operated
> + * on with the flex*()-family of helpers defined below.
> + *
> + * To use the normal flex*() helpers, prepare for future C syntax that
> + * would identify a flex array's count member directly, and keep kernel
> + * declarations minimized, a common declaration, bounded_flex_array(), is
> + * provided:
> + *
> + * struct flex_array_struct_example {
> + *	...			 // arbitrary members
> + *	bounded_flex_array(
> + *		u16, part_count, // count of elements stored in "parts" below.
> + *		u32, parts	 // flexible array with elements of type u32.
> + *	);
> + * );
> + *
> + * To handle the less common case when the count member cannot be made a
> + * neighbor of the flex array, either the extended flex*() helpers can be
> + * used, or the members can also be declared separately:
> + *
> + * struct flex_array_struct_example {
> + *	...		// position-sensitive members
> + *	// count of elements stored in "parts" below.
> + *	DECLARE_FAS_COUNT(u16, part_count);
> + *	..		// position-sensitive members
> + *	// flexible array with elements of type u32.
> + *	DECLARE_FAS_ARRAY(u32, parts);
> + * };
> + *
> + * The above two declaration styles will create an alias for part_count as
> + * __flex_array_elements_count and for parts as __flex_array_elements,
> + * which are used internally to avoid needing to repeat the member names
> + * as arguments to the normal flex*() helpers.
> + *
> + * The extended flex*() helpers are designed to be used in the less common
> + * situations when the member aliases are not available, especially in two
> + * flexible array struct layout situations: "encapsulated" and "split".
> + *
> + * An "encapsulated flexible array structure" is a structure that contains
> + * a full "flexible array structure" as its final struct member. These are
> + * used frequently when needing to pass around a copy of a flexible array
> + * structure, and track other things about the data outside of the scope of
> + * the flexible array structure itself:
> + *
> + * struct encapsulated_example {
> + *	...		// arbitrary members
> + *	struct flex_array_struct_example fas;
> + * };
> + *
> + * A "split flexible array structure" is like an encapsulated flexible
> + * array struct, but the element count member is at a different level,
> + * separate from the struct that contains the flexible array:
> + *
> + * struct blob_example {
> + *	...		// arbitrary members
> + *	u32 parts[];	// flexible array with elements of type u32.
> + * };
> + *
> + * struct split_example {
> + *	...		// arbitrary members
> + *	u16 part_count;	// count of elements stored in "parts" below.
> + *	...		// arbitrary members
> + *	struct blob_example blob;
> + * };
> + *
> + * In the case where the element count member is not stored in native
> + * CPU format, the extended helpers can be used to specify the to/from
> + * cpu helper needed to do the conversions.
> + *
> + * Examples of using flex_cpy():
> + *
> + *	struct simple {
> + *		u32 flags;
> + *		bounded_flex_array(
> + *			u32,	count,
> + *			u8,	data
> + *		);
> + *	};
> + *	struct hardware_defined {
> + *		DECLARE_FAS_COUNT(u32, count);
> + *		u32 state;
> + *		u32 flags;
> + *		DECLARE_FAS_ARRAY(u8, data);
> + *	};
> + *
> + *	int do_simple(struct simple *src) {
> + *		struct simple *ptr_simple = NULL;
> + *		struct hardware_defined *ptr_hw = NULL;
> + *
> + *		...allocation of ptr_simple happens...
> + *		ptr_simple->count = src->count;
> + *		rc = flex_cpy(ptr_simple, src);
> + *		...
> + *		...allocation of ptr_hw happens...
> + *		ptr_hw->count = src->count;
> + *		rc = flex_cpy(ptr_hw, src);
> + *		...
> + *	}
> + *
> + *	struct blob {
> + *		u32 flags;
> + *		u8 data[];
> + *	};
> + *	struct split {
> + *		be32 count;
> + *		struct blob blob;
> + *	};
> + *	struct split *ptr_split = NULL;
> + *
> + *	int do_split(struct split *src) {
> + *		struct split *ptr = NULL;
> + *
> + *		...allocation of ptr happens...
> + *		ptr->count = src->count;
> + *		rc = __flex_cpy(ptr, src, blob.data, count, be32_to_cpu);
> + *		...
> + *	}
> + *
> + */
> +
> +/* Wrappers around the UAPI macros. */
> +#define bounded_flex_array(COUNT_TYPE, COUNT_NAME, ARRAY_TYPE, ARRAY_NAME) \
> +	__DECLARE_FAS_COUNT(COUNT_TYPE, COUNT_NAME);	\
> +	__DECLARE_FAS_ARRAY(ARRAY_TYPE, ARRAY_NAME)
> +
> +#define DECLARE_FAS_COUNT(TYPE, NAME)			\
> +	__DECLARE_FAS_COUNT(TYPE, NAME)
> +
> +#define DECLARE_FAS_ARRAY(TYPE, NAME)			\
> +	__DECLARE_FAS_ARRAY(TYPE, NAME)
> +
> +/* All the helpers return negative on failure, and must be checked. */
> +static inline int __must_check __must_check_errno(int err)
> +{
> +	return err;
> +}
> +
> +#define __passthru(x)	(x)
> +
> +/**
> + * __fas_elements_bytes - Calculate potential size of the flexible
> + *			  array elements of a given flexible array
> + *			  structure
> + *
> + * @p: Pointer to flexible array structure.
> + * @flex_member: Member name of the flexible array elements.
> + * @count_member: Member name of the flexible array elements count.
> + * @elements_count: Count of proposed number of @p->__flex_array_elements
> + * @bytes: Pointer to variable to write calculation of total size in bytes.
> + *
> + * Returns: 0 on successful calculation, -ve on error.
> + *
> + * This performs the same calculation as flex_array_size(), except
> + * that the result is bounds checked and written to @bytes instead
> + * of being returned.
> + */
> +#define __fas_elements_bytes(p, flex_member, count_member,		\
> +			     elements_count, bytes)			\
> +__must_check_errno(({							\
> +	int __feb_err = -EINVAL;					\
> +	size_t __feb_elements_count = (elements_count);			\
> +	size_t __feb_elements_max =					\
> +		type_max(typeof((p)->count_member));			\
> +	if (__feb_elements_count > __feb_elements_max ||		\
> +	    check_mul_overflow(sizeof(*(p)->flex_member),		\
> +			       __feb_elements_count, bytes)) {		\
> +		*(bytes) = 0;						\
> +		__feb_err = -E2BIG;					\
> +	} else {							\
> +		__feb_err = 0;						\
> +	}								\
> +	__feb_err;							\
> +}))
> +
> +/**
> + * fas_elements_bytes - Calculate current size of the flexible array
> + *			elements of a given flexible array structure
> + *
> + * @p: Pointer to flexible array structure.
> + * @bytes: Pointer to variable to write calculation of total size in bytes.
> + *
> + * Returns: 0 on successful calculation, -ve on error.
> + *
> + * This performs the same calculation as flex_array_size(), except
> + * that the result is bounds checked and written to @bytes instead
> + * of being returned.
> + */
> +#define fas_elements_bytes(p, bytes)					\
> +	__fas_elements_bytes(p, __flex_array_elements,			\
> +			     __flex_array_elements_count,		\
> +			     (p)->__flex_array_elements_count, bytes)
> +
> +/**
> + * __fas_bytes - Calculate potential size of flexible array structure
> + *
> + * @p: Pointer to flexible array structure.
> + * @flex_member: Member name of the flexible array elements.
> + * @count_member: Member name of the flexible array elements count.
> + * @elements_count: Count of proposed number of @p->__flex_array_elements
> + * @bytes: Pointer to variable to write calculation of total size in bytes.
> + *
> + * Returns: 0 on successful calculation, -ve on error.
> + *
> + * This performs the same calculation as struct_size(), except
> + * that the result is bounds checked and written to @bytes instead
> + * of being returned.
> + */
> +#define __fas_bytes(p, flex_member, count_member, elements_count, bytes)\
> +__must_check_errno(({							\
> +	int __fasb_err;							\
> +	typeof(*bytes) __fasb_bytes;					\
> +									\
> +	if (__fas_elements_bytes(p, flex_member, count_member,		\
> +				 elements_count, &__fasb_bytes) ||	\
> +	    check_add_overflow(sizeof(*(p)), __fasb_bytes, bytes)) {	\
> +		*(bytes) = 0;						\
> +		__fasb_err = -E2BIG;					\
> +	} else {							\
> +		__fasb_err = 0;						\
> +	}								\
> +	__fasb_err;							\
> +}))
> +
> +/**
> + * fas_bytes - Calculate current size of flexible array structure
> + *
> + * @p: Pointer to flexible array structure.
> + * @bytes: Pointer to variable to write calculation of total size in bytes.
> + *
> + * This performs the same calculation as struct_size(), except
> + * that the result is bounds checked and written to @bytes instead
> + * of being returned, using the current size of the flexible array
> + * structure (via @p->__flexible_array_elements_count).
> + *
> + * Returns: 0 on successful calculation, -ve on error.
> + */
> +#define fas_bytes(p, bytes)						\
> +	__fas_bytes(p, __flex_array_elements,				\
> +		    __flex_array_elements_count,			\
> +		    (p)->__flex_array_elements_count, bytes)
> +
> +/**
> + * flex_cpy - Copy from one flexible array struct into another
> + *
> + * @dst: Destination pointer
> + * @src: Source pointer
> + *
> + * The full structure of @src will be copied to @dst, including all trailing
> + * flexible array elements. @dst->__flex_array_elements_count must be large
> + * enough to hold @src->__flex_array_elements_count. Any elements left over
> + * in @dst will be zero-wiped.
> + *
> + * Returns: 0 on successful calculation, -ve on error.
> + */
> +#define __flex_cpy(dst, src, elements_member, count_member, to_cpu)	\
> +__must_check_errno(({							\
> +	int __fc_err = -EINVAL;						\
> +	typeof(*(dst)) *__fc_dst = (dst);				\
> +	typeof(*(src)) *__fc_src = (src);				\
> +	size_t __fc_dst_bytes, __fc_src_bytes;				\
> +									\
> +	BUILD_BUG_ON(!__same_type(*(__fc_dst), *(__fc_src)));		\
> +									\
> +	do {								\
> +		if (__fas_bytes(__fc_dst,				\
> +				elements_member,			\
> +				count_member,				\
> +				to_cpu(__fc_dst->count_member),		\
> +				&__fc_dst_bytes) ||			\
> +		    __fas_bytes(__fc_src,				\
> +				elements_member,			\
> +				count_member,				\
> +				to_cpu(__fc_src->count_member),		\
> +				&__fc_src_bytes) ||			\
> +		    __fc_dst_bytes < __fc_src_bytes) {			\
> +			/* do we need to wipe dst here? */		\
> +			__fc_err = -E2BIG;				\
> +			break;						\
> +		}							\
> +		__underlying_memcpy(__fc_dst, __fc_src, __fc_src_bytes);\
> +		/* __flex_array_elements_count is included in memcpy */	\
> +		/* Wipe any now-unused trailing elements in @dst: */	\
> +		__underlying_memset((u8 *)__fc_dst + __fc_src_bytes, 0,	\
> +				 __fc_dst_bytes - __fc_src_bytes);	\
> +		__fc_err = 0;						\
> +	} while (0);							\
> +	__fc_err;							\
> +}))
> +
> +#define flex_cpy(dst, src)						\
> +	__flex_cpy(dst, src, __flex_array_elements,			\
> +		   __flex_array_elements_count, __passthru)
> +
> +#endif /* _LINUX_FLEX_ARRAY_H_ */
> diff --git a/include/linux/string.h b/include/linux/string.h
> index cf7607b32102..9277f9e2a432 100644
> --- a/include/linux/string.h
> +++ b/include/linux/string.h
> @@ -256,6 +256,7 @@ static inline const char *kbasename(const char *path)
>  #define unsafe_memcpy(dst, src, bytes, justification)		\
>  	memcpy(dst, src, bytes)
>  #endif
> +#include <linux/flex_array.h>

...seems to be a hidden mine for the future. Can we avoid dependency hell, please?

>  void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
>  		    int pad);
> diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h
> index 7837ba4fe728..e16afe1951d8 100644
> --- a/include/uapi/linux/stddef.h
> +++ b/include/uapi/linux/stddef.h
> @@ -44,4 +44,18 @@
>  		struct { } __empty_ ## NAME; \
>  		TYPE NAME[]; \
>  	}
> +
> +/* For use with flexible array structure helpers, in <linux/flex_array.h> */
> +#define __DECLARE_FAS_COUNT(TYPE, NAME)					\
> +	union {								\
> +		TYPE __flex_array_elements_count;			\
> +		TYPE NAME;						\
> +	}
> +
> +#define __DECLARE_FAS_ARRAY(TYPE, NAME)					\
> +	union {								\
> +		__DECLARE_FLEX_ARRAY(TYPE, __flex_array_elements);	\
> +		__DECLARE_FLEX_ARRAY(TYPE, NAME);			\
> +	}
> +
>  #endif
> -- 
> 2.34.1
>
Kees Cook Oct. 26, 2022, 8:33 p.m. UTC | #4
On Mon, Oct 24, 2022 at 11:35:03AM -0700, Keith Packard wrote:
> Kees Cook <keescook@chromium.org> writes:
> 
> > + * struct flex_array_struct_example {
> > + *	...			 // arbitrary members
> > + *	bounded_flex_array(
> > + *		u16, part_count, // count of elements stored in "parts" below.
> > + *		u32, parts	 // flexible array with elements of type u32.
> > + *	);
> > + * );
> 
> > + * struct flex_array_struct_example {
> > + *	...		// position-sensitive members
> > + *	// count of elements stored in "parts" below.
> > + *	DECLARE_FAS_COUNT(u16, part_count);
> > + *	..		// position-sensitive members
> > + *	// flexible array with elements of type u32.
> > + *	DECLARE_FAS_ARRAY(u32, parts);
> > + * };
> 
> I'm sure there's a good reason, but these two macros appear to be doing
> similar things and yet have very different naming conventions. Maybe:
> 
>         FAS_DECLARE_COUNT(type, name)
>         FAS_DECLARE_ARRAY(type, name)
>         FAS_DECLARE(size_type, size_name, array_type, array_name)

Well, the custom has been for individual things, call it "DECLARE_*",
and for groups, we went with lower-case macros (e.g. struct_group()).

> 
> > +/* For use with flexible array structure helpers, in <linux/flex_array.h> */
> > +#define __DECLARE_FAS_COUNT(TYPE, NAME)					\
> > +	union {								\
> > +		TYPE __flex_array_elements_count;			\
> > +		TYPE NAME;						\
> > +	}
> 
> How often could that second "public" member be 'const'? That would catch
> places which accidentally assign to this field.
> 
> For code which does want to write to this field, is it mostly trimming
> data from the end, or does it actually smash in arbitrary values? For
> the former case, would it be helpful to have a test to make sure the
> assigned size isn't larger than the real size (yeah, that would probably
> take an extra field holding the real size), or larger than the current size?

I don't think this'll work within arbitrary struct declarations, but it
would be nice. :)
diff mbox series

Patch

diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h
new file mode 100644
index 000000000000..ebdbf0e5f722
--- /dev/null
+++ b/include/linux/flex_array.h
@@ -0,0 +1,325 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_FLEX_ARRAY_H_
+#define _LINUX_FLEX_ARRAY_H_
+
+#include <linux/string.h>
+
+/* Make sure we compose correctly with KASAN. */
+#ifndef __underlying_memcpy
+#define __underlying_memcpy     __builtin_memcpy
+#endif
+#ifndef __underlying_memset
+#define __underlying_memset	__builtin_memset
+#endif
+
+/*
+ * A "flexible array structure" (FAS) is a structure which ends with a
+ * flexible array member (FAM) _and_ contains another member that represents
+ * how many array elements are present in the struct instance's flexible
+ * array member:
+ *
+ * struct flex_array_struct_example {
+ *	...		// arbitrary members
+ *	u16 part_count;	// count of elements stored in "parts" below.
+ *	...		// arbitrary members
+ *	u32 parts[];	// flexible array member containing u32 elements.
+ * };
+ *
+ * Without the "count of elements" member, a structure ending with a
+ * flexible array has no way to check its own size, and should be
+ * considered just a blob of memory that is length-checked through some
+ * other means. Kernel structures with flexible arrays should strive to
+ * always be true flexible array structures so that they can be operated
+ * on with the flex*()-family of helpers defined below.
+ *
+ * To use the normal flex*() helpers, prepare for future C syntax that
+ * would identify a flex array's count member directly, and keep kernel
+ * declarations minimized, a common declaration, bounded_flex_array(), is
+ * provided:
+ *
+ * struct flex_array_struct_example {
+ *	...			 // arbitrary members
+ *	bounded_flex_array(
+ *		u16, part_count, // count of elements stored in "parts" below.
+ *		u32, parts	 // flexible array with elements of type u32.
+ *	);
+ * );
+ *
+ * To handle the less common case when the count member cannot be made a
+ * neighbor of the flex array, either the extended flex*() helpers can be
+ * used, or the members can also be declared separately:
+ *
+ * struct flex_array_struct_example {
+ *	...		// position-sensitive members
+ *	// count of elements stored in "parts" below.
+ *	DECLARE_FAS_COUNT(u16, part_count);
+ *	..		// position-sensitive members
+ *	// flexible array with elements of type u32.
+ *	DECLARE_FAS_ARRAY(u32, parts);
+ * };
+ *
+ * The above two declaration styles will create an alias for part_count as
+ * __flex_array_elements_count and for parts as __flex_array_elements,
+ * which are used internally to avoid needing to repeat the member names
+ * as arguments to the normal flex*() helpers.
+ *
+ * The extended flex*() helpers are designed to be used in the less common
+ * situations when the member aliases are not available, especially in two
+ * flexible array struct layout situations: "encapsulated" and "split".
+ *
+ * An "encapsulated flexible array structure" is a structure that contains
+ * a full "flexible array structure" as its final struct member. These are
+ * used frequently when needing to pass around a copy of a flexible array
+ * structure, and track other things about the data outside of the scope of
+ * the flexible array structure itself:
+ *
+ * struct encapsulated_example {
+ *	...		// arbitrary members
+ *	struct flex_array_struct_example fas;
+ * };
+ *
+ * A "split flexible array structure" is like an encapsulated flexible
+ * array struct, but the element count member is at a different level,
+ * separate from the struct that contains the flexible array:
+ *
+ * struct blob_example {
+ *	...		// arbitrary members
+ *	u32 parts[];	// flexible array with elements of type u32.
+ * };
+ *
+ * struct split_example {
+ *	...		// arbitrary members
+ *	u16 part_count;	// count of elements stored in "parts" below.
+ *	...		// arbitrary members
+ *	struct blob_example blob;
+ * };
+ *
+ * In the case where the element count member is not stored in native
+ * CPU format, the extended helpers can be used to specify the to/from
+ * cpu helper needed to do the conversions.
+ *
+ * Examples of using flex_cpy():
+ *
+ *	struct simple {
+ *		u32 flags;
+ *		bounded_flex_array(
+ *			u32,	count,
+ *			u8,	data
+ *		);
+ *	};
+ *	struct hardware_defined {
+ *		DECLARE_FAS_COUNT(u32, count);
+ *		u32 state;
+ *		u32 flags;
+ *		DECLARE_FAS_ARRAY(u8, data);
+ *	};
+ *
+ *	int do_simple(struct simple *src) {
+ *		struct simple *ptr_simple = NULL;
+ *		struct hardware_defined *ptr_hw = NULL;
+ *
+ *		...allocation of ptr_simple happens...
+ *		ptr_simple->count = src->count;
+ *		rc = flex_cpy(ptr_simple, src);
+ *		...
+ *		...allocation of ptr_hw happens...
+ *		ptr_hw->count = src->count;
+ *		rc = flex_cpy(ptr_hw, src);
+ *		...
+ *	}
+ *
+ *	struct blob {
+ *		u32 flags;
+ *		u8 data[];
+ *	};
+ *	struct split {
+ *		be32 count;
+ *		struct blob blob;
+ *	};
+ *	struct split *ptr_split = NULL;
+ *
+ *	int do_split(struct split *src) {
+ *		struct split *ptr = NULL;
+ *
+ *		...allocation of ptr happens...
+ *		ptr->count = src->count;
+ *		rc = __flex_cpy(ptr, src, blob.data, count, be32_to_cpu);
+ *		...
+ *	}
+ *
+ */
+
+/* Wrappers around the UAPI macros. */
+#define bounded_flex_array(COUNT_TYPE, COUNT_NAME, ARRAY_TYPE, ARRAY_NAME) \
+	__DECLARE_FAS_COUNT(COUNT_TYPE, COUNT_NAME);	\
+	__DECLARE_FAS_ARRAY(ARRAY_TYPE, ARRAY_NAME)
+
+#define DECLARE_FAS_COUNT(TYPE, NAME)			\
+	__DECLARE_FAS_COUNT(TYPE, NAME)
+
+#define DECLARE_FAS_ARRAY(TYPE, NAME)			\
+	__DECLARE_FAS_ARRAY(TYPE, NAME)
+
+/* All the helpers return negative on failure, and must be checked. */
+static inline int __must_check __must_check_errno(int err)
+{
+	return err;
+}
+
+#define __passthru(x)	(x)
+
+/**
+ * __fas_elements_bytes - Calculate potential size of the flexible
+ *			  array elements of a given flexible array
+ *			  structure
+ *
+ * @p: Pointer to flexible array structure.
+ * @flex_member: Member name of the flexible array elements.
+ * @count_member: Member name of the flexible array elements count.
+ * @elements_count: Count of proposed number of @p->__flex_array_elements
+ * @bytes: Pointer to variable to write calculation of total size in bytes.
+ *
+ * Returns: 0 on successful calculation, -ve on error.
+ *
+ * This performs the same calculation as flex_array_size(), except
+ * that the result is bounds checked and written to @bytes instead
+ * of being returned.
+ */
+#define __fas_elements_bytes(p, flex_member, count_member,		\
+			     elements_count, bytes)			\
+__must_check_errno(({							\
+	int __feb_err = -EINVAL;					\
+	size_t __feb_elements_count = (elements_count);			\
+	size_t __feb_elements_max =					\
+		type_max(typeof((p)->count_member));			\
+	if (__feb_elements_count > __feb_elements_max ||		\
+	    check_mul_overflow(sizeof(*(p)->flex_member),		\
+			       __feb_elements_count, bytes)) {		\
+		*(bytes) = 0;						\
+		__feb_err = -E2BIG;					\
+	} else {							\
+		__feb_err = 0;						\
+	}								\
+	__feb_err;							\
+}))
+
+/**
+ * fas_elements_bytes - Calculate current size of the flexible array
+ *			elements of a given flexible array structure
+ *
+ * @p: Pointer to flexible array structure.
+ * @bytes: Pointer to variable to write calculation of total size in bytes.
+ *
+ * Returns: 0 on successful calculation, -ve on error.
+ *
+ * This performs the same calculation as flex_array_size(), except
+ * that the result is bounds checked and written to @bytes instead
+ * of being returned.
+ */
+#define fas_elements_bytes(p, bytes)					\
+	__fas_elements_bytes(p, __flex_array_elements,			\
+			     __flex_array_elements_count,		\
+			     (p)->__flex_array_elements_count, bytes)
+
+/**
+ * __fas_bytes - Calculate potential size of flexible array structure
+ *
+ * @p: Pointer to flexible array structure.
+ * @flex_member: Member name of the flexible array elements.
+ * @count_member: Member name of the flexible array elements count.
+ * @elements_count: Count of proposed number of @p->__flex_array_elements
+ * @bytes: Pointer to variable to write calculation of total size in bytes.
+ *
+ * Returns: 0 on successful calculation, -ve on error.
+ *
+ * This performs the same calculation as struct_size(), except
+ * that the result is bounds checked and written to @bytes instead
+ * of being returned.
+ */
+#define __fas_bytes(p, flex_member, count_member, elements_count, bytes)\
+__must_check_errno(({							\
+	int __fasb_err;							\
+	typeof(*bytes) __fasb_bytes;					\
+									\
+	if (__fas_elements_bytes(p, flex_member, count_member,		\
+				 elements_count, &__fasb_bytes) ||	\
+	    check_add_overflow(sizeof(*(p)), __fasb_bytes, bytes)) {	\
+		*(bytes) = 0;						\
+		__fasb_err = -E2BIG;					\
+	} else {							\
+		__fasb_err = 0;						\
+	}								\
+	__fasb_err;							\
+}))
+
+/**
+ * fas_bytes - Calculate current size of flexible array structure
+ *
+ * @p: Pointer to flexible array structure.
+ * @bytes: Pointer to variable to write calculation of total size in bytes.
+ *
+ * This performs the same calculation as struct_size(), except
+ * that the result is bounds checked and written to @bytes instead
+ * of being returned, using the current size of the flexible array
+ * structure (via @p->__flexible_array_elements_count).
+ *
+ * Returns: 0 on successful calculation, -ve on error.
+ */
+#define fas_bytes(p, bytes)						\
+	__fas_bytes(p, __flex_array_elements,				\
+		    __flex_array_elements_count,			\
+		    (p)->__flex_array_elements_count, bytes)
+
+/**
+ * flex_cpy - Copy from one flexible array struct into another
+ *
+ * @dst: Destination pointer
+ * @src: Source pointer
+ *
+ * The full structure of @src will be copied to @dst, including all trailing
+ * flexible array elements. @dst->__flex_array_elements_count must be large
+ * enough to hold @src->__flex_array_elements_count. Any elements left over
+ * in @dst will be zero-wiped.
+ *
+ * Returns: 0 on successful calculation, -ve on error.
+ */
+#define __flex_cpy(dst, src, elements_member, count_member, to_cpu)	\
+__must_check_errno(({							\
+	int __fc_err = -EINVAL;						\
+	typeof(*(dst)) *__fc_dst = (dst);				\
+	typeof(*(src)) *__fc_src = (src);				\
+	size_t __fc_dst_bytes, __fc_src_bytes;				\
+									\
+	BUILD_BUG_ON(!__same_type(*(__fc_dst), *(__fc_src)));		\
+									\
+	do {								\
+		if (__fas_bytes(__fc_dst,				\
+				elements_member,			\
+				count_member,				\
+				to_cpu(__fc_dst->count_member),		\
+				&__fc_dst_bytes) ||			\
+		    __fas_bytes(__fc_src,				\
+				elements_member,			\
+				count_member,				\
+				to_cpu(__fc_src->count_member),		\
+				&__fc_src_bytes) ||			\
+		    __fc_dst_bytes < __fc_src_bytes) {			\
+			/* do we need to wipe dst here? */		\
+			__fc_err = -E2BIG;				\
+			break;						\
+		}							\
+		__underlying_memcpy(__fc_dst, __fc_src, __fc_src_bytes);\
+		/* __flex_array_elements_count is included in memcpy */	\
+		/* Wipe any now-unused trailing elements in @dst: */	\
+		__underlying_memset((u8 *)__fc_dst + __fc_src_bytes, 0,	\
+				 __fc_dst_bytes - __fc_src_bytes);	\
+		__fc_err = 0;						\
+	} while (0);							\
+	__fc_err;							\
+}))
+
+#define flex_cpy(dst, src)						\
+	__flex_cpy(dst, src, __flex_array_elements,			\
+		   __flex_array_elements_count, __passthru)
+
+#endif /* _LINUX_FLEX_ARRAY_H_ */
diff --git a/include/linux/string.h b/include/linux/string.h
index cf7607b32102..9277f9e2a432 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -256,6 +256,7 @@  static inline const char *kbasename(const char *path)
 #define unsafe_memcpy(dst, src, bytes, justification)		\
 	memcpy(dst, src, bytes)
 #endif
+#include <linux/flex_array.h>
 
 void memcpy_and_pad(void *dest, size_t dest_len, const void *src, size_t count,
 		    int pad);
diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h
index 7837ba4fe728..e16afe1951d8 100644
--- a/include/uapi/linux/stddef.h
+++ b/include/uapi/linux/stddef.h
@@ -44,4 +44,18 @@ 
 		struct { } __empty_ ## NAME; \
 		TYPE NAME[]; \
 	}
+
+/* For use with flexible array structure helpers, in <linux/flex_array.h> */
+#define __DECLARE_FAS_COUNT(TYPE, NAME)					\
+	union {								\
+		TYPE __flex_array_elements_count;			\
+		TYPE NAME;						\
+	}
+
+#define __DECLARE_FAS_ARRAY(TYPE, NAME)					\
+	union {								\
+		__DECLARE_FLEX_ARRAY(TYPE, __flex_array_elements);	\
+		__DECLARE_FLEX_ARRAY(TYPE, NAME);			\
+	}
+
 #endif