Skip to content

Commit

Permalink
lib/genalloc.c: fix overflow of ending address of memory chunk
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Joonyoung Shim authored and torvalds committed Sep 11, 2013
1 parent eee87e1 commit 674470d
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 9 deletions.
4 changes: 2 additions & 2 deletions include/linux/genalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ struct gen_pool_chunk {
struct list_head next_chunk; /* next chunk in pool */
atomic_t avail;
phys_addr_t phys_addr; /* physical starting address of memory chunk */
unsigned long start_addr; /* starting address of memory chunk */
unsigned long end_addr; /* ending address of memory chunk */
unsigned long start_addr; /* start address of memory chunk */
unsigned long end_addr; /* end address of memory chunk (inclusive) */
unsigned long bits[0]; /* bitmap for allocating memory chunk */
};

Expand Down
19 changes: 12 additions & 7 deletions lib/genalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
#include <linux/of_address.h>
#include <linux/of_device.h>

static inline size_t chunk_size(const struct gen_pool_chunk *chunk)
{
return chunk->end_addr - chunk->start_addr + 1;
}

static int set_bits_ll(unsigned long *addr, unsigned long mask_to_set)
{
unsigned long val, nval;
Expand Down Expand Up @@ -188,7 +193,7 @@ int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phy

chunk->phys_addr = phys;
chunk->start_addr = virt;
chunk->end_addr = virt + size;
chunk->end_addr = virt + size - 1;
atomic_set(&chunk->avail, size);

spin_lock(&pool->lock);
Expand All @@ -213,7 +218,7 @@ phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr)

rcu_read_lock();
list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
if (addr >= chunk->start_addr && addr < chunk->end_addr) {
if (addr >= chunk->start_addr && addr <= chunk->end_addr) {
paddr = chunk->phys_addr + (addr - chunk->start_addr);
break;
}
Expand Down Expand Up @@ -242,7 +247,7 @@ void gen_pool_destroy(struct gen_pool *pool)
chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
list_del(&chunk->next_chunk);

end_bit = (chunk->end_addr - chunk->start_addr) >> order;
end_bit = chunk_size(chunk) >> order;
bit = find_next_bit(chunk->bits, end_bit, 0);
BUG_ON(bit < end_bit);

Expand Down Expand Up @@ -283,7 +288,7 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
if (size > atomic_read(&chunk->avail))
continue;

end_bit = (chunk->end_addr - chunk->start_addr) >> order;
end_bit = chunk_size(chunk) >> order;
retry:
start_bit = pool->algo(chunk->bits, end_bit, start_bit, nbits,
pool->data);
Expand Down Expand Up @@ -330,8 +335,8 @@ void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
nbits = (size + (1UL << order) - 1) >> order;
rcu_read_lock();
list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
if (addr >= chunk->start_addr && addr < chunk->end_addr) {
BUG_ON(addr + size > chunk->end_addr);
if (addr >= chunk->start_addr && addr <= chunk->end_addr) {
BUG_ON(addr + size - 1 > chunk->end_addr);
start_bit = (addr - chunk->start_addr) >> order;
remain = bitmap_clear_ll(chunk->bits, start_bit, nbits);
BUG_ON(remain);
Expand Down Expand Up @@ -400,7 +405,7 @@ size_t gen_pool_size(struct gen_pool *pool)

rcu_read_lock();
list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk)
size += chunk->end_addr - chunk->start_addr;
size += chunk_size(chunk);
rcu_read_unlock();
return size;
}
Expand Down

0 comments on commit 674470d

Please sign in to comment.