summaryrefslogtreecommitdiff
path: root/mm/percpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/percpu.c')
-rw-r--r--mm/percpu.c15
1 files changed, 14 insertions, 1 deletions
diff --git a/mm/percpu.c b/mm/percpu.c
index 81462ce5866e..a2107bdebf0b 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1279,12 +1279,16 @@ static int pcpu_free_area(struct pcpu_chunk *chunk, int off)
int bit_off, bits, end, oslot, freed;
lockdep_assert_held(&pcpu_lock);
- pcpu_stats_area_dealloc(chunk);
oslot = pcpu_chunk_slot(chunk);
bit_off = off / PCPU_MIN_ALLOC_SIZE;
+ /* check invalid free */
+ if (!test_bit(bit_off, chunk->alloc_map) ||
+ !test_bit(bit_off, chunk->bound_map))
+ return 0;
+
/* find end index */
end = find_next_bit(chunk->bound_map, pcpu_chunk_map_bits(chunk),
bit_off + 1);
@@ -1303,6 +1307,8 @@ static int pcpu_free_area(struct pcpu_chunk *chunk, int off)
pcpu_chunk_relocate(chunk, oslot);
+ pcpu_stats_area_dealloc(chunk);
+
return freed;
}
@@ -2242,6 +2248,13 @@ void free_percpu(void __percpu *ptr)
spin_lock_irqsave(&pcpu_lock, flags);
size = pcpu_free_area(chunk, off);
+ if (size == 0) {
+ spin_unlock_irqrestore(&pcpu_lock, flags);
+
+ /* invalid percpu free */
+ WARN_ON_ONCE(1);
+ return;
+ }
pcpu_alloc_tag_free_hook(chunk, off, size);