linux-hardening.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] libxfs: Redefine 1-element arrays as flexible arrays
@ 2023-07-11 22:20 Kees Cook
  2023-07-12  5:37 ` Darrick J. Wong
  0 siblings, 1 reply; 6+ messages in thread
From: Kees Cook @ 2023-07-11 22:20 UTC (permalink / raw)
  To: Carlos Maiolino
  Cc: Kees Cook, Darrick J. Wong, Dave Chinner, Jeff Layton,
	Eric Biggers, Gustavo A. R. Silva, linux-xfs, linux-hardening

To allow for code bases that include libxfs (e.g. the Linux kernel) and
build with strict flexible array handling (-fstrict-flex-arrays=3),
FORTIFY_SOURCE, and/or UBSAN bounds checking, redefine the remaining
1-element trailing arrays as true flexible arrays, but without changing
their structure sizes. This is done via a union to retain a single element
(named "legacy_padding"). As not all distro headers may yet have the
UAPI stddef.h __DECLARE_FLEX_ARRAY macro, include it explicitly in
platform_defs.h.in.

Addresses the warnings being seen under Linux:
https://lore.kernel.org/all/20230710170243.GF11456@frogsfrogsfrogs

Signed-off-by: Kees Cook <keescook@chromium.org>
---
 include/platform_defs.h.in | 18 ++++++++++++++++++
 libxfs/xfs_da_format.h     | 38 ++++++++++++++++++++++++++++----------
 libxfs/xfs_fs.h            | 12 ++++++++++--
 3 files changed, 56 insertions(+), 12 deletions(-)

diff --git a/include/platform_defs.h.in b/include/platform_defs.h.in
index 315ad77cfb78..a55965ce050d 100644
--- a/include/platform_defs.h.in
+++ b/include/platform_defs.h.in
@@ -134,6 +134,24 @@ static inline size_t __ab_c_size(size_t a, size_t b, size_t c)
 #    define fallthrough                    do {} while (0)  /* fallthrough */
 #endif
 
+#ifndef __DECLARE_FLEX_ARRAY
+/**
+ * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @type: The type of each flexible array element
+ * @name: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define __DECLARE_FLEX_ARRAY(type, name)	\
+	struct { \
+		struct { } __empty_ ## name; \
+		type name[]; \
+	}
+#endif
+
 /* Only needed for the kernel. */
 #define __init
 
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 25e2841084e1..4af92f16d5cd 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -580,18 +580,23 @@ xfs_dir2_block_leaf_p(struct xfs_dir2_block_tail *btp)
 /*
  * Entries are packed toward the top as tight as possible.
  */
+struct xfs_attr_sf_entry {
+	uint8_t namelen;	/* actual length of name (no NULL) */
+	uint8_t valuelen;	/* actual length of value (no NULL) */
+	uint8_t flags;		/* flags bits (see xfs_attr_leaf.h) */
+	uint8_t nameval[];	/* name & value bytes concatenated */
+};
+
 struct xfs_attr_shortform {
 	struct xfs_attr_sf_hdr {	/* constant-structure header block */
 		__be16	totsize;	/* total bytes in shortform list */
 		__u8	count;	/* count of active entries */
 		__u8	padding;
 	} hdr;
-	struct xfs_attr_sf_entry {
-		uint8_t namelen;	/* actual length of name (no NULL) */
-		uint8_t valuelen;	/* actual length of value (no NULL) */
-		uint8_t flags;	/* flags bits (see xfs_attr_leaf.h) */
-		uint8_t nameval[];	/* name & value bytes concatenated */
-	} list[1];			/* variable sized array */
+	union {
+		struct xfs_attr_sf_entry legacy_padding;
+		__DECLARE_FLEX_ARRAY(struct xfs_attr_sf_entry, list);
+	};
 };
 
 typedef struct xfs_attr_leaf_map {	/* RLE map of free bytes */
@@ -620,19 +625,29 @@ typedef struct xfs_attr_leaf_entry {	/* sorted on key, not name */
 typedef struct xfs_attr_leaf_name_local {
 	__be16	valuelen;		/* number of bytes in value */
 	__u8	namelen;		/* length of name bytes */
-	__u8	nameval[1];		/* name/value bytes */
+	union {
+		__u8	legacy_padding;
+		__DECLARE_FLEX_ARRAY(__u8, nameval);	/* name/value bytes */
+	};
 } xfs_attr_leaf_name_local_t;
 
 typedef struct xfs_attr_leaf_name_remote {
 	__be32	valueblk;		/* block number of value bytes */
 	__be32	valuelen;		/* number of bytes in value */
 	__u8	namelen;		/* length of name bytes */
-	__u8	name[1];		/* name bytes */
+	union {
+		__u8	legacy_padding;
+		__DECLARE_FLEX_ARRAY(__u8, name);	/* name bytes */
+	};
 } xfs_attr_leaf_name_remote_t;
 
 typedef struct xfs_attr_leafblock {
 	xfs_attr_leaf_hdr_t	hdr;	/* constant-structure header block */
-	xfs_attr_leaf_entry_t	entries[1];	/* sorted on key, not name */
+	union {
+		xfs_attr_leaf_entry_t	legacy_padding;
+		/* sorted on key, not name */
+		__DECLARE_FLEX_ARRAY(xfs_attr_leaf_entry_t, entries);
+	};
 	/*
 	 * The rest of the block contains the following structures after the
 	 * leaf entries, growing from the bottom up. The variables are never
@@ -664,7 +679,10 @@ struct xfs_attr3_leaf_hdr {
 
 struct xfs_attr3_leafblock {
 	struct xfs_attr3_leaf_hdr	hdr;
-	struct xfs_attr_leaf_entry	entries[1];
+	union {
+		struct xfs_attr_leaf_entry	legacy_padding;
+		__DECLARE_FLEX_ARRAY(struct xfs_attr_leaf_entry, entries);
+	};
 
 	/*
 	 * The rest of the block contains the following structures after the
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 1cfd5bc6520a..d6ec66ef0194 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -590,12 +590,20 @@ typedef struct xfs_attrlist_cursor {
 struct xfs_attrlist {
 	__s32	al_count;	/* number of entries in attrlist */
 	__s32	al_more;	/* T/F: more attrs (do call again) */
-	__s32	al_offset[1];	/* byte offsets of attrs [var-sized] */
+	union {
+		__s32	legacy_padding;
+		/* byte offsets of attrs [var-sized] */
+		__DECLARE_FLEX_ARRAY(__s32, al_offset);
+	};
 };
 
 struct xfs_attrlist_ent {	/* data from attr_list() */
 	__u32	a_valuelen;	/* number bytes in value of attr */
-	char	a_name[1];	/* attr name (NULL terminated) */
+	union {
+		char	legacy_padding;
+		/* attr name (NULL terminated) */
+		__DECLARE_FLEX_ARRAY(char, a_name);
+	};
 };
 
 typedef struct xfs_fsop_attrlist_handlereq {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] libxfs: Redefine 1-element arrays as flexible arrays
  2023-07-11 22:20 [PATCH] libxfs: Redefine 1-element arrays as flexible arrays Kees Cook
@ 2023-07-12  5:37 ` Darrick J. Wong
  2023-07-12 13:09   ` Christoph Hellwig
  2023-07-12 18:45   ` Kees Cook
  0 siblings, 2 replies; 6+ messages in thread
