[PATCH v2] bitops: make the *_bit_le functions use unsigned long
Benjamin Marzinski <[email protected]>
| Newsgroups | org.kernel.vger.linux-arch,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
The *_bit_le functions use a signed integer for the bit number. However, the *_bit functions can use an unsigned long. This causes problems if there is a large bitmap and a bit number > 0x80000000 is passed in. Since that is a negative int, it will get sign extended to a long when getting passed to the *_bit function, turning it into a huge bit number. This usually ends up with the memory address wrapping around and the function accessing memory before the start of the bitmap. Avoid this by making the *_bit_le functions take an unsigned long. This can be triggered by faking an almost 4TB dm-mirror device, which uses bitmaps to track the mirror regions: $ dmsetup create bigzero --table '0 8589934590 zero' $ dmsetup create mymirror --table '0 8589934590 mirror core 2 2 nosync 2 /dev/mapper/bigzero 0 /dev/mapper/bigzero 0' This will access memory before the start of the sync_bits bitmap, and likely hit the guard page of the previously allocated clean_bits bitmap, causing a kernel panic with the old code. I looked and didn't see any crazy code using the signed int to intentionally try and access bits before some address within the bitmap. Signed-off-by: Benjamin Marzinski <[email protected]> --- Changes from v1 (commit message only. No code changed): - changed the commit message to correctly say that the functions now take unsigned long arguments from the bit nr - Added the reproducer instructions to the commit message. include/asm-generic/bitops/le.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/asm-generic/bitops/le.h b/include/asm-generic/bitops/le.h index d51beff60375..e3b0da9a25f1 100644 --- a/include/asm-generic/bitops/le.h +++ b/include/asm-generic/bitops/le.h @@ -16,47 +16,47 @@ #endif -static inline int test_bit_le(int nr, const void *addr) +static inline int test_bit_le(unsigned long nr, const void *addr) { return test_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void set_bit_le(int nr, void *addr) +static inline void set_bit_le(unsigned long nr, void *addr) { set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void clear_bit_le(int nr, void *addr) +static inline void clear_bit_le(unsigned long nr, void *addr) { clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void __set_bit_le(int nr, void *addr) +static inline void __set_bit_le(unsigned long nr, void *addr) { __set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline void __clear_bit_le(int nr, void *addr) +static inline void __clear_bit_le(unsigned long nr, void *addr) { __clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int test_and_set_bit_le(int nr, void *addr) +static inline int test_and_set_bit_le(unsigned long nr, void *addr) { return test_and_set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int test_and_clear_bit_le(int nr, void *addr) +static inline int test_and_clear_bit_le(unsigned long nr, void *addr) { return test_and_clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int __test_and_set_bit_le(int nr, void *addr) +static inline int __test_and_set_bit_le(unsigned long nr, void *addr) { return __test_and_set_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -static inline int __test_and_clear_bit_le(int nr, void *addr) +static inline int __test_and_clear_bit_le(unsigned long nr, void *addr) { return __test_and_clear_bit(nr ^ BITOP_LE_SWIZZLE, addr); } -- 2.53.0