Commit 674470d97958a0ec72f72caf7f6451da40159cc7

Authored by Joonyoung Shim
Committed by Linus Torvalds
1 parent eee87e1726

lib/genalloc.c: fix overflow of ending address of memory chunk

In struct gen_pool_chunk, end_addr means the end address of memory chunk
(inclusive), but in the implementation it is treated as address + size of
memory chunk (exclusive), so it points to the address plus one instead of
correct ending address.

The ending address of memory chunk plus one will cause overflow on the
memory chunk including the last address of memory map, e.g.  when starting
address is 0xFFF00000 and size is 0x100000 on 32bit machine, ending
address will be 0x100000000.

Use correct ending address like starting address + size - 1.

[akpm@linux-foundation.org: add comment to struct gen_pool_chunk:end_addr]
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 2 changed files with 14 additions and 9 deletions Side-by-side Diff

include/linux/genalloc.h
... ... @@ -66,8 +66,8 @@
66 66 struct list_head next_chunk; /* next chunk in pool */
67 67 atomic_t avail;
68 68 phys_addr_t phys_addr; /* physical starting address of memory chunk */
69   - unsigned long start_addr; /* starting address of memory chunk */
70   - unsigned long end_addr; /* ending address of memory chunk */
  69 + unsigned long start_addr; /* start address of memory chunk */
  70 + unsigned long end_addr; /* end address of memory chunk (inclusive) */
71 71 unsigned long bits[0]; /* bitmap for allocating memory chunk */
72 72 };
73 73  
... ... @@ -37,6 +37,11 @@
37 37 #include <linux/of_address.h>
38 38 #include <linux/of_device.h>
39 39  
  40 +static inline size_t chunk_size(const struct gen_pool_chunk *chunk)
  41 +{
  42 + return chunk->end_addr - chunk->start_addr + 1;
  43 +}
  44 +
40 45 static int set_bits_ll(unsigned long *addr, unsigned long mask_to_set)
41 46 {
42 47 unsigned long val, nval;
... ... @@ -188,7 +193,7 @@
188 193  
189 194 chunk->phys_addr = phys;
190 195 chunk->start_addr = virt;
191   - chunk->end_addr = virt + size;
  196 + chunk->end_addr = virt + size - 1;
192 197 atomic_set(&chunk->avail, size);
193 198  
194 199 spin_lock(&pool->lock);
... ... @@ -213,7 +218,7 @@
213 218  
214 219 rcu_read_lock();
215 220 list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
216   - if (addr >= chunk->start_addr && addr < chunk->end_addr) {
  221 + if (addr >= chunk->start_addr && addr <= chunk->end_addr) {
217 222 paddr = chunk->phys_addr + (addr - chunk->start_addr);
218 223 break;
219 224 }
... ... @@ -242,7 +247,7 @@
242 247 chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
243 248 list_del(&chunk->next_chunk);
244 249  
245   - end_bit = (chunk->end_addr - chunk->start_addr) >> order;
  250 + end_bit = chunk_size(chunk) >> order;
246 251 bit = find_next_bit(chunk->bits, end_bit, 0);
247 252 BUG_ON(bit < end_bit);
248 253  
... ... @@ -283,7 +288,7 @@
283 288 if (size > atomic_read(&chunk->avail))
284 289 continue;
285 290  
286   - end_bit = (chunk->end_addr - chunk->start_addr) >> order;
  291 + end_bit = chunk_size(chunk) >> order;
287 292 retry:
288 293 start_bit = pool->algo(chunk->bits, end_bit, start_bit, nbits,
289 294 pool->data);
... ... @@ -330,8 +335,8 @@
330 335 nbits = (size + (1UL << order) - 1) >> order;
331 336 rcu_read_lock();
332 337 list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
333   - if (addr >= chunk->start_addr && addr < chunk->end_addr) {
334   - BUG_ON(addr + size > chunk->end_addr);
  338 + if (addr >= chunk->start_addr && addr <= chunk->end_addr) {
  339 + BUG_ON(addr + size - 1 > chunk->end_addr);
335 340 start_bit = (addr - chunk->start_addr) >> order;
336 341 remain = bitmap_clear_ll(chunk->bits, start_bit, nbits);
337 342 BUG_ON(remain);
... ... @@ -400,7 +405,7 @@
400 405  
401 406 rcu_read_lock();
402 407 list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk)
403   - size += chunk->end_addr - chunk->start_addr;
  408 + size += chunk_size(chunk);
404 409 rcu_read_unlock();
405 410 return size;
406 411 }