From: Darrick J. Wong @ 2023-07-12  5:37 UTC (permalink / raw)
  To: Kees Cook
  Cc: Carlos Maiolino, Dave Chinner, Jeff Layton, Eric Biggers,
	Gustavo A. R. Silva, linux-xfs, linux-hardening

On Tue, Jul 11, 2023 at 03:20:28PM -0700, Kees Cook wrote:
> To allow for code bases that include libxfs (e.g. the Linux kernel) and
> build with strict flexible array handling (-fstrict-flex-arrays=3),
> FORTIFY_SOURCE, and/or UBSAN bounds checking, redefine the remaining
> 1-element trailing arrays as true flexible arrays, but without changing
> their structure sizes. This is done via a union to retain a single element
> (named "legacy_padding"). As not all distro headers may yet have the
> UAPI stddef.h __DECLARE_FLEX_ARRAY macro, include it explicitly in
> platform_defs.h.in.
> 
> Addresses the warnings being seen under Linux:
> https://lore.kernel.org/all/20230710170243.GF11456@frogsfrogsfrogs
> 
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  include/platform_defs.h.in | 18 ++++++++++++++++++
>  libxfs/xfs_da_format.h     | 38 ++++++++++++++++++++++++++++----------
>  libxfs/xfs_fs.h            | 12 ++++++++++--
>  3 files changed, 56 insertions(+), 12 deletions(-)
> 
> diff --git a/include/platform_defs.h.in b/include/platform_defs.h.in
> index 315ad77cfb78..a55965ce050d 100644
> --- a/include/platform_defs.h.in
> +++ b/include/platform_defs.h.in
> @@ -134,6 +134,24 @@ static inline size_t __ab_c_size(size_t a, size_t b, size_t c)
>  #    define fallthrough                    do {} while (0)  /* fallthrough */
>  #endif
>  
> +#ifndef __DECLARE_FLEX_ARRAY
> +/**
> + * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
> + *
> + * @type: The type of each flexible array element
> + * @name: The name of the flexible array member
> + *
> + * In order to have a flexible array member in a union or alone in a
> + * struct, it needs to be wrapped in an anonymous struct with at least 1
> + * named member, but that member can be empty.
> + */
> +#define __DECLARE_FLEX_ARRAY(type, name)	\
> +	struct { \
> +		struct { } __empty_ ## name; \
> +		type name[]; \
> +	}
> +#endif

