summaryrefslogtreecommitdiff
path: root/mm/slub.c
diff options
context:
space:
mode:
authorVladimir Davydov <vdavydov@parallels.com>2014-07-10 10:25:31 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2014-07-10 10:25:31 +1000
commit2691c829dbd9d31ef2693fd0278af380001c923a (patch)
tree5746623d8a14de15f894abe02c3c0cc601cefe80 /mm/slub.c
parent42f01560b64a26b4d662cc441310d3c96555ca04 (diff)
slub: make slab_free non-preemptable
Since per memcg cache destruction is scheduled when the last slab is freed, to avoid use-after-free in kmem_cache_free we should either rearrange code in kmem_cache_free so that it won't dereference the cache ptr after freeing the object, or wait for all kmem_cache_free's to complete before proceeding to cache destruction. The former approach isn't a good option from the future development point of view, because every modifications to kmem_cache_free must be done with great care then. Hence we should provide a method to wait for all currently executing kmem_cache_free's to finish. This patch makes SLUB's implementation of kmem_cache_free non-preemptable. As a result, synchronize_sched() will work as a barrier against kmem_cache_free's in flight, so that issuing it before cache destruction will protect us against the use-after-free. This won't affect performance of kmem_cache_free, because we already disable preemption there, and this patch only moves preempt_enable to the end of the function. Neither should it affect the system latency, because kmem_cache_free is extremely short, even in its slow path. SLAB's version of kmem_cache_free already proceeds with irqs disabled, so we only add a comment explaining why it's necessary for kmemcg there. Signed-off-by: Vladimir Davydov <vdavydov@parallels.com> Acked-by: Christoph Lameter <cl@linux.com> Cc: Michal Hocko <mhocko@suse.cz> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'mm/slub.c')
-rw-r--r--mm/slub.c12
1 files changed, 6 insertions, 6 deletions
diff --git a/mm/slub.c b/mm/slub.c
index d4130db7c918..15f98092dddd 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2640,18 +2640,17 @@ static __always_inline void slab_free(struct kmem_cache *s,
slab_free_hook(s, x);
-redo:
/*
- * Determine the currently cpus per cpu slab.
- * The cpu may change afterward. However that does not matter since
- * data is retrieved via this pointer. If we are on the same cpu
- * during the cmpxchg then the free will succedd.
+ * We could make this function fully preemptable, but then we wouldn't
+ * have a method to wait for all currently executing kfree's to finish,
+ * which is necessary to avoid use-after-free on per memcg cache
+ * destruction.
*/
preempt_disable();
+redo:
c = this_cpu_ptr(s->cpu_slab);
tid = c->tid;
- preempt_enable();
if (likely(page == c->page)) {
set_freepointer(s, object, c->freelist);
@@ -2668,6 +2667,7 @@ redo:
} else
__slab_free(s, page, x, addr);
+ preempt_enable();
}
void kmem_cache_free(struct kmem_cache *s, void *x)