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), \
}; \
- 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