From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-dl1-f41.google.com (mail-dl1-f41.google.com [74.125.82.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D3E1C31E106 for ; Sun, 12 Apr 2026 02:27:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.41 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775960855; cv=none; b=Kk2pHdsVCbY0g3U08gbjCYqjfCnMvxnd3GCJmt7R6NThZBn+WoY6he+tLX5vGGDO9WDR4V22xSF9Zi8sliUHJjCfc0xuFC5Ntm3tXrLRdTIrbnOXP7YeN+1pS8S8bvLmrgajz/7hYHuUEPXPYFhmjCGHR00bpAn0kyz8J2PwI8o= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775960855; c=relaxed/simple; bh=AAo390ls7O+/gBJECTw5jRwQRLXWQ3hPBdBKg6IolV4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p3cA0MgE1aFdgUMdOOaXzOcxAm+CK9iQiJwTweEA0NFBAU3Ot5NISI9f/gUSJzEoRYuT8JtSk0fiO6KP5wwtmCG+iRVnwRETN5PsaJWguTa+sXvDm7zEvdJ+XlRFxsjmjRb+7ldG0Yxvz4nhQQvM6zToTq6Pix6dgMunimEItFw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nNBjl3YW; arc=none smtp.client-ip=74.125.82.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nNBjl3YW" Received: by mail-dl1-f41.google.com with SMTP id a92af1059eb24-1271257ae53so10742073c88.1 for ; Sat, 11 Apr 2026 19:27:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775960853; x=1776565653; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zcJawOFnHtEXmwU7Ts86h653zGkHF4K3zLulupg+ZRE=; b=nNBjl3YWut/7i64zvyTFb2vp4TZv+G3AcQXHtbHl3BelwVRE4MMJqiqXerE2Xr5SNo icTgQ90bUbog/e3pvTPWVYvKj0whMhV0pmDTZAD6q9UUB33a2/sTE/Bn83czQPkaG5Dk zOPENQbnsd8bX+Cxfqu/JfXlsDtc56VHpUkfPuLLmvM2clbXFlLbCwtFJzM5rYUMnPqL ilDSzUQUuN8/BbU2AlcWwdrueD0qyb/hwJVUCfZtBdTG3SZagdA1JoKjtpVbyJ24gmBI h4nGzWA1O7ww1JY379C4p95XrVH3BFIkZ7tMgAVMm3j1+rKEXj3BX4pgZdnfu4p7TRR5 vAmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775960853; x=1776565653; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zcJawOFnHtEXmwU7Ts86h653zGkHF4K3zLulupg+ZRE=; b=LGgCg3J7jys4t/tLOEJtYN/weqaUfLCdyBaZIn1pm1Prict4v/1/yACgiE25knACP0 8zyrV8HynaZtHe1aa9n04whvLUQNvTyvFSk6rMnNjmiywWdItf9xDHpZvFyktlVy6Qom CNvT2WFa6Zg5jMk+9dkEWQKjRahpowUN7wsZvSioAcspfiKDi/VkmtgDRWOeyQEifVXU ohEAcGIHwmkcOiEs9gmM2orbRlPrjQNjHRQT57vyQeGuLy2Ddsvm1mLLcJtjp4+bPipa em/wLGYtjjJDGt7z94ccXZLr8cnNu1VhyPbIfIdwQQZPeKerekzEGO4KHWqqWNTWFCVv YeIw== X-Forwarded-Encrypted: i=1; AJvYcCVdDIH5y3zABpywSEWX5AmZwMvirwG8zxgSG/0ytjaj486TLTh/xqdJb4BbiUvO439kG4G8LwrNa1fAMrs=@vger.kernel.org X-Gm-Message-State: AOJu0YxVbDrCwsiG3ZuAjzW5gDAtPiL8c5CBz43ubf0/yo+Dp1ggn3wD qLDrTskwG3j2KoI9AIXcm2VhhycryOmoOPrMd79/ncstOtSe7HelmE6q X-Gm-Gg: AeBDiesvom0dont6EzYV2xawtSnxmF1TXoajoWwRaXWuqbQ6tPD7U5pkQH9m8E0Jcw0 JoL3m9khbDY47/RkhXv5KkHwWeYfJcY7v0xH3mH79x7aoRjvv9ZYsqLWY1KHoBe1DSRgzvlyAuK SsV+6HKDNjwS99i63s5B/1UlFxcb81QGFtsfMS/ENtpyDn1jXjgpQ3O//IBaDMZeMP34M39Y1sN kBFpKDt9ohpfFsOgHXZIJ4VbNhrwt4kwSPTKfzLdrwwgdPKuY8DVp9D80gh3ZUuaebt/GYJeTCG TBkSsH7O+j3lWTO6th4B79Wte8riSA9uvFqAX+G0kGQhz2R17a9e6X431Cc0gOasVpePVN6I+F0 f1KjIrER2f3rOgIXnCGPYa0ZOziAcfoCcI4WDO3VVeU1SZPldjwMk78ZUk/MX1UAEoFrnAM5DWu LGueoFNXQtKiVWj50dtgozRjYw8osK+E92FRyW9rArrVQ4/uujHlFr8+dLtrWvBnyImHFAUJIzs fn4TPhQGg== X-Received: by 2002:a05:7022:517:b0:128:dab3:f528 with SMTP id a92af1059eb24-12c34e892c2mr4994180c88.8.1775960852898; Sat, 11 Apr 2026 19:27:32 -0700 (PDT) Received: from efaec68ba852.tailc0aff1.ts.net ([206.206.192.132]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-12c34352490sm8757860c88.0.2026.04.11.19.27.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 11 Apr 2026 19:27:32 -0700 (PDT) From: Weiming Shi To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko Cc: Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , John Fastabend , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Barret Rhoden , Emil Tsalapatis , bpf@vger.kernel.org, linux-kernel@vger.kernel.org, Xiang Mei , Weiming Shi Subject: [PATCH bpf v4 2/2] selftests/bpf: Add test for arena VMA use-after-free on fork Date: Sat, 11 Apr 2026 19:27:15 -0700 Message-ID: <20260412022714.1955495-4-bestswngs@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260412022714.1955495-2-bestswngs@gmail.com> References: <20260412022714.1955495-2-bestswngs@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add a selftest that reproduces the arena VMA use-after-free fixed in the previous commit. The test creates an arena, mmaps it, allocates pages via BPF, forks, has the parent munmap the arena, then has the child call bpf_arena_free_pages. Without the fix this triggers a KASAN slab-use-after-free in zap_page_range_single. Note: the UAF occurs entirely in kernel space and is not observable from userspace, so this test relies on KASAN to detect the bug. Without KASAN the test passes regardless of whether the fix is applied. Signed-off-by: Weiming Shi --- .../selftests/bpf/prog_tests/arena_fork.c | 80 +++++++++++++++++++ .../testing/selftests/bpf/progs/arena_fork.c | 41 ++++++++++ 2 files changed, 121 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/arena_fork.c create mode 100644 tools/testing/selftests/bpf/progs/arena_fork.c diff --git a/tools/testing/selftests/bpf/prog_tests/arena_fork.c b/tools/testing/selftests/bpf/prog_tests/arena_fork.c new file mode 100644 index 000000000000..0235884c5906 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/arena_fork.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +/* + * Test that forking a process with an arena mmap does not cause a + * use-after-free when the parent unmaps and the child frees arena pages. + */ +#include +#include +#include +#include + +#include "arena_fork.skel.h" + +void test_arena_fork(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + struct bpf_map_info info = {}; + __u32 info_len = sizeof(info); + struct arena_fork *skel; + long page_size; + size_t arena_sz; + void *arena_addr; + int arena_fd, ret, status; + pid_t pid; + + page_size = sysconf(_SC_PAGESIZE); + if (!ASSERT_GT(page_size, 0, "page_size")) + return; + + skel = arena_fork__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + arena_fd = bpf_map__fd(skel->maps.arena); + + /* libbpf mmaps the arena via initial_value */ + arena_addr = bpf_map__initial_value(skel->maps.arena, &arena_sz); + if (!ASSERT_OK_PTR(arena_addr, "arena_mmap")) + goto out; + + /* Get real arena byte size for munmap */ + bpf_map_get_info_by_fd(arena_fd, &info, &info_len); + arena_sz = (size_t)info.max_entries * page_size; + + /* Allocate 4 pages in the arena via BPF */ + ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.arena_alloc), + &opts); + if (!ASSERT_OK(ret, "alloc_run") || + !ASSERT_OK(opts.retval, "alloc_ret")) + goto out; + + /* Fault in a page so zap_pages has work to do */ + ((volatile char *)arena_addr)[0] = 'A'; + + /* Fork: child inherits the arena VMA */ + pid = fork(); + if (!ASSERT_GE(pid, 0, "fork")) + goto out; + + if (pid == 0) { + /* Child: parent will unmap first, then we free pages. */ + LIBBPF_OPTS(bpf_test_run_opts, child_opts); + int free_fd = bpf_program__fd(skel->progs.arena_free); + + usleep(200000); /* let parent munmap first */ + + ret = bpf_prog_test_run_opts(free_fd, &child_opts); + _exit(ret || child_opts.retval); + } + + /* Parent: unmap the arena */ + munmap(arena_addr, arena_sz); + + waitpid(pid, &status, 0); + ASSERT_TRUE(WIFEXITED(status), "child_exited"); + ASSERT_EQ(WEXITSTATUS(status), 0, "child_exit_code"); +out: + arena_fork__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/arena_fork.c b/tools/testing/selftests/bpf/progs/arena_fork.c new file mode 100644 index 000000000000..783c935a0af8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/arena_fork.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ +#include +#include +#include "bpf_arena_common.h" + +struct { + __uint(type, BPF_MAP_TYPE_ARENA); + __uint(map_flags, BPF_F_MMAPABLE); + __uint(max_entries, 16); /* number of pages */ +#ifdef __TARGET_ARCH_arm64 + __ulong(map_extra, 0x1ull << 32); /* start of mmap() region */ +#else + __ulong(map_extra, 0x1ull << 44); /* start of mmap() region */ +#endif +} arena SEC(".maps"); + +void __arena *alloc_addr; + +SEC("syscall") +int arena_alloc(void *ctx) +{ + void __arena *p; + + p = bpf_arena_alloc_pages(&arena, NULL, 4, NUMA_NO_NODE, 0); + if (!p) + return 1; + alloc_addr = p; + return 0; +} + +SEC("syscall") +int arena_free(void *ctx) +{ + if (!alloc_addr) + return 1; + bpf_arena_free_pages(&arena, alloc_addr, 4); + return 0; +} + +char _license[] SEC("license") = "GPL"; -- 2.43.0