Hmm.  This is tempting, since the struct sizes stay the same, which
avoids all the potential sizeof breakage problems and whatnot.  I think
the downside of doing it this way is that xfs_fs.h ships in xfslibs-dev
but platform_defs.h does not, which means that random user programs can
pick up a *user* of __DECLARE_FLEX_ARRAY from /usr/include/xfs/xfs_fs.h.

I guess this could be a problem for anyone who might (a) compile against
xfs_fs.h from a newer xfslibs-dev package on a system with (b) old
kernel headers that don't define __DECLARE_FLEX_ARRAY.

More on this at the bottom.

> +
>  /* Only needed for the kernel. */
>  #define __init
>  
> diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
> index 25e2841084e1..4af92f16d5cd 100644
> --- a/libxfs/xfs_da_format.h
> +++ b/libxfs/xfs_da_format.h
> @@ -580,18 +580,23 @@ xfs_dir2_block_leaf_p(struct xfs_dir2_block_tail *btp)
>  /*
>   * Entries are packed toward the top as tight as possible.
>   */
> +struct xfs_attr_sf_entry {
> +	uint8_t namelen;	/* actual length of name (no NULL) */
> +	uint8_t valuelen;	/* actual length of value (no NULL) */
> +	uint8_t flags;		/* flags bits (see xfs_attr_leaf.h) */
> +	uint8_t nameval[];	/* name & value bytes concatenated */
> +};

(Yeah, these ought to be broken out...)

