Commit 674470d97958a0ec72f72caf7f6451da40159cc7
Committed by
Linus Torvalds
1 parent
eee87e1726
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
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 |
lib/genalloc.c
... | ... | @@ -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 | } |