lkml.org 
[lkml]   [2020]   [Oct]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.9 390/757] mm: memcg/slab: fix racy access to page->mem_cgroup in mem_cgroup_from_obj()
    Date
    From: Roman Gushchin <guro@fb.com>

    [ Upstream commit 19b629c9795bfe67bf77be8fb611b84424b56d91 ]

    mem_cgroup_from_obj() checks the lowest bit of the page->mem_cgroup
    pointer to determine if the page has an attached obj_cgroup vector instead
    of a regular memcg pointer. If it's not set, it simple returns the
    page->mem_cgroup value as a struct mem_cgroup pointer.

    The commit 10befea91b61 ("mm: memcg/slab: use a single set of kmem_caches
    for all allocations") changed the moment when this bit is set: if
    previously it was set on the allocation of the slab page, now it can be
    set well after, when the first accounted object is allocated on this page.

    It opened a race: if page->mem_cgroup is set concurrently after the first
    page_has_obj_cgroups(page) check, a pointer to the obj_cgroups array can
    be returned as a memory cgroup pointer.

    A simple check for page->mem_cgroup pointer for NULL before the
    page_has_obj_cgroups() check fixes the race. Indeed, if the pointer is
    not NULL, it's either a simple mem_cgroup pointer or a pointer to
    obj_cgroup vector. The pointer can be asynchronously changed from NULL to
    (obj_cgroup_vec | 0x1UL), but can't be changed from a valid memcg pointer
    to objcg vector or back.

    If the object passed to mem_cgroup_from_obj() is a slab object and
    page->mem_cgroup is NULL, it means that the object is not accounted, so
    the function must return NULL.

    I've discovered the race looking at the code, so far I haven't seen it in
    the wild.

    Fixes: 10befea91b61 ("mm: memcg/slab: use a single set of kmem_caches for all allocations")
    Signed-off-by: Roman Gushchin <guro@fb.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Reviewed-by: Shakeel Butt <shakeelb@google.com>
    Cc: Johannes Weiner <hannes@cmpxchg.org>
    Cc: Vlastimil Babka <vbabka@suse.cz>
    Link: https://lkml.kernel.org/r/20200910022435.2773735-1-guro@fb.com
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    mm/memcontrol.c | 11 +++++++++++
    1 file changed, 11 insertions(+)

    diff --git a/mm/memcontrol.c b/mm/memcontrol.c
    index 6877c765b8d03..b9688a4b1d550 100644
    --- a/mm/memcontrol.c
    +++ b/mm/memcontrol.c
    @@ -2887,6 +2887,17 @@ struct mem_cgroup *mem_cgroup_from_obj(void *p)

    page = virt_to_head_page(p);

    + /*
    + * If page->mem_cgroup is set, it's either a simple mem_cgroup pointer
    + * or a pointer to obj_cgroup vector. In the latter case the lowest
    + * bit of the pointer is set.
    + * The page->mem_cgroup pointer can be asynchronously changed
    + * from NULL to (obj_cgroup_vec | 0x1UL), but can't be changed
    + * from a valid memcg pointer to objcg vector or back.
    + */
    + if (!page->mem_cgroup)
    + return NULL;
    +
    /*
    * Slab objects are accounted individually, not per-page.
    * Memcg membership data for each individual object is saved in
    --
    2.25.1


    \
     
     \ /
      Last update: 2020-10-27 16:48    [W:4.021 / U:1.024 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site