Commit f82a519f authored by Geert Uytterhoeven's avatar Geert Uytterhoeven

m68k: bitops - Never step beyond the end of the bitmap

find_next bitops on m68k (find_next_zero_bit, find_next_bit, and
find_next_bit_le) may cause out of bounds memory access
when the bitmap size in bits % 32 != 0 and offset (the bitnumber
to start searching at) is very close to the bitmap size.

For example,

       unsigned long bitmap[2] = { 0, 0 };
       find_next_bit(bitmap, 63, 62);

1. find_next_bit() tries to find any set bits in bitmap[1],
   but no bits set.

2. Then find_first_bit(bimap + 2, -1)

3. Unfortunately find_first_bit() takes unsigned int as the size argument.

4. find_first_bit will access bitmap[2~] until it find any set bits.

Add missing tests for stepping beyond the end of the bitmap to all
find_{first,next}_*() functions, and make sure they never return a value
larger than the bitmap size.
Reported-by: default avatarAkinobu Mita <akinobu.mita@gmail.com>
Cc: Andreas Schwab <schwab@linux-m68k.org>
Signed-off-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
parent 359c47ea
...@@ -181,14 +181,15 @@ static inline int find_first_zero_bit(const unsigned long *vaddr, ...@@ -181,14 +181,15 @@ static inline int find_first_zero_bit(const unsigned long *vaddr,
{ {
const unsigned long *p = vaddr; const unsigned long *p = vaddr;
int res = 32; int res = 32;
unsigned int words;
unsigned long num; unsigned long num;
if (!size) if (!size)
return 0; return 0;
size = (size + 31) >> 5; words = (size + 31) >> 5;
while (!(num = ~*p++)) { while (!(num = ~*p++)) {
if (!--size) if (!--words)
goto out; goto out;
} }
...@@ -196,7 +197,8 @@ static inline int find_first_zero_bit(const unsigned long *vaddr, ...@@ -196,7 +197,8 @@ static inline int find_first_zero_bit(const unsigned long *vaddr,
: "=d" (res) : "d" (num & -num)); : "=d" (res) : "d" (num & -num));
res ^= 31; res ^= 31;
out: out:
return ((long)p - (long)vaddr - 4) * 8 + res; res += ((long)p - (long)vaddr - 4) * 8;
return res < size ? res : size;
} }
static inline int find_next_zero_bit(const unsigned long *vaddr, int size, static inline int find_next_zero_bit(const unsigned long *vaddr, int size,
...@@ -215,9 +217,14 @@ static inline int find_next_zero_bit(const unsigned long *vaddr, int size, ...@@ -215,9 +217,14 @@ static inline int find_next_zero_bit(const unsigned long *vaddr, int size,
/* Look for zero in first longword */ /* Look for zero in first longword */
__asm__ __volatile__ ("bfffo %1{#0,#0},%0" __asm__ __volatile__ ("bfffo %1{#0,#0},%0"
: "=d" (res) : "d" (num & -num)); : "=d" (res) : "d" (num & -num));
if (res < 32) if (res < 32) {
return offset + (res ^ 31); offset += res ^ 31;
return offset < size ? offset : size;
}
offset += 32; offset += 32;
if (offset >= size)
return size;
} }
/* No zero yet, search remaining full bytes for a zero */ /* No zero yet, search remaining full bytes for a zero */
return offset + find_first_zero_bit(p, size - offset); return offset + find_first_zero_bit(p, size - offset);
...@@ -227,14 +234,15 @@ static inline int find_first_bit(const unsigned long *vaddr, unsigned size) ...@@ -227,14 +234,15 @@ static inline int find_first_bit(const unsigned long *vaddr, unsigned size)
{ {
const unsigned long *p = vaddr; const unsigned long *p = vaddr;
int res = 32; int res = 32;
unsigned int words;
unsigned long num; unsigned long num;
if (!size) if (!size)
return 0; return 0;
size = (size + 31) >> 5; words = (size + 31) >> 5;
while (!(num = *p++)) { while (!(num = *p++)) {
if (!--size) if (!--words)
goto out; goto out;
} }
...@@ -242,7 +250,8 @@ static inline int find_first_bit(const unsigned long *vaddr, unsigned size) ...@@ -242,7 +250,8 @@ static inline int find_first_bit(const unsigned long *vaddr, unsigned size)
: "=d" (res) : "d" (num & -num)); : "=d" (res) : "d" (num & -num));
res ^= 31; res ^= 31;
out: out:
return ((long)p - (long)vaddr - 4) * 8 + res; res += ((long)p - (long)vaddr - 4) * 8;
return res < size ? res : size;
} }
static inline int find_next_bit(const unsigned long *vaddr, int size, static inline int find_next_bit(const unsigned long *vaddr, int size,
...@@ -261,9 +270,14 @@ static inline int find_next_bit(const unsigned long *vaddr, int size, ...@@ -261,9 +270,14 @@ static inline int find_next_bit(const unsigned long *vaddr, int size,
/* Look for one in first longword */ /* Look for one in first longword */
__asm__ __volatile__ ("bfffo %1{#0,#0},%0" __asm__ __volatile__ ("bfffo %1{#0,#0},%0"
: "=d" (res) : "d" (num & -num)); : "=d" (res) : "d" (num & -num));
if (res < 32) if (res < 32) {
return offset + (res ^ 31); offset += res ^ 31;
return offset < size ? offset : size;
}
offset += 32; offset += 32;
if (offset >= size)
return size;
} }
/* No one yet, search remaining full bytes for a one */ /* No one yet, search remaining full bytes for a one */
return offset + find_first_bit(p, size - offset); return offset + find_first_bit(p, size - offset);
...@@ -364,23 +378,25 @@ static inline int test_bit_le(int nr, const void *vaddr) ...@@ -364,23 +378,25 @@ static inline int test_bit_le(int nr, const void *vaddr)
static inline int find_first_zero_bit_le(const void *vaddr, unsigned size) static inline int find_first_zero_bit_le(const void *vaddr, unsigned size)
{ {
const unsigned long *p = vaddr, *addr = vaddr; const unsigned long *p = vaddr, *addr = vaddr;
int res; int res = 0;
unsigned int words;
if (!size) if (!size)
return 0; return 0;
size = (size >> 5) + ((size & 31) > 0); words = (size >> 5) + ((size & 31) > 0);
while (*p++ == ~0UL) while (*p++ == ~0UL) {
{ if (--words == 0)
if (--size == 0) goto out;
return (p - addr) << 5;
} }
--p; --p;
for (res = 0; res < 32; res++) for (res = 0; res < 32; res++)
if (!test_bit_le(res, p)) if (!test_bit_le(res, p))
break; break;
return (p - addr) * 32 + res; out:
res += (p - addr) * 32;
return res < size ? res : size;
} }
static inline unsigned long find_next_zero_bit_le(const void *addr, static inline unsigned long find_next_zero_bit_le(const void *addr,
...@@ -398,10 +414,15 @@ static inline unsigned long find_next_zero_bit_le(const void *addr, ...@@ -398,10 +414,15 @@ static inline unsigned long find_next_zero_bit_le(const void *addr,
offset -= bit; offset -= bit;
/* Look for zero in first longword */ /* Look for zero in first longword */
for (res = bit; res < 32; res++) for (res = bit; res < 32; res++)
if (!test_bit_le(res, p)) if (!test_bit_le(res, p)) {
return offset + res; offset += res;
return offset < size ? offset : size;
}
p++; p++;
offset += 32; offset += 32;
if (offset >= size)
return size;
} }
/* No zero yet, search remaining full bytes for a zero */ /* No zero yet, search remaining full bytes for a zero */
return offset + find_first_zero_bit_le(p, size - offset); return offset + find_first_zero_bit_le(p, size - offset);
...@@ -410,22 +431,25 @@ static inline unsigned long find_next_zero_bit_le(const void *addr, ...@@ -410,22 +431,25 @@ static inline unsigned long find_next_zero_bit_le(const void *addr,
static inline int find_first_bit_le(const void *vaddr, unsigned size) static inline int find_first_bit_le(const void *vaddr, unsigned size)
{ {
const unsigned long *p = vaddr, *addr = vaddr; const unsigned long *p = vaddr, *addr = vaddr;
int res; int res = 0;
unsigned int words;
if (!size) if (!size)
return 0; return 0;
size = (size >> 5) + ((size & 31) > 0); words = (size >> 5) + ((size & 31) > 0);
while (*p++ == 0UL) { while (*p++ == 0UL) {
if (--size == 0) if (--words == 0)
return (p - addr) << 5; goto out;
} }
--p; --p;
for (res = 0; res < 32; res++) for (res = 0; res < 32; res++)
if (test_bit_le(res, p)) if (test_bit_le(res, p))
break; break;
return (p - addr) * 32 + res; out:
res += (p - addr) * 32;
return res < size ? res : size;
} }
static inline unsigned long find_next_bit_le(const void *addr, static inline unsigned long find_next_bit_le(const void *addr,
...@@ -443,10 +467,15 @@ static inline unsigned long find_next_bit_le(const void *addr, ...@@ -443,10 +467,15 @@ static inline unsigned long find_next_bit_le(const void *addr,
offset -= bit; offset -= bit;
/* Look for one in first longword */ /* Look for one in first longword */
for (res = bit; res < 32; res++) for (res = bit; res < 32; res++)
if (test_bit_le(res, p)) if (test_bit_le(res, p)) {
return offset + res; offset += res;
return offset < size ? offset : size;
}
p++; p++;
offset += 32; offset += 32;
if (offset >= size)
return size;
} }
/* No set bit yet, search remaining full bytes for a set bit */ /* No set bit yet, search remaining full bytes for a set bit */
return offset + find_first_bit_le(p, size - offset); return offset + find_first_bit_le(p, size - offset);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment