bitfield.h: Fix "type of reg too small for mask" test
authorPeter Zijlstra <peterz@infradead.org>
Wed, 10 Nov 2021 10:01:03 +0000 (11:01 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Sat, 11 Dec 2021 08:09:45 +0000 (09:09 +0100)
The test: 'mask > (typeof(_reg))~0ull' only works correctly when both
sides are unsigned, consider:

 - 0xff000000 vs (int)~0ull
 - 0x000000ff vs (int)~0ull

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/20211110101324.950210584@infradead.org
include/linux/bitfield.h

index 4e035ac..6093fa6 100644 (file)
 
 #define __bf_shf(x) (__builtin_ffsll(x) - 1)
 
+#define __scalar_type_to_unsigned_cases(type)                          \
+               unsigned type:  (unsigned type)0,                       \
+               signed type:    (unsigned type)0
+
+#define __unsigned_scalar_typeof(x) typeof(                            \
+               _Generic((x),                                           \
+                       char:   (unsigned char)0,                       \
+                       __scalar_type_to_unsigned_cases(char),          \
+                       __scalar_type_to_unsigned_cases(short),         \
+                       __scalar_type_to_unsigned_cases(int),           \
+                       __scalar_type_to_unsigned_cases(long),          \
+                       __scalar_type_to_unsigned_cases(long long),     \
+                       default: (x)))
+
+#define __bf_cast_unsigned(type, x)    ((__unsigned_scalar_typeof(type))(x))
+
 #define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx)                      \
        ({                                                              \
                BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask),          \
@@ -49,7 +65,8 @@
                BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ?           \
                                 ~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \
                                 _pfx "value too large for the field"); \
-               BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull,         \
+               BUILD_BUG_ON_MSG(__bf_cast_unsigned(_mask, _mask) >     \
+                                __bf_cast_unsigned(_reg, ~0ull),       \
                                 _pfx "type of reg too small for mask"); \
                __BUILD_BUG_ON_NOT_POWER_OF_2((_mask) +                 \
                                              (1ULL << __bf_shf(_mask))); \