diff options
author | Geert Uytterhoeven <geert@linux-m68k.org> | 2011-04-03 08:00:10 -0400 |
---|---|---|
committer | Geert Uytterhoeven <geert@linux-m68k.org> | 2011-05-19 12:19:09 -0400 |
commit | f82a519f1262963d6ab30fa238721463fad2e0c8 (patch) | |
tree | c7808cc9a265b923a20d96d5a9aff2c2f1669d4a /arch/m68k | |
parent | 359c47ea71be21c1105ae474e8c90ec3e988bbdf (diff) |
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: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Andreas Schwab <schwab@linux-m68k.org>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Diffstat (limited to 'arch/m68k')
-rw-r--r-- | arch/m68k/include/asm/bitops_mm.h | 81 |
1 files changed, 55 insertions, 26 deletions
diff --git a/arch/m68k/include/asm/bitops_mm.h b/arch/m68k/include/asm/bitops_mm.h index 5bd3afadeafa..e9020f88a748 100644 --- a/arch/m68k/include/asm/bitops_mm.h +++ b/arch/m68k/include/asm/bitops_mm.h | |||
@@ -181,14 +181,15 @@ static inline int find_first_zero_bit(const unsigned long *vaddr, | |||
181 | { | 181 | { |
182 | const unsigned long *p = vaddr; | 182 | const unsigned long *p = vaddr; |
183 | int res = 32; | 183 | int res = 32; |
184 | unsigned int words; | ||
184 | unsigned long num; | 185 | unsigned long num; |
185 | 186 | ||
186 | if (!size) | 187 | if (!size) |
187 | return 0; | 188 | return 0; |
188 | 189 | ||
189 | size = (size + 31) >> 5; | 190 | words = (size + 31) >> 5; |
190 | while (!(num = ~*p++)) { | 191 | while (!(num = ~*p++)) { |
191 | if (!--size) | 192 | if (!--words) |
192 | goto out; | 193 | goto out; |
193 | } | 194 | } |
194 | 195 | ||
@@ -196,7 +197,8 @@ static inline int find_first_zero_bit(const unsigned long *vaddr, | |||
196 | : "=d" (res) : "d" (num & -num)); | 197 | : "=d" (res) : "d" (num & -num)); |
197 | res ^= 31; | 198 | res ^= 31; |
198 | out: | 199 | out: |
199 | return ((long)p - (long)vaddr - 4) * 8 + res; | 200 | res += ((long)p - (long)vaddr - 4) * 8; |
201 | return res < size ? res : size; | ||
200 | } | 202 | } |
201 | 203 | ||
202 | static inline int find_next_zero_bit(const unsigned long *vaddr, int size, | 204 | 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 | /* Look for zero in first longword */ | 217 | /* Look for zero in first longword */ |
216 | __asm__ __volatile__ ("bfffo %1{#0,#0},%0" | 218 | __asm__ __volatile__ ("bfffo %1{#0,#0},%0" |
217 | : "=d" (res) : "d" (num & -num)); | 219 | : "=d" (res) : "d" (num & -num)); |
218 | if (res < 32) | 220 | if (res < 32) { |
219 | return offset + (res ^ 31); | 221 | offset += res ^ 31; |
222 | return offset < size ? offset : size; | ||
223 | } | ||
220 | offset += 32; | 224 | offset += 32; |
225 | |||
226 | if (offset >= size) | ||
227 | return size; | ||
221 | } | 228 | } |
222 | /* No zero yet, search remaining full bytes for a zero */ | 229 | /* No zero yet, search remaining full bytes for a zero */ |
223 | return offset + find_first_zero_bit(p, size - offset); | 230 | 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 | { | 234 | { |
228 | const unsigned long *p = vaddr; | 235 | const unsigned long *p = vaddr; |
229 | int res = 32; | 236 | int res = 32; |
237 | unsigned int words; | ||
230 | unsigned long num; | 238 | unsigned long num; |
231 | 239 | ||
232 | if (!size) | 240 | if (!size) |
233 | return 0; | 241 | return 0; |
234 | 242 | ||
235 | size = (size + 31) >> 5; | 243 | words = (size + 31) >> 5; |
236 | while (!(num = *p++)) { | 244 | while (!(num = *p++)) { |
237 | if (!--size) | 245 | if (!--words) |
238 | goto out; | 246 | goto out; |
239 | } | 247 | } |
240 | 248 | ||
@@ -242,7 +250,8 @@ static inline int find_first_bit(const unsigned long *vaddr, unsigned size) | |||
242 | : "=d" (res) : "d" (num & -num)); | 250 | : "=d" (res) : "d" (num & -num)); |
243 | res ^= 31; | 251 | res ^= 31; |
244 | out: | 252 | out: |
245 | return ((long)p - (long)vaddr - 4) * 8 + res; | 253 | res += ((long)p - (long)vaddr - 4) * 8; |
254 | return res < size ? res : size; | ||
246 | } | 255 | } |
247 | 256 | ||
248 | static inline int find_next_bit(const unsigned long *vaddr, int size, | 257 | 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 | /* Look for one in first longword */ | 270 | /* Look for one in first longword */ |
262 | __asm__ __volatile__ ("bfffo %1{#0,#0},%0" | 271 | __asm__ __volatile__ ("bfffo %1{#0,#0},%0" |
263 | : "=d" (res) : "d" (num & -num)); | 272 | : "=d" (res) : "d" (num & -num)); |
264 | if (res < 32) | 273 | if (res < 32) { |
265 | return offset + (res ^ 31); | 274 | offset += res ^ 31; |
275 | return offset < size ? offset : size; | ||
276 | } | ||
266 | offset += 32; | 277 | offset += 32; |
278 | |||
279 | if (offset >= size) | ||
280 | return size; | ||
267 | } | 281 | } |
268 | /* No one yet, search remaining full bytes for a one */ | 282 | /* No one yet, search remaining full bytes for a one */ |
269 | return offset + find_first_bit(p, size - offset); | 283 | return offset + find_first_bit(p, size - offset); |
@@ -364,23 +378,25 @@ static inline int test_bit_le(int nr, const void *vaddr) | |||
364 | static inline int find_first_zero_bit_le(const void *vaddr, unsigned size) | 378 | static inline int find_first_zero_bit_le(const void *vaddr, unsigned size) |
365 | { | 379 | { |
366 | const unsigned long *p = vaddr, *addr = vaddr; | 380 | const unsigned long *p = vaddr, *addr = vaddr; |
367 | int res; | 381 | int res = 0; |
382 | unsigned int words; | ||
368 | 383 | ||
369 | if (!size) | 384 | if (!size) |
370 | return 0; | 385 | return 0; |
371 | 386 | ||
372 | size = (size >> 5) + ((size & 31) > 0); | 387 | words = (size >> 5) + ((size & 31) > 0); |
373 | while (*p++ == ~0UL) | 388 | while (*p++ == ~0UL) { |
374 | { | 389 | if (--words == 0) |
375 | if (--size == 0) | 390 | goto out; |
376 | return (p - addr) << 5; | ||
377 | } | 391 | } |
378 | 392 | ||
379 | --p; | 393 | --p; |
380 | for (res = 0; res < 32; res++) | 394 | for (res = 0; res < 32; res++) |
381 | if (!test_bit_le(res, p)) | 395 | if (!test_bit_le(res, p)) |
382 | break; | 396 | break; |
383 | return (p - addr) * 32 + res; | 397 | out: |
398 | res += (p - addr) * 32; | ||
399 | return res < size ? res : size; | ||
384 | } | 400 | } |
385 | 401 | ||
386 | static inline unsigned long find_next_zero_bit_le(const void *addr, | 402 | 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 | offset -= bit; | 414 | offset -= bit; |
399 | /* Look for zero in first longword */ | 415 | /* Look for zero in first longword */ |
400 | for (res = bit; res < 32; res++) | 416 | for (res = bit; res < 32; res++) |
401 | if (!test_bit_le(res, p)) | 417 | if (!test_bit_le(res, p)) { |
402 | return offset + res; | 418 | offset += res; |
419 | return offset < size ? offset : size; | ||
420 | } | ||
403 | p++; | 421 | p++; |
404 | offset += 32; | 422 | offset += 32; |
423 | |||
424 | if (offset >= size) | ||
425 | return size; | ||
405 | } | 426 | } |
406 | /* No zero yet, search remaining full bytes for a zero */ | 427 | /* No zero yet, search remaining full bytes for a zero */ |
407 | return offset + find_first_zero_bit_le(p, size - offset); | 428 | 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 | static inline int find_first_bit_le(const void *vaddr, unsigned size) | 431 | static inline int find_first_bit_le(const void *vaddr, unsigned size) |
411 | { | 432 | { |
412 | const unsigned long *p = vaddr, *addr = vaddr; | 433 | const unsigned long *p = vaddr, *addr = vaddr; |
413 | int res; | 434 | int res = 0; |
435 | unsigned int words; | ||
414 | 436 | ||
415 | if (!size) | 437 | if (!size) |
416 | return 0; | 438 | return 0; |
417 | 439 | ||
418 | size = (size >> 5) + ((size & 31) > 0); | 440 | words = (size >> 5) + ((size & 31) > 0); |
419 | while (*p++ == 0UL) { | 441 | while (*p++ == 0UL) { |
420 | if (--size == 0) | 442 | if (--words == 0) |
421 | return (p - addr) << 5; | 443 | goto out; |
422 | } | 444 | } |
423 | 445 | ||
424 | --p; | 446 | --p; |
425 | for (res = 0; res < 32; res++) | 447 | for (res = 0; res < 32; res++) |
426 | if (test_bit_le(res, p)) | 448 | if (test_bit_le(res, p)) |
427 | break; | 449 | break; |
428 | return (p - addr) * 32 + res; | 450 | out: |
451 | res += (p - addr) * 32; | ||
452 | return res < size ? res : size; | ||
429 | } | 453 | } |
430 | 454 | ||
431 | static inline unsigned long find_next_bit_le(const void *addr, | 455 | 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 | offset -= bit; | 467 | offset -= bit; |
444 | /* Look for one in first longword */ | 468 | /* Look for one in first longword */ |
445 | for (res = bit; res < 32; res++) | 469 | for (res = bit; res < 32; res++) |
446 | if (test_bit_le(res, p)) | 470 | if (test_bit_le(res, p)) { |
447 | return offset + res; | 471 | offset += res; |
472 | return offset < size ? offset : size; | ||
473 | } | ||
448 | p++; | 474 | p++; |
449 | offset += 32; | 475 | offset += 32; |
476 | |||
477 | if (offset >= size) | ||
478 | return size; | ||
450 | } | 479 | } |
451 | /* No set bit yet, search remaining full bytes for a set bit */ | 480 | /* No set bit yet, search remaining full bytes for a set bit */ |
452 | return offset + find_first_bit_le(p, size - offset); | 481 | return offset + find_first_bit_le(p, size - offset); |