From: Greg Kroah-Hartman Date: Thu, 7 Jul 2022 18:04:57 +0000 (+0200) Subject: 5.15-stable patches X-Git-Tag: v4.9.323~70 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dbe6fe30dfa99f5a96150d1df2630db2e28dc7be;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: mm-filemap-fix-uaf-in-find_lock_entries.patch mm-slub-add-missing-tid-updates-on-slab-deactivation.patch revert-selftests-bpf-add-test-for-bpf_timer-overwriting-crash.patch --- diff --git a/queue-5.15/mm-filemap-fix-uaf-in-find_lock_entries.patch b/queue-5.15/mm-filemap-fix-uaf-in-find_lock_entries.patch new file mode 100644 index 00000000000..1a0416b8aac --- /dev/null +++ b/queue-5.15/mm-filemap-fix-uaf-in-find_lock_entries.patch @@ -0,0 +1,80 @@ +From liushixin2@huawei.com Thu Jul 7 20:03:15 2022 +From: Liu Shixin +Date: Thu, 7 Jul 2022 10:09:38 +0800 +Subject: mm/filemap: fix UAF in find_lock_entries +To: Greg Kroah-Hartman , Andrew Morton , Matthew Wilcox , Jan Kara , William Kucharski , "Christoph Hellwig" +Cc: , , Liu Shixin +Message-ID: <20220707020938.2122198-1-liushixin2@huawei.com> + +From: Liu Shixin + +Release refcount after xas_set to fix UAF which may cause panic like this: + + page:ffffea000491fa40 refcount:1 mapcount:0 mapping:0000000000000000 index:0x1 pfn:0x1247e9 + head:ffffea000491fa00 order:3 compound_mapcount:0 compound_pincount:0 + memcg:ffff888104f91091 + flags: 0x2fffff80010200(slab|head|node=0|zone=2|lastcpupid=0x1fffff) +... +page dumped because: VM_BUG_ON_PAGE(PageTail(page)) + ------------[ cut here ]------------ + kernel BUG at include/linux/page-flags.h:632! + invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN + CPU: 1 PID: 7642 Comm: sh Not tainted 5.15.51-dirty #26 +... + Call Trace: + + __invalidate_mapping_pages+0xe7/0x540 + drop_pagecache_sb+0x159/0x320 + iterate_supers+0x120/0x240 + drop_caches_sysctl_handler+0xaa/0xe0 + proc_sys_call_handler+0x2b4/0x480 + new_sync_write+0x3d6/0x5c0 + vfs_write+0x446/0x7a0 + ksys_write+0x105/0x210 + do_syscall_64+0x35/0x80 + entry_SYSCALL_64_after_hwframe+0x44/0xae + RIP: 0033:0x7f52b5733130 +... + +This problem has been fixed on mainline by patch 6b24ca4a1a8d ("mm: Use +multi-index entries in the page cache") since it deletes the related code. + +Fixes: 5c211ba29deb ("mm: add and use find_lock_entries") +Signed-off-by: Liu Shixin +Acked-by: Matthew Wilcox (Oracle) +Signed-off-by: Greg Kroah-Hartman +--- + mm/filemap.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2090,7 +2090,11 @@ unsigned find_lock_entries(struct addres + + rcu_read_lock(); + while ((page = find_get_entry(&xas, end, XA_PRESENT))) { ++ unsigned long next_idx = xas.xa_index + 1; ++ + if (!xa_is_value(page)) { ++ if (PageTransHuge(page)) ++ next_idx = page->index + thp_nr_pages(page); + if (page->index < start) + goto put; + if (page->index + thp_nr_pages(page) - 1 > end) +@@ -2111,13 +2115,11 @@ unlock: + put: + put_page(page); + next: +- if (!xa_is_value(page) && PageTransHuge(page)) { +- unsigned int nr_pages = thp_nr_pages(page); +- ++ if (next_idx != xas.xa_index + 1) { + /* Final THP may cross MAX_LFS_FILESIZE on 32-bit */ +- xas_set(&xas, page->index + nr_pages); +- if (xas.xa_index < nr_pages) ++ if (next_idx < xas.xa_index) + break; ++ xas_set(&xas, next_idx); + } + } + rcu_read_unlock(); diff --git a/queue-5.15/mm-slub-add-missing-tid-updates-on-slab-deactivation.patch b/queue-5.15/mm-slub-add-missing-tid-updates-on-slab-deactivation.patch new file mode 100644 index 00000000000..d26f2a40ce9 --- /dev/null +++ b/queue-5.15/mm-slub-add-missing-tid-updates-on-slab-deactivation.patch @@ -0,0 +1,113 @@ +From eeaa345e128515135ccb864c04482180c08e3259 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 8 Jun 2022 20:22:05 +0200 +Subject: mm/slub: add missing TID updates on slab deactivation + +From: Jann Horn + +commit eeaa345e128515135ccb864c04482180c08e3259 upstream. + +The fastpath in slab_alloc_node() assumes that c->slab is stable as long as +the TID stays the same. However, two places in __slab_alloc() currently +don't update the TID when deactivating the CPU slab. + +If multiple operations race the right way, this could lead to an object +getting lost; or, in an even more unlikely situation, it could even lead to +an object being freed onto the wrong slab's freelist, messing up the +`inuse` counter and eventually causing a page to be freed to the page +allocator while it still contains slab objects. + +(I haven't actually tested these cases though, this is just based on +looking at the code. Writing testcases for this stuff seems like it'd be +a pain...) + +The race leading to state inconsistency is (all operations on the same CPU +and kmem_cache): + + - task A: begin do_slab_free(): + - read TID + - read pcpu freelist (==NULL) + - check `slab == c->slab` (true) + - [PREEMPT A->B] + - task B: begin slab_alloc_node(): + - fastpath fails (`c->freelist` is NULL) + - enter __slab_alloc() + - slub_get_cpu_ptr() (disables preemption) + - enter ___slab_alloc() + - take local_lock_irqsave() + - read c->freelist as NULL + - get_freelist() returns NULL + - write `c->slab = NULL` + - drop local_unlock_irqrestore() + - goto new_slab + - slub_percpu_partial() is NULL + - get_partial() returns NULL + - slub_put_cpu_ptr() (enables preemption) + - [PREEMPT B->A] + - task A: finish do_slab_free(): + - this_cpu_cmpxchg_double() succeeds() + - [CORRUPT STATE: c->slab==NULL, c->freelist!=NULL] + +From there, the object on c->freelist will get lost if task B is allowed to +continue from here: It will proceed to the retry_load_slab label, +set c->slab, then jump to load_freelist, which clobbers c->freelist. + +But if we instead continue as follows, we get worse corruption: + + - task A: run __slab_free() on object from other struct slab: + - CPU_PARTIAL_FREE case (slab was on no list, is now on pcpu partial) + - task A: run slab_alloc_node() with NUMA node constraint: + - fastpath fails (c->slab is NULL) + - call __slab_alloc() + - slub_get_cpu_ptr() (disables preemption) + - enter ___slab_alloc() + - c->slab is NULL: goto new_slab + - slub_percpu_partial() is non-NULL + - set c->slab to slub_percpu_partial(c) + - [CORRUPT STATE: c->slab points to slab-1, c->freelist has objects + from slab-2] + - goto redo + - node_match() fails + - goto deactivate_slab + - existing c->freelist is passed into deactivate_slab() + - inuse count of slab-1 is decremented to account for object from + slab-2 + +At this point, the inuse count of slab-1 is 1 lower than it should be. +This means that if we free all allocated objects in slab-1 except for one, +SLUB will think that slab-1 is completely unused, and may free its page, +leading to use-after-free. + +Fixes: c17dda40a6a4e ("slub: Separate out kmem_cache_cpu processing from deactivate_slab") +Fixes: 03e404af26dc2 ("slub: fast release on full slab") +Cc: stable@vger.kernel.org +Signed-off-by: Jann Horn +Acked-by: Christoph Lameter +Acked-by: David Rientjes +Reviewed-by: Muchun Song +Tested-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> +Signed-off-by: Vlastimil Babka +Link: https://lore.kernel.org/r/20220608182205.2945720-1-jannh@google.com +Signed-off-by: Greg Kroah-Hartman +--- + mm/slub.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -2935,6 +2935,7 @@ redo: + + if (!freelist) { + c->page = NULL; ++ c->tid = next_tid(c->tid); + local_unlock_irqrestore(&s->cpu_slab->lock, flags); + stat(s, DEACTIVATE_BYPASS); + goto new_slab; +@@ -2967,6 +2968,7 @@ deactivate_slab: + freelist = c->freelist; + c->page = NULL; + c->freelist = NULL; ++ c->tid = next_tid(c->tid); + local_unlock_irqrestore(&s->cpu_slab->lock, flags); + deactivate_slab(s, page, freelist); + diff --git a/queue-5.15/revert-selftests-bpf-add-test-for-bpf_timer-overwriting-crash.patch b/queue-5.15/revert-selftests-bpf-add-test-for-bpf_timer-overwriting-crash.patch new file mode 100644 index 00000000000..9cf19b1baa2 --- /dev/null +++ b/queue-5.15/revert-selftests-bpf-add-test-for-bpf_timer-overwriting-crash.patch @@ -0,0 +1,126 @@ +From po-hsu.lin@canonical.com Thu Jul 7 20:04:15 2022 +From: Po-Hsu Lin +Date: Thu, 7 Jul 2022 17:42:07 +0800 +Subject: Revert "selftests/bpf: Add test for bpf_timer overwriting crash" +To: stable@vger.kernel.org, gregkh@linuxfoundation.org +Cc: memxor@gmail.com, linux-kernel@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, kafai@fb.com, songliubraving@fb.com, yhs@fb.com, john.fastabend@gmail.com, kpsingh@kernel.org, shuah@kernel.org, bpf@vger.kernel.org, po-hsu.lin@canonical.com +Message-ID: <20220707094207.229875-2-po-hsu.lin@canonical.com> + +From: Po-Hsu Lin + +This reverts commit b0028e1cc1faf2e5d88ad4065590aca90d650182 which is +commit a7e75016a0753c24d6c995bc02501ae35368e333 upstream. + +It will break the bpf self-tests build with: +progs/timer_crash.c:8:19: error: field has incomplete type 'struct bpf_timer' + struct bpf_timer timer; + ^ +/home/ubuntu/linux/tools/testing/selftests/bpf/tools/include/bpf/bpf_helper_defs.h:39:8: +note: forward declaration of 'struct bpf_timer' +struct bpf_timer; + ^ +1 error generated. + +This test can only be built with 5.17 and newer kernels. + +Signed-off-by: Po-Hsu Lin +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/prog_tests/timer_crash.c | 32 ----------- + tools/testing/selftests/bpf/progs/timer_crash.c | 54 ------------------- + 2 files changed, 86 deletions(-) + delete mode 100644 tools/testing/selftests/bpf/prog_tests/timer_crash.c + delete mode 100644 tools/testing/selftests/bpf/progs/timer_crash.c + +--- a/tools/testing/selftests/bpf/prog_tests/timer_crash.c ++++ /dev/null +@@ -1,32 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-#include +-#include "timer_crash.skel.h" +- +-enum { +- MODE_ARRAY, +- MODE_HASH, +-}; +- +-static void test_timer_crash_mode(int mode) +-{ +- struct timer_crash *skel; +- +- skel = timer_crash__open_and_load(); +- if (!ASSERT_OK_PTR(skel, "timer_crash__open_and_load")) +- return; +- skel->bss->pid = getpid(); +- skel->bss->crash_map = mode; +- if (!ASSERT_OK(timer_crash__attach(skel), "timer_crash__attach")) +- goto end; +- usleep(1); +-end: +- timer_crash__destroy(skel); +-} +- +-void test_timer_crash(void) +-{ +- if (test__start_subtest("array")) +- test_timer_crash_mode(MODE_ARRAY); +- if (test__start_subtest("hash")) +- test_timer_crash_mode(MODE_HASH); +-} +--- a/tools/testing/selftests/bpf/progs/timer_crash.c ++++ /dev/null +@@ -1,54 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +- +-#include +-#include +-#include +- +-struct map_elem { +- struct bpf_timer timer; +- struct bpf_spin_lock lock; +-}; +- +-struct { +- __uint(type, BPF_MAP_TYPE_ARRAY); +- __uint(max_entries, 1); +- __type(key, int); +- __type(value, struct map_elem); +-} amap SEC(".maps"); +- +-struct { +- __uint(type, BPF_MAP_TYPE_HASH); +- __uint(max_entries, 1); +- __type(key, int); +- __type(value, struct map_elem); +-} hmap SEC(".maps"); +- +-int pid = 0; +-int crash_map = 0; /* 0 for amap, 1 for hmap */ +- +-SEC("fentry/do_nanosleep") +-int sys_enter(void *ctx) +-{ +- struct map_elem *e, value = {}; +- void *map = crash_map ? (void *)&hmap : (void *)&amap; +- +- if (bpf_get_current_task_btf()->tgid != pid) +- return 0; +- +- *(void **)&value = (void *)0xdeadcaf3; +- +- bpf_map_update_elem(map, &(int){0}, &value, 0); +- /* For array map, doing bpf_map_update_elem will do a +- * check_and_free_timer_in_array, which will trigger the crash if timer +- * pointer was overwritten, for hmap we need to use bpf_timer_cancel. +- */ +- if (crash_map == 1) { +- e = bpf_map_lookup_elem(map, &(int){0}); +- if (!e) +- return 0; +- bpf_timer_cancel(&e->timer); +- } +- return 0; +-} +- +-char _license[] SEC("license") = "GPL"; diff --git a/queue-5.15/series b/queue-5.15/series new file mode 100644 index 00000000000..aa8af5d2a01 --- /dev/null +++ b/queue-5.15/series @@ -0,0 +1,3 @@ +mm-slub-add-missing-tid-updates-on-slab-deactivation.patch +mm-filemap-fix-uaf-in-find_lock_entries.patch +revert-selftests-bpf-add-test-for-bpf_timer-overwriting-crash.patch