On Wed, Dec 17, 2025 at 10:21:09PM +0100, Martin Wilck wrote:
> The macro BITFIELD used a cast of a pointer to a different anonymous struct
> to (struct bitfield *). This violates strict aliasing rules, because the
> two structs aren't compatible types in the sense of the language
> specification. With -O2, gcc 15.1 generates code that makes th pgcmp()
> function fail, because the compiler doesn't initialize bf->len aka
> __storage_for__bf.len to 64.
> 
> The problem will show through error messages like this:
> 
> multipathd[1974208]: is_bit_set_in_bitfield: bitfield overflow: 1 >= 0
> 
> Fix this by using a union
> 
> Fixes: 9a2f173 ("libmpathutil: change STATIC_BITFIELD to BITFIELD")
> Signed-off-by: Martin Wilck <[email protected]>
> ---
>  libmpathutil/util.c       |  8 ++++----
>  libmpathutil/util.h       | 41 +++++++++++++++++++++++----------------
>  libmultipath/configure.c  |  4 ++--
>  libmultipath/pgpolicies.c |  2 +-
>  tests/util.c              | 10 +++++-----
>  5 files changed, 36 insertions(+), 29 deletions(-)
> 
> diff --git a/libmpathutil/util.c b/libmpathutil/util.c
> index 37412c6..5e45750 100644
> --- a/libmpathutil/util.c
> +++ b/libmpathutil/util.c
> @@ -337,15 +337,15 @@ void cleanup_fclose(void *p)
>               fclose(p);
>  }
>  
> -void cleanup_bitfield(struct bitfield **p)
> +void cleanup_bitfield(union bitfield **p)
>  {
>       free(*p);
>  }
>  
> -struct bitfield *alloc_bitfield(unsigned int maxbit)
> +union bitfield *alloc_bitfield(unsigned int maxbit)
>  {
>       unsigned int n;
> -     struct bitfield *bf;
> +     union bitfield *bf;
>  
>       if (maxbit == 0) {
>               errno = EINVAL;
> @@ -353,7 +353,7 @@ struct bitfield *alloc_bitfield(unsigned int maxbit)
>       }
>  
>       n = (maxbit - 1) / bits_per_slot + 1;
> -     bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
> +     bf = calloc(1, sizeof(union bitfield) + (n - 1) * sizeof(bitfield_t));
>       if (bf)
>               bf->len = maxbit;
>       return bf;
> diff --git a/libmpathutil/util.h b/libmpathutil/util.h
> index 3799fd4..1ec6a46 100644
> --- a/libmpathutil/util.h
> +++ b/libmpathutil/util.h
> @@ -86,29 +86,36 @@ typedef unsigned int bitfield_t;
>  #endif
>  #define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
>  
> -struct bitfield {
> -     unsigned int len;
> -     bitfield_t bits[];
> +union bitfield {
> +     struct {
> +             unsigned int len;
> +             bitfield_t bits[];
> +     };
> +     /* for initialization in the BITFIELD macro */
> +     struct {
> +             unsigned int __len;
> +             bitfield_t __bits[1];
> +     };
>  };
>  
> -#define BITFIELD(name, length)                                       \
> -     struct {                                                        \
> -             unsigned int len;                                       \
> -             bitfield_t bits[((length) - 1) / bits_per_slot + 1];    \
> -     } __storage_for__ ## name = {                                   \
> -             .len = (length),                                        \
> -             .bits = { 0, },                                         \
> +/*
> + * gcc 4.9.2 (Debian Jessie) raises an error if the initializer for
> + * .__len comes first. Thus put .__bits first.
> + */
> +#define BITFIELD(name, length)                                               
> \
> +     union bitfield __storage_for__ ## name = {                      \
> +             .__bits = { 0 },                                        \
> +             .__len = (length),                                      \
>       }; \

It looks like this only allocates one bitfield_t, regardless of length.
Am I missing something here?

-Ben

> -     struct bitfield *name = (struct bitfield *)& __storage_for__ ## name
> +     union bitfield *name = & __storage_for__ ## name
>  
> -struct bitfield *alloc_bitfield(unsigned int maxbit);
> +union bitfield *alloc_bitfield(unsigned int maxbit);
>  
>  void log_bitfield_overflow__(const char *f, unsigned int bit, unsigned int 
> len);
>  #define log_bitfield_overflow(bit, len) \
>       log_bitfield_overflow__(__func__, bit, len)
>  
> -static inline bool is_bit_set_in_bitfield(unsigned int bit,
> -                                    const struct bitfield *bf)
> +static inline bool is_bit_set_in_bitfield(unsigned int bit, const union 
> bitfield *bf)
>  {
>       if (bit >= bf->len) {
>               log_bitfield_overflow(bit, bf->len);
> @@ -118,7 +125,7 @@ static inline bool is_bit_set_in_bitfield(unsigned int 
> bit,
>                 (1ULL << (bit % bits_per_slot)));
>  }
>  
> -static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
> +static inline void set_bit_in_bitfield(unsigned int bit, union bitfield *bf)
>  {
>       if (bit >= bf->len) {
>               log_bitfield_overflow(bit, bf->len);
> @@ -127,7 +134,7 @@ static inline void set_bit_in_bitfield(unsigned int bit, 
> struct bitfield *bf)
>       bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
>  }
>  
> -static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield 
> *bf)
> +static inline void clear_bit_in_bitfield(unsigned int bit, union bitfield 
> *bf)
>  {
>       if (bit >= bf->len) {
>               log_bitfield_overflow(bit, bf->len);
> @@ -146,5 +153,5 @@ static inline void clear_bit_in_bitfield(unsigned int 
> bit, struct bitfield *bf)
>  void cleanup_charp(char **p);
>  void cleanup_ucharp(unsigned char **p);
>  void cleanup_udev_device(struct udev_device **udd);
> -void cleanup_bitfield(struct bitfield **p);
> +void cleanup_bitfield(union bitfield **p);
>  #endif /* UTIL_H_INCLUDED */
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index a8e18a2..41b6a9f 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -440,7 +440,7 @@ static int pgcmp(struct multipath *mpp, struct multipath 
> *cmpp)
>       int i, j;
>       struct pathgroup *pgp, *cpgp;
>       BITFIELD(bf, bits_per_slot);
> -     struct bitfield *bf__  __attribute__((cleanup(cleanup_bitfield))) = 
> NULL;
> +     union bitfield *bf__ __attribute__((cleanup(cleanup_bitfield))) = NULL;
>  
>       if (VECTOR_SIZE(mpp->pg) != VECTOR_SIZE(cmpp->pg))
>               return 1;
> @@ -1049,7 +1049,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, 
> char *refwwid,
>       vector newmp;
>       struct config *conf = NULL;
>       int allow_queueing;
> -     struct bitfield *size_mismatch_seen;
> +     union bitfield *size_mismatch_seen;
>       struct multipath * cmpp;
>  
>       /* ignore refwwid if it's empty */
> diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
> index 2e1a543..57b7485 100644
> --- a/libmultipath/pgpolicies.c
> +++ b/libmultipath/pgpolicies.c
> @@ -201,7 +201,7 @@ int group_by_match(struct multipath * mp, vector paths,
>                  bool (*path_match_fn)(struct path *, struct path *))
>  {
>       int i, j;
> -     struct bitfield *bitmap;
> +     union bitfield *bitmap;
>       struct path * pp;
>       struct pathgroup * pgp;
>       struct path * pp2;
> diff --git a/tests/util.c b/tests/util.c
> index da192ba..67d8148 100644
> --- a/tests/util.c
> +++ b/tests/util.c
> @@ -198,7 +198,7 @@ static uint64_t maybe_swap(uint64_t v)
>  
>  static void test_bitmask_1(void **state)
>  {
> -     struct bitfield *bf;
> +     union bitfield *bf;
>       uint64_t *arr;
>       int i, j, k, m, b;
>  
> @@ -240,7 +240,7 @@ static void test_bitmask_1(void **state)
>  
>  static void test_bitmask_2(void **state)
>  {
> -     struct bitfield *bf;
> +     union bitfield *bf;
>       uint64_t *arr;
>       int i, j, k, m, b;
>  
> @@ -307,7 +307,7 @@ static void test_bitmask_2(void **state)
>   */
>  static void test_bitmask_len_0(void **state)
>  {
> -     struct bitfield *bf;
> +     union bitfield *bf;
>  
>       bf = alloc_bitfield(0);
>       assert_null(bf);
> @@ -329,7 +329,7 @@ static unsigned int maybe_swap_idx(unsigned int i)
>  
>  static void _test_bitmask_small(unsigned int n)
>  {
> -     struct bitfield *bf;
> +     union bitfield *bf;
>       uint32_t *arr;
>       unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
>  
> @@ -378,7 +378,7 @@ static void _test_bitmask_small(unsigned int n)
>  
>  static void _test_bitmask_small_2(unsigned int n)
>  {
> -     struct bitfield *bf;
> +     union bitfield *bf;
>       uint32_t *arr;
>       unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
>  
> -- 
> 2.52.0


Reply via email to