> +
>  struct xfs_attr_shortform {
>  	struct xfs_attr_sf_hdr {	/* constant-structure header block */
>  		__be16	totsize;	/* total bytes in shortform list */
>  		__u8	count;	/* count of active entries */
>  		__u8	padding;
>  	} hdr;
> -	struct xfs_attr_sf_entry {
> -		uint8_t namelen;	/* actual length of name (no NULL) */
> -		uint8_t valuelen;	/* actual length of value (no NULL) */
> -		uint8_t flags;	/* flags bits (see xfs_attr_leaf.h) */
> -		uint8_t nameval[];	/* name & value bytes concatenated */
> -	} list[1];			/* variable sized array */
> +	union {
> +		struct xfs_attr_sf_entry legacy_padding;
> +		__DECLARE_FLEX_ARRAY(struct xfs_attr_sf_entry, list);
> +	};
>  };
>  
>  typedef struct xfs_attr_leaf_map {	/* RLE map of free bytes */
> @@ -620,19 +625,29 @@ typedef struct xfs_attr_leaf_entry {	/* sorted on key, not name */
>  typedef struct xfs_attr_leaf_name_local {
>  	__be16	valuelen;		/* number of bytes in value */
>  	__u8	namelen;		/* length of name bytes */
> -	__u8	nameval[1];		/* name/value bytes */
> +	union {
> +		__u8	legacy_padding;
> +		__DECLARE_FLEX_ARRAY(__u8, nameval);	/* name/value bytes */
> +	};
>  } xfs_attr_leaf_name_local_t;
>  
>  typedef struct xfs_attr_leaf_name_remote {
>  	__be32	valueblk;		/* block number of value bytes */
>  	__be32	valuelen;		/* number of bytes in value */
>  	__u8	namelen;		/* length of name bytes */
> -	__u8	name[1];		/* name bytes */
> +	union {
> +		__u8	legacy_padding;
> +		__DECLARE_FLEX_ARRAY(__u8, name);	/* name bytes */
> +	};
>  } xfs_attr_leaf_name_remote_t;
>  
>  typedef struct xfs_attr_leafblock {
>  	xfs_attr_leaf_hdr_t	hdr;	/* constant-structure header block */
> -	xfs_attr_leaf_entry_t	entries[1];	/* sorted on key, not name */
> +	union {
> +		xfs_attr_leaf_entry_t	legacy_padding;
> +		/* sorted on key, not name */
> +		__DECLARE_FLEX_ARRAY(xfs_attr_leaf_entry_t, entries);
> +	};
>  	/*
>  	 * The rest of the block contains the following structures after the
>  	 * leaf entries, growing from the bottom up. The variables are never
> @@ -664,7 +679,10 @@ struct xfs_attr3_leaf_hdr {
>  
>  struct xfs_attr3_leafblock {
>  	struct xfs_attr3_leaf_hdr	hdr;
> -	struct xfs_attr_leaf_entry	entries[1];
> +	union {
> +		struct xfs_attr_leaf_entry	legacy_padding;
> +		__DECLARE_FLEX_ARRAY(struct xfs_attr_leaf_entry, entries);
> +	};
>  
>  	/*
>  	 * The rest of the block contains the following structures after the
> diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
> index 1cfd5bc6520a..d6ec66ef0194 100644
> --- a/libxfs/xfs_fs.h
> +++ b/libxfs/xfs_fs.h
> @@ -590,12 +590,20 @@ typedef struct xfs_attrlist_cursor {
>  struct xfs_attrlist {
>  	__s32	al_count;	/* number of entries in attrlist */
>  	__s32	al_more;	/* T/F: more attrs (do call again) */
> -	__s32	al_offset[1];	/* byte offsets of attrs [var-sized] */
> +	union {
> +		__s32	legacy_padding;
> +		/* byte offsets of attrs [var-sized] */
> +		__DECLARE_FLEX_ARRAY(__s32, al_offset);
> +	};
>  };
>  
>  struct xfs_attrlist_ent {	/* data from attr_list() */
>  	__u32	a_valuelen;	/* number bytes in value of attr */
> -	char	a_name[1];	/* attr name (NULL terminated) */
> +	union {
> +		char	legacy_padding;
> +		/* attr name (NULL terminated) */
> +		__DECLARE_FLEX_ARRAY(char, a_name);
> +	};
>  };
>  
>  typedef struct xfs_fsop_attrlist_handlereq {
> -- 
> 2.34.1

Here's my version, where I go for a straight a[1] -> a[] conversion and
let downstream attrlist ioctl users eat their lumps.  What do you think
of the excess-documentation approach?

--D

diff --git a/db/metadump.c b/db/metadump.c
index d9a616a9..3545124f 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -1475,7 +1475,7 @@ process_attr_block(
 			nlen = local->namelen;
 			vlen = be16_to_cpu(local->valuelen);
 			zlen = xfs_attr_leaf_entsize_local(nlen, vlen) -
-				(sizeof(xfs_attr_leaf_name_local_t) - 1 +
+				(offsetof(struct xfs_attr_leaf_name_local, nameval) +
 				 nlen + vlen);
 			if (zero_stale_data)
 				memset(&local->nameval[nlen + vlen], 0, zlen);
@@ -1497,7 +1497,7 @@ process_attr_block(
 			/* zero from end of name[] to next name start */
 			nlen = remote->namelen;
 			zlen = xfs_attr_leaf_entsize_remote(nlen) -
-				(sizeof(xfs_attr_leaf_name_remote_t) - 1 +
+				(offsetof(struct xfs_attr_leaf_name_remote, name) +
 				 nlen);
 			if (zero_stale_data)
 				memset(&remote->name[nlen], 0, zlen);
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 25e28410..17a6d8be 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -586,12 +586,14 @@ struct xfs_attr_shortform {
 		__u8	count;	/* count of active entries */
 		__u8	padding;
 	} hdr;
+
+	/* In Linux 6.5 this flex array was changed from list[1] to list[]. */
 	struct xfs_attr_sf_entry {
 		uint8_t namelen;	/* actual length of name (no NULL) */
 		uint8_t valuelen;	/* actual length of value (no NULL) */
 		uint8_t flags;	/* flags bits (see xfs_attr_leaf.h) */
 		uint8_t nameval[];	/* name & value bytes concatenated */
-	} list[1];			/* variable sized array */
+	} list[];			/* variable sized array */
 };
 
 typedef struct xfs_attr_leaf_map {	/* RLE map of free bytes */
@@ -620,19 +622,29 @@ typedef struct xfs_attr_leaf_entry {	/* sorted on key, not name */
 typedef struct xfs_attr_leaf_name_local {
 	__be16	valuelen;		/* number of bytes in value */
 	__u8	namelen;		/* length of name bytes */
-	__u8	nameval[1];		/* name/value bytes */
+	/*
+	 * In Linux 6.5 this flex array was converted from nameval[1] to
+	 * nameval[].  Be very careful here about extra padding at the end;
+	 * see xfs_attr_leaf_entsize_local() for details.
+	 */
+	__u8	nameval[];		/* name/value bytes */
 } xfs_attr_leaf_name_local_t;
 
 typedef struct xfs_attr_leaf_name_remote {
 	__be32	valueblk;		/* block number of value bytes */
 	__be32	valuelen;		/* number of bytes in value */
 	__u8	namelen;		/* length of name bytes */
-	__u8	name[1];		/* name bytes */
+	/*
+	 * In Linux 6.5 this flex array was converted from name[1] to name[].
+	 * Be very careful here about extra padding at the end; see
+	 * xfs_attr_leaf_entsize_remote() for details.
+	 */
+	__u8	name[];			/* name bytes */
 } xfs_attr_leaf_name_remote_t;
 
 typedef struct xfs_attr_leafblock {
 	xfs_attr_leaf_hdr_t	hdr;	/* constant-structure header block */
-	xfs_attr_leaf_entry_t	entries[1];	/* sorted on key, not name */
+	xfs_attr_leaf_entry_t	entries[];	/* sorted on key, not name */
 	/*
 	 * The rest of the block contains the following structures after the
 	 * leaf entries, growing from the bottom up. The variables are never
@@ -641,6 +653,9 @@ typedef struct xfs_attr_leafblock {
 	 *
 	 * xfs_attr_leaf_name_local_t namelist;
 	 * xfs_attr_leaf_name_remote_t valuelist;
+	 *
+	 * In Linux 6.5 this flex array was converted from entries[1] to
+	 * entries[].
 	 */
 } xfs_attr_leafblock_t;
 
@@ -664,7 +679,7 @@ struct xfs_attr3_leaf_hdr {
 
 struct xfs_attr3_leafblock {
 	struct xfs_attr3_leaf_hdr	hdr;
-	struct xfs_attr_leaf_entry	entries[1];
+	struct xfs_attr_leaf_entry	entries[];
 
 	/*
 	 * The rest of the block contains the following structures after the
@@ -673,6 +688,9 @@ struct xfs_attr3_leafblock {
 	 *
 	 * struct xfs_attr_leaf_name_local
 	 * struct xfs_attr_leaf_name_remote
+	 *
+	 * In Linux 6.5 this flex array was converted from entries[1] to
+	 * entries[].
 	 */
 };
 
@@ -747,14 +765,61 @@ xfs_attr3_leaf_name_local(xfs_attr_leafblock_t *leafp, int idx)
  */
 static inline int xfs_attr_leaf_entsize_remote(int nlen)
 {
-	return round_up(sizeof(struct xfs_attr_leaf_name_remote) - 1 +
-			nlen, XFS_ATTR_LEAF_NAME_ALIGN);
+	/*
+	 * Prior to Linux 6.5, struct xfs_attr_leaf_name_remote ended with
+	 * name[1], which was used as a flexarray.  The layout of this struct
+	 * is 9 bytes of fixed-length fields followed by a __u8 flex array at
+	 * offset 9.
+	 *
+	 * On most architectures, struct xfs_attr_leaf_name_remote had two
+	 * bytes of implicit padding at the end of the struct to make the
+	 * struct length 12.  After converting name[1] to name[], there are
+	 * three implicit padding bytes and the struct size remains 12.
+	 * However, there are compiler configurations that do not add implicit
+	 * padding at all (m68k) and have been broken for years.
+	 *
+	 * This entsize computation historically added (the xattr name length)
+	 * to (the padded struct length - 1) and rounded that sum up to the
+	 * nearest multiple of 4 (NAME_ALIGN).  IOWs, round_up(11 + nlen, 4).
+	 * This is encoded in the ondisk format, so we cannot change this.
+	 *
+	 * Compute the entsize from offsetof of the flexarray and manually
+	 * adding bytes for the implicit padding.
+	 */
+	const size_t remotesize =
+			offsetof(struct xfs_attr_leaf_name_remote, name) + 2;
+
+	return round_up(remotesize + nlen, XFS_ATTR_LEAF_NAME_ALIGN);
 }
 
 static inline int xfs_attr_leaf_entsize_local(int nlen, int vlen)
 {
-	return round_up(sizeof(struct xfs_attr_leaf_name_local) - 1 +
-			nlen + vlen, XFS_ATTR_LEAF_NAME_ALIGN);
+	/*
+	 * Prior to Linux 6.5, struct xfs_attr_leaf_name_local ended with
+	 * nameval[1], which was used as a flexarray.  The layout of this
+	 * struct is 3 bytes of fixed-length fields followed by a __u8 flex
+	 * array at offset 3.
+	 *
+	 * struct xfs_attr_leaf_name_local had zero bytes of implicit padding
+	 * at the end of the struct to make the struct length 4.  On most
+	 * architectures, after converting nameval[1] to nameval[], there is
+	 * one implicit padding byte and the struct size remains 4.  However,
+	 * there are compiler configurations that do not add implicit padding
+	 * at all (m68k) and would break.
+	 *
+	 * This entsize computation historically added (the xattr name and
+	 * value length) to (the padded struct length - 1) and rounded that sum
+	 * up to the nearest multiple of 4 (NAME_ALIGN).  IOWs, the formula is
+	 * round_up(3 + nlen + vlen, 4).  This is encoded in the ondisk format,
+	 * so we cannot change this.
+	 *
+	 * Compute the entsize from offsetof of the flexarray and manually
+	 * adding bytes for the implicit padding.
+	 */
+	const size_t localsize =
+			offsetof(struct xfs_attr_leaf_name_local, nameval);
+
+	return round_up(localsize + nlen + vlen, XFS_ATTR_LEAF_NAME_ALIGN);
 }
 
 static inline int xfs_attr_leaf_entsize_local_max(int bsize)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 9c60ebb3..8927ecb2 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -588,16 +588,19 @@ typedef struct xfs_attrlist_cursor {
  *
  * NOTE: struct xfs_attrlist must match struct attrlist defined in libattr, and
  * struct xfs_attrlist_ent must match struct attrlist_ent defined in libattr.
+ *
+ * WARNING: In Linux 6.5, al_offset and a_name were changed from array[1] to
+ * array[].  Anyone using sizeof is (already) broken!
  */
 struct xfs_attrlist {
 	__s32	al_count;	/* number of entries in attrlist */
 	__s32	al_more;	/* T/F: more attrs (do call again) */
-	__s32	al_offset[1];	/* byte offsets of attrs [var-sized] */
+	__s32	al_offset[];	/* byte offsets of attrs [var-sized] */
 };
 
 struct xfs_attrlist_ent {	/* data from attr_list() */
 	__u32	a_valuelen;	/* number bytes in value of attr */
-	char	a_name[1];	/* attr name (NULL terminated) */
+	char	a_name[];	/* attr name (NULL terminated) */
 };
 
 typedef struct xfs_fsop_attrlist_handlereq {

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] libxfs: Redefine 1-element arrays as flexible arrays
  2023-07-12  5:37 ` Darrick J. Wong
@ 2023-07-12 13:09   ` Christoph Hellwig
  2023-07-13  5:44     ` Darrick J. Wong
  2023-07-12 18:45   ` Kees Cook
  1 sibling, 1 reply; 6+ messages in thread
From: Christoph Hellwig @ 2023-07-12 13:09 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Kees Cook, Carlos Maiolino, Dave Chinner, Jeff Layton,
	Eric Biggers, Gustavo A. R. Silva, linux-xfs, linux-hardening

On Tue, Jul 11, 2023 at 10:37:38PM -0700, Darrick J. Wong wrote:
> Here's my version, where I go for a straight a[1] -> a[] conversion and
> let downstream attrlist ioctl users eat their lumps.  What do you think
> of the excess-documentation approach?

I think this is roughtly the right thing, with one big caveat:

> +	/* In Linux 6.5 this flex array was changed from list[1] to list[]. */

For all the format headers there's no need for these comments.  We've
done this for various other structures that had the old one size arrays
and never bothered.

> diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
> index 9c60ebb3..8927ecb2 100644
> --- a/libxfs/xfs_fs.h
> +++ b/libxfs/xfs_fs.h
> @@ -588,16 +588,19 @@ typedef struct xfs_attrlist_cursor {
>   *
>   * NOTE: struct xfs_attrlist must match struct attrlist defined in libattr, and
>   * struct xfs_attrlist_ent must match struct attrlist_ent defined in libattr.
> + *
> + * WARNING: In Linux 6.5, al_offset and a_name were changed from array[1] to
> + * array[].  Anyone using sizeof is (already) broken!
>   */
>  struct xfs_attrlist {
>  	__s32	al_count;	/* number of entries in attrlist */
>  	__s32	al_more;	/* T/F: more attrs (do call again) */
> -	__s32	al_offset[1];	/* byte offsets of attrs [var-sized] */
> +	__s32	al_offset[];	/* byte offsets of attrs [var-sized] */
>  };
>  
>  struct xfs_attrlist_ent {	/* data from attr_list() */
>  	__u32	a_valuelen;	/* number bytes in value of attr */
> -	char	a_name[1];	/* attr name (NULL terminated) */
> +	char	a_name[];	/* attr name (NULL terminated) */
>  };

Now these are currently exposed in a xfsprogs headeer and IFF someone
is using them it would break them in nasty ways.  That being said,
xfsprogs itself doesn't use them as it relies on identically layed
out but differntly named structures from libattr.

So I think we should just move these two out of xfs_fs.h, because
while they document a UAPI, they aren't actually used by userspace.

With that the need for any caution goes away and we can just fix the
definition without any caveats.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] libxfs: Redefine 1-element arrays as flexible arrays
  2023-07-12  5:37 ` Darrick J. Wong
  2023-07-12 13:09   ` Christoph Hellwig
@ 2023-07-12 18:45   ` Kees Cook
  1 sibling, 0 replies; 6+ messages in thread
From: Kees Cook @ 2023-07-12 18:45 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Carlos Maiolino, Dave Chinner, Jeff Layton, Eric Biggers,
	Gustavo A. R. Silva, linux-xfs, linux-hardening

On Tue, Jul 11, 2023 at 10:37:38PM -0700, Darrick J. Wong wrote:
> Here's my version, where I go for a straight a[1] -> a[] conversion and
> let downstream attrlist ioctl users eat their lumps.  What do you think
> of the excess-documentation approach?

This is fine by me, and I think it's much cleaner. Thanks for running
with this!

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] libxfs: Redefine 1-element arrays as flexible arrays
  2023-07-12 13:09   ` Christoph Hellwig
@ 2023-07-13  5:44     ` Darrick J. Wong
  2023-07-13  5:54       ` Christoph Hellwig
  0 siblings, 1 reply; 6+ messages in thread
From: Darrick J. Wong @ 2023-07-13  5:44 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Kees Cook, Carlos Maiolino, Dave Chinner, Jeff Layton,
	Eric Biggers, Gustavo A. R. Silva, linux-xfs, linux-hardening

On Wed, Jul 12, 2023 at 06:09:31AM -0700, Christoph Hellwig wrote:
> On Tue, Jul 11, 2023 at 10:37:38PM -0700, Darrick J. Wong wrote:
> > Here's my version, where I go for a straight a[1] -> a[] conversion and
> > let downstream attrlist ioctl users eat their lumps.  What do you think
> > of the excess-documentation approach?
> 
> I think this is roughtly the right thing, with one big caveat:
> 
> > +	/* In Linux 6.5 this flex array was changed from list[1] to list[]. */
> 
> For all the format headers there's no need for these comments.  We've
> done this for various other structures that had the old one size arrays
> and never bothered.

<nod> Dave looked at an earlier version and wanted the comments for
xfs_da_format.h to steer people at the entsize helpers.  I more or less
agree that it's overkill everywhere else though.

> > diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
> > index 9c60ebb3..8927ecb2 100644
> > --- a/libxfs/xfs_fs.h
> > +++ b/libxfs/xfs_fs.h
> > @@ -588,16 +588,19 @@ typedef struct xfs_attrlist_cursor {
> >   *
> >   * NOTE: struct xfs_attrlist must match struct attrlist defined in libattr, and
> >   * struct xfs_attrlist_ent must match struct attrlist_ent defined in libattr.
> > + *
> > + * WARNING: In Linux 6.5, al_offset and a_name were changed from array[1] to
> > + * array[].  Anyone using sizeof is (already) broken!
> >   */
> >  struct xfs_attrlist {
> >  	__s32	al_count;	/* number of entries in attrlist */
> >  	__s32	al_more;	/* T/F: more attrs (do call again) */
> > -	__s32	al_offset[1];	/* byte offsets of attrs [var-sized] */
> > +	__s32	al_offset[];	/* byte offsets of attrs [var-sized] */
> >  };
> >  
> >  struct xfs_attrlist_ent {	/* data from attr_list() */
> >  	__u32	a_valuelen;	/* number bytes in value of attr */
> > -	char	a_name[1];	/* attr name (NULL terminated) */
> > +	char	a_name[];	/* attr name (NULL terminated) */
> >  };
> 
> Now these are currently exposed in a xfsprogs headeer and IFF someone
> is using them it would break them in nasty ways.  That being said,
> xfsprogs itself doesn't use them as it relies on identically layed
> out but differntly named structures from libattr.
> 
> So I think we should just move these two out of xfs_fs.h, because
> while they document a UAPI, they aren't actually used by userspace.
> 
> With that the need for any caution goes away and we can just fix the
> definition without any caveats.

So I looked at the libattr headers for Ubuntu 22.04 and saw this:

/*
 * List the names and sizes of the values of all the attributes of an object.
 * "Cursor" must be allocated and zeroed before the first call, it is used
 * to maintain context between system calls if all the attribute names won't
 * fit into the buffer on the first system call.
 * The return value is -1 on error (w/errno set appropriately), 0 on success.
 */
extern int attr_list(const char *__path, char *__buffer, const int __buffersize,
		int __flags, attrlist_cursor_t *__cursor)
	__attribute__ ((deprecated ("Use listxattr or llistxattr instead")));
extern int attr_listf(int __fd, char *__buffer, const int __buffersize,
		int __flags, attrlist_cursor_t *__cursor)
	__attribute__ ((deprecated ("Use flistxattr instead")));

I take that as a sign that they could drop all these xfs-specific APIs
one day, which means we ought to keep them in xfs_fs.h.

--D

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] libxfs: Redefine 1-element arrays as flexible arrays
  2023-07-13  5:44     ` Darrick J. Wong
@ 2023-07-13  5:54       ` Christoph Hellwig
  0 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2023-07-13  5:54 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Christoph Hellwig, Kees Cook, Carlos Maiolino, Dave Chinner,
	Jeff Layton, Eric Biggers, Gustavo A. R. Silva, linux-xfs,
	linux-hardening

On Wed, Jul 12, 2023 at 10:44:50PM -0700, Darrick J. Wong wrote:
> /*
>  * List the names and sizes of the values of all the attributes of an object.
>  * "Cursor" must be allocated and zeroed before the first call, it is used
>  * to maintain context between system calls if all the attribute names won't
>  * fit into the buffer on the first system call.
>  * The return value is -1 on error (w/errno set appropriately), 0 on success.
>  */
> extern int attr_list(const char *__path, char *__buffer, const int __buffersize,
> 		int __flags, attrlist_cursor_t *__cursor)
> 	__attribute__ ((deprecated ("Use listxattr or llistxattr instead")));
> extern int attr_listf(int __fd, char *__buffer, const int __buffersize,
> 		int __flags, attrlist_cursor_t *__cursor)
> 	__attribute__ ((deprecated ("Use flistxattr instead")));
> 
> I take that as a sign that they could drop all these xfs-specific APIs
> one day, which means we ought to keep them in xfs_fs.h.

Well...

These APIs you quoted are in fact internally mapped to the normal
xattr syscalls by libattr, and have been since the Linux xattr syscalls
were created.  The only thing that actually uses the definitions in
Linux is the magic attrlist by handle ioctl that exists only in
XFS and which is exported through libhandle in xfsprogs.  But the
libhandle API is based on the attrlist_cursor from libattr and
doesn't use the xfs_ kernel definitions.

(that struct attrlist/attrlist_ent in libattr have the same 1-sized
array problem, but fortunately we don't need to solve that here..)

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-07-13  5:54 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-11 22:20 [PATCH] libxfs: Redefine 1-element arrays as flexible arrays Kees Cook
2023-07-12  5:37 ` Darrick J. Wong
2023-07-12 13:09   ` Christoph Hellwig
2023-07-13  5:44     ` Darrick J. Wong
2023-07-13  5:54       ` Christoph Hellwig
2023-07-12 18:45   ` Kees Cook

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).