From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-yw1-f178.google.com (mail-yw1-f178.google.com [209.85.128.178]) (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 C7D0A329C40 for ; Thu, 23 Apr 2026 15:22:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.178 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776957778; cv=none; b=cVLJmhFSUtoxHW7NOUsvouWKVx9J8Wxm5CApdH/s01QqTWq171Uj+32LCqg0NUuwhrnhj4UsdpiFNDOkfWVQFGtRQ74wV5p5i+o0tE74HHmLkGepshHVa02bcDjznjHycJYU3UNR21x45LZWE2K5lmCNPuiu7CkOh4ZMBC5m0tk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776957778; c=relaxed/simple; bh=ZqaGxIuW6V/rh+lTGW+6ZjonSTAolBwtZOm4082aV1A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EH7RRCL5EMBkK2gCZ5DTpLfFHNSaO3XM/jPRTFoFIf3zWxdYaqYIoFltr6TMtqGHGZv1W96ipHqdBXsEP4hPoKF68mrynJPGBUUNvAm5lzhVHBlTWamPuQo6Ml3ijojr7bZG3Wd5k7bIfO/xbowT9OjYPZPG6ZHTAcL6Hr2Az1g= 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=qIgv5rM3; arc=none smtp.client-ip=209.85.128.178 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="qIgv5rM3" Received: by mail-yw1-f178.google.com with SMTP id 00721157ae682-7927261a3acso61056557b3.0 for ; Thu, 23 Apr 2026 08:22:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776957776; x=1777562576; 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=/pBstuHQcp3jfMXm2fRsgUU8hmUx2M3sdRetuFefRl4=; b=qIgv5rM3SARIHIpr8Mqek/Ye+yX2Dj4emliKTfTZK9GhhMpa73uuwlGyGHp2wlTDFp rl1taxxM42o3q7l6yLQQCk8JgX5VFsTRS8fj8H8rNw7J9RKj4TgKGpmNpQTqzmcco83u Z5DgJvjiarUx9CoMxRlEjolm/mWoqMo+NzWSF2eYU1xB5A52Z4s4yv2fi+oijMBDA9pr KNe83dwNliuoEQPzqiYKfWY6Lcksuhrjy094WhyCkE0ZVOuNf3731QwyYTV724aPSURc ShWCI9cDYkl0+0Uh/yvMzGLQMC6GbZSlkyDC6wLql+PBZom4mvJVbXkTqaESs8ftie4a 8ZDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776957776; x=1777562576; 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=/pBstuHQcp3jfMXm2fRsgUU8hmUx2M3sdRetuFefRl4=; b=A4iU/j+ba1jCx5KIH2RMsut77PnQuE8k3NfgL+dEhbX9CQhDUxAJL5W5PuFkowzXs0 fJE2y66N26U0ZS7nd2O85vPS4z+8s591SMxxyEqEEbMR6ujE8Ru1KNh1GlYwfSueXsmF ed8KJoqT/cp2W1UjjkW1K2aXLTnGvhvD5O7uyvpXCMzJA+cGs8Uq75COaYiL33+1JwHA S2153D28iS4+HNsGhUeJ0w0zssV7yyslpso2FSs4tB+esoh5czYzJvVc3mIfksAUaR7x ZUWoSbMaNJ4DUcBKrork1HLIyyEkkYYZhL2Cu7Fof/4VBZgcPmM9OJHl/oMPI5bIqd73 +zhQ== X-Forwarded-Encrypted: i=1; AFNElJ90Lob5Gl4j6ZGNEA4x60QVTFeiI4iEC6wmnvE6Z6Gj8Y902FD8Eu6mWAZiZi8CBIBUe5k=@vger.kernel.org X-Gm-Message-State: AOJu0YzdD2e+S07Z5tBBkueS+ZW5BQJiHxyeQVhdinsjDq9XH/vZ50Dx nZrzCX7kvyKVbKxzwKceB5X9KK7M8mDFROvQ6kLiz/bKo67UZ5TOlYvT X-Gm-Gg: AeBDieunj60kUXzcuP6zTytQ511UNgDY86FYzFESIaQmp9guwvD+3brzIJXqSL9Dxp9 0kz1JLZavCabMH1Ks62sZJhX242yvRyA5OiPEnExOWk1yz5+TNAuKAeLXQsef3MHW+m9K6+ojNj pY98xqt1fz8dR2tTV5RalSQBnvpem5KvdTQISRKcG0xzynWa7P3nLLYjyQB78uwgAplrt6X/9+Q BfN7qaf/ye0CHeZEfu0NRNcHWeuQnuMTsH3vQJWxPR3UJbsX/0uldyiCtCrND1SN456u6Or82Dp l+qoUQ2wVcvxqjdAzpNRVbbVghmjJk3R7Wh+zjsYm5Aej6VOIorwqLDka82m+WAGTFc9rLSV4m6 wBlmqiMg+IsXLn2KUP37Ni2lmYE39KqZNmwTAfDOVU6zjDNNNf/VpGBB2w61suu21RcyepERWMi UYCmKVbPaolNu8bWxp48SoDL0MjNjHIzXs86zUWWLTn83JheuwvvQ2TZEenNvfAzti9yD/n1EDb q13BY0h2As= X-Received: by 2002:a05:690c:e686:20b0:7ba:a726:58da with SMTP id 00721157ae682-7baa7266e28mr188766867b3.24.1776957775508; Thu, 23 Apr 2026 08:22:55 -0700 (PDT) Received: from zenbox.prizrak.me ([2600:1700:18fb:6011:b41c:424e:7501:d0c7]) by smtp.gmail.com with ESMTPSA id 00721157ae682-7b9ee89b88bsm84293957b3.9.2026.04.23.08.22.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Apr 2026 08:22:55 -0700 (PDT) From: Justin Suess To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, memxor@gmail.com Cc: martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, jolsa@kernel.org, bpf@vger.kernel.org, mic@digikod.net, Justin Suess Subject: [PATCH bpf-next v2 2/2] selftests/bpf: Add test for map-stored struct file kptrs Date: Thu, 23 Apr 2026 11:22:39 -0400 Message-ID: <20260423152239.4140627-3-utilityemal77@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260423152239.4140627-1-utilityemal77@gmail.com> References: <20260423152239.4140627-1-utilityemal77@gmail.com> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add tests for map-stored struct file referenced kptrs. Add a test storing a referenced kptr in a map. The referenced pointer is acquired by get_task_exe_file in this case. Add a test that verifies that the struct file reference remains valid when the original file descriptor is closed, by checking the extended attributes on it after completion. Trigger this validation manually by hooking file_open and opening dev null. Validation is done with xattrs. Add a test verifying that attempting to insert unreferenced struct file pointers into the map is rejected by the verifier. Add fail tests for reference counting with the struct file objects. Cc: Song Liu Cc: Kumar Kartikeya Dwivedi Signed-off-by: Justin Suess --- Changes in v2: - Add success verifier tests per Song Liu's suggestion. - Remove close_fd per Song Liu's suggestion. - Fix typos in commit per Sashiko. - Fix subtest ordering per Sashiko. - Fix comments per Sashiko. .../bpf/prog_tests/refcounted_kptr_file.c | 249 ++++++++++++++++++ .../bpf/progs/refcounted_kptr_file.c | 158 +++++++++++ .../bpf/progs/refcounted_kptr_file_fail.c | 141 ++++++++++ .../bpf/progs/refcounted_kptr_file_success.c | 61 +++++ 4 files changed, 609 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/refcounted_kptr_file.c create mode 100644 tools/testing/selftests/bpf/progs/refcounted_kptr_file.c create mode 100644 tools/testing/selftests/bpf/progs/refcounted_kptr_file_fail.c create mode 100644 tools/testing/selftests/bpf/progs/refcounted_kptr_file_success.c diff --git a/tools/testing/selftests/bpf/prog_tests/refcounted_kptr_file.c b/tools/testing/selftests/bpf/prog_tests/refcounted_kptr_file.c new file mode 100644 index 000000000000..1f2586a7af93 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/refcounted_kptr_file.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "refcounted_kptr_file_fail.skel.h" +#include "refcounted_kptr_file.skel.h" +#include "refcounted_kptr_file_success.skel.h" + +static const char shell_path[] = "/bin/sh"; +static const char xattr_name[] = "user.kptr_ref"; +static const char xattr_value[] = "kptr-live"; + +static int write_full(int fd, const void *buf, size_t len) +{ + const char *pos = buf; + + while (len) { + ssize_t written; + + written = write(fd, pos, len); + if (written < 0) { + if (errno == EINTR) + continue; + return -errno; + } + + pos += written; + len -= written; + } + + return 0; +} + +static int copy_file(const char *src_path, const char *dst_path, mode_t mode) +{ + char buf[4096]; + int src_fd = -1, dst_fd = -1; + int err = 0; + + src_fd = open(src_path, O_RDONLY | O_CLOEXEC); + if (src_fd < 0) + return -errno; + + dst_fd = open(dst_path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, + mode & 0777); + if (dst_fd < 0) { + err = -errno; + goto out; + } + + while (1) { + ssize_t bytes_read; + + bytes_read = read(src_fd, buf, sizeof(buf)); + if (bytes_read < 0) { + if (errno == EINTR) + continue; + err = -errno; + goto out; + } + + if (!bytes_read) + break; + + err = write_full(dst_fd, buf, bytes_read); + if (err) { + unlink(dst_path); + goto out; + } + } + +out: + err = close(src_fd) ?: err; + err = close(dst_fd) ?: err; + return err; +} + +static bool prepare_tagged_shell(char *temp_dir, size_t temp_dir_sz, + char *shell_copy, size_t shell_copy_sz) +{ + struct stat st; + int err; + + if (!ASSERT_LT(snprintf(temp_dir, temp_dir_sz, + "./refcounted_kptr_file.XXXXXX"), + (int)temp_dir_sz, "temp_dir_template")) + return false; + + if (!ASSERT_OK_PTR(mkdtemp(temp_dir), "mkdtemp")) + return false; + + if (!ASSERT_LT(snprintf(shell_copy, shell_copy_sz, "%s/sh", temp_dir), + (int)shell_copy_sz, "shell_copy_path")) + goto err_rmdir; + + if (!ASSERT_OK(stat(shell_path, &st), "stat_shell")) + goto err_rmdir; + + err = copy_file(shell_path, shell_copy, st.st_mode); + if (!ASSERT_OK(err, "copy_shell")) + goto err_unlink; + + err = setxattr(shell_copy, xattr_name, xattr_value, sizeof(xattr_value), 0); + if (err && errno == EOPNOTSUPP) { + printf("%s:SKIP:filesystem does not support user xattr (%d)\n", + __func__, errno); + test__skip(); + goto err_unlink; + } + + if (!ASSERT_OK(err, "setxattr_shell")) + goto err_unlink; + + return true; + +err_unlink: + unlink(shell_copy); +err_rmdir: + rmdir(temp_dir); + return false; +} + +static void run_refcounted_file_kptr_success(void) +{ + struct refcounted_kptr_file *skel; + char shell_copy[PATH_MAX] = {}; + char temp_dir[PATH_MAX] = {}; + int pipefd[2] = { -1, -1 }; + int status; + pid_t child_pid = -1; + int err, fd = -1; + + skel = refcounted_kptr_file__open(); + if (!ASSERT_OK_PTR(skel, "refcounted_kptr_file__open")) + return; + + err = refcounted_kptr_file__load(skel); + if (!ASSERT_OK(err, "refcounted_kptr_file__load")) + goto out; + + err = refcounted_kptr_file__attach(skel); + if (!ASSERT_OK(err, "refcounted_kptr_file__attach")) + goto out; + + if (!prepare_tagged_shell(temp_dir, sizeof(temp_dir), shell_copy, + sizeof(shell_copy))) + goto out; + + if (!ASSERT_OK(pipe2(pipefd, O_CLOEXEC), "pipe2")) + goto out; + + child_pid = fork(); + if (!ASSERT_GT(child_pid, -1, "fork")) + goto out; + + if (child_pid == 0) { + char sync; + + close(pipefd[1]); + if (read(pipefd[0], &sync, 1) != 1) + _exit(127); + close(pipefd[0]); + execl(shell_copy, shell_copy, "-c", ": bss->file_kptr_insert_pid = child_pid; + err = write_full(pipefd[1], "1", 1); + if (!ASSERT_OK(err, "start_child")) + goto out; + close(pipefd[1]); + pipefd[1] = -1; + + if (!ASSERT_EQ(waitpid(child_pid, &status, 0), child_pid, "waitpid")) + goto out; + child_pid = -1; + if (!ASSERT_TRUE(WIFEXITED(status), "child_exited")) + goto out; + if (!ASSERT_EQ(WEXITSTATUS(status), 0, "child_status")) + goto out; + + skel->bss->file_kptr_verify_pid = getpid(); + /* + * The child is gone at this point. Reopening an unrelated file triggers a + * second file_open hook where the BPF program validates the stashed ref. + * Our test op for the ref validity is reading the xattrs we set earlier. + */ + fd = open("/dev/null", O_RDONLY | O_CLOEXEC); + if (!ASSERT_GE(fd, 0, "open_dev_null")) + goto out; + close(fd); + fd = -1; + + ASSERT_EQ(skel->bss->file_kptr_err, 0, "file_kptr_err"); + ASSERT_EQ(skel->bss->file_kptr_inserted, 1, "file_kptr_inserted"); + ASSERT_EQ(skel->bss->file_kptr_verified, 1, "file_kptr_verified"); + ASSERT_EQ(skel->bss->file_kptr_xattr_ret, sizeof(xattr_value), + "file_kptr_xattr_ret"); + ASSERT_EQ(strcmp(skel->bss->file_kptr_value, xattr_value), 0, + "file_kptr_value"); + +out: + close(fd); + close(pipefd[0]); + close(pipefd[1]); + if (child_pid > 0) + (void)waitpid(child_pid, NULL, 0); + if (shell_copy[0]) + unlink(shell_copy); + if (temp_dir[0]) + rmdir(temp_dir); + refcounted_kptr_file__destroy(skel); +} + +void test_refcounted_kptr_file_runtime(void) +{ + if (test__start_subtest("holds_ref_after_close")) + run_refcounted_file_kptr_success(); +} + +void test_refcounted_kptr_file(void) +{ + RUN_TESTS(refcounted_kptr_file_success); + + RUN_TESTS(refcounted_kptr_file_fail); + + RUN_TESTS(refcounted_kptr_file); +} diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_file.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_file.c new file mode 100644 index 000000000000..510ea1107791 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_file.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include "bpf_misc.h" +#include "bpf_kfuncs.h" +#include "bpf_experimental.h" + +struct file_map_value { + struct file __kptr * file; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct file_map_value); + __uint(max_entries, 1); +} stashed_files SEC(".maps"); + +static const char xattr_name[] = "user.kptr_ref"; +static const char expected_value[] = "kptr-live"; + +char file_kptr_probe_value[32]; +int file_kptr_insert_pid; +int file_kptr_verify_pid; +int file_kptr_inserted; +int file_kptr_verified; +int file_kptr_err; +int file_kptr_xattr_ret; +char file_kptr_value[32]; + +SEC("lsm.s/file_open") +__description("file_kptr: insert referenced file") +__success +int insert_file_kptr(struct file *ctx_file) +{ + struct bpf_dynptr value_ptr; + struct file_map_value *mapval; + struct task_struct *task; + struct file *file, *old; + int ret; + int zero = 0; + + (void)ctx_file; + + if ((__u32)(bpf_get_current_pid_tgid() >> 32) != (__u32)file_kptr_insert_pid) + return 0; + + if (file_kptr_inserted) + return 0; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) { + file_kptr_err = 1; + return 0; + } + + task = bpf_get_current_task_btf(); + file = bpf_get_task_exe_file(task); + if (!file) { + file_kptr_err = 2; + return 0; + } + + /* + * Exec can open multiple files while the new image is being installed. + * Only stash the child's final executable, which we identify by the test + * xattr. + */ + ret = bpf_dynptr_from_mem(file_kptr_probe_value, + sizeof(file_kptr_probe_value), 0, + &value_ptr); + if (ret) { + file_kptr_err = 8; + bpf_put_file(file); + return 0; + } + + ret = bpf_get_file_xattr(file, xattr_name, &value_ptr); + if (ret != sizeof(expected_value) || + bpf_strncmp(file_kptr_probe_value, sizeof(expected_value), + expected_value)) { + bpf_put_file(file); + return 0; + } + + old = bpf_kptr_xchg(&mapval->file, file); + if (old) + bpf_put_file(old); + + file_kptr_inserted = 1; + return 0; +} + +SEC("lsm.s/file_open") +__description("file_kptr: verify referenced file") +__success +int verify_file_kptr(struct file *ctx_file) +{ + struct bpf_dynptr value_ptr; + struct file_map_value *mapval; + struct file *file; + int zero = 0; + int ret; + + (void)ctx_file; + + if ((__u32)(bpf_get_current_pid_tgid() >> 32) != (__u32)file_kptr_verify_pid) + return 0; + + if (!file_kptr_inserted || file_kptr_verified) + return 0; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) { + file_kptr_err = 3; + return 0; + } + + /* + * Pull the file out of the map to get a referenced pointer for the xattr + * kfunc and to drop the map's last reference once verification completes. + */ + file = bpf_kptr_xchg(&mapval->file, NULL); + if (!file) { + file_kptr_err = 4; + return 0; + } + + ret = bpf_dynptr_from_mem(file_kptr_value, sizeof(file_kptr_value), 0, + &value_ptr); + if (ret) { + file_kptr_err = 5; + bpf_put_file(file); + return 0; + } + + ret = bpf_get_file_xattr(file, xattr_name, &value_ptr); + file_kptr_xattr_ret = ret; + if (ret != sizeof(expected_value)) { + file_kptr_err = 6; + bpf_put_file(file); + return 0; + } + + if (bpf_strncmp(file_kptr_value, sizeof(expected_value), expected_value)) { + file_kptr_err = 7; + bpf_put_file(file); + return 0; + } + + file_kptr_verified = 1; + bpf_put_file(file); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_file_fail.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_file_fail.c new file mode 100644 index 000000000000..67fea792960d --- /dev/null +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_file_fail.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include "bpf_misc.h" +#include "bpf_experimental.h" + +struct file_map_value { + struct file __kptr * file; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct file_map_value); + __uint(max_entries, 1); +} stashed_files SEC(".maps"); + +SEC("lsm.s/file_open") +__failure __msg("R2 type=ctx expected=ptr_, trusted_ptr_, rcu_ptr_") +int stash_unref_ctx_file(struct file *ctx_file) +{ + struct file_map_value *mapval; + int zero = 0; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) + return 0; + + /* ctx_file is just the hook argument, not an acquired file reference. */ + bpf_kptr_xchg(&mapval->file, ctx_file); + return 0; +} + +SEC("lsm.s/file_open") +__failure +__msg("Unreleased reference") +int stash_xchg_file_kptr_unreleased(struct file *ctx_file) +{ + struct file_map_value *mapval; + struct file *file = NULL; + int zero = 0; + + (void)ctx_file; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) + return 0; + + file = bpf_get_task_exe_file(bpf_get_current_task_btf()); + if (!file) + return 0; + + file = bpf_kptr_xchg(&mapval->file, file); + if (file) + bpf_put_file(file); + + file = bpf_kptr_xchg(&mapval->file, NULL); + if (!file) + return 0; + + /* Retrieved kptr is never released. */ + return 0; +} + +SEC("lsm.s/file_open") +__failure +__msg("Possibly NULL pointer passed to trusted arg0") +int stash_xchg_file_kptr_no_null_check(struct file *ctx_file) +{ + struct file_map_value *mapval; + struct file *file = NULL; + int zero = 0; + + (void)ctx_file; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) + return 0; + + file = bpf_get_task_exe_file(bpf_get_current_task_btf()); + if (!file) + return 0; + + file = bpf_kptr_xchg(&mapval->file, file); + if (file) + bpf_put_file(file); + + file = bpf_kptr_xchg(&mapval->file, NULL); + bpf_put_file(file); + return 0; +} + +SEC("lsm.s/file_open") +__failure +__msg("R1 must be referenced or trusted") +int put_map_owned_file(struct file *ctx_file) +{ + struct file_map_value *mapval; + struct file *file = NULL; + int zero = 0; + + (void)ctx_file; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) + return 0; + + file = bpf_get_task_exe_file(bpf_get_current_task_btf()); + if (!file) + return 0; + + file = bpf_kptr_xchg(&mapval->file, file); + if (file) + bpf_put_file(file); + + bpf_rcu_read_lock(); + file = mapval->file; + if (!file) { + bpf_rcu_read_unlock(); + return 0; + } + + /* Can't release a kptr while it is still owned by the map. */ + bpf_put_file(file); + bpf_rcu_read_unlock(); + return 0; +} + +SEC("lsm.s/file_open") +__failure +__msg("release kernel function bpf_put_file expects") +int BPF_PROG(put_unref_ctx_file, struct file *file) +{ + /* Can't release the unacquired LSM hook argument. */ + bpf_put_file(file); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_file_success.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_file_success.c new file mode 100644 index 000000000000..ece00c9e584f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_file_success.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include + +#include "bpf_misc.h" +#include "bpf_experimental.h" + +struct file_map_value { + struct file __kptr * file; +}; + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, struct file_map_value); + __uint(max_entries, 1); +} stashed_files SEC(".maps"); + +SEC("lsm.s/file_open") +__description("file_kptr: xchg and reinsert") +__success +int xchg_reinsert_file_kptr(struct file *ctx_file) +{ + struct file_map_value *mapval; + struct file *file, *old; + int zero = 0; + + (void)ctx_file; + + mapval = bpf_map_lookup_elem(&stashed_files, &zero); + if (!mapval) + return 0; + + file = bpf_get_task_exe_file(bpf_get_current_task_btf()); + if (!file) + return 0; + + old = bpf_kptr_xchg(&mapval->file, file); + if (old) + bpf_put_file(old); + + file = bpf_kptr_xchg(&mapval->file, NULL); + if (!file) + return 0; + + old = bpf_kptr_xchg(&mapval->file, file); + if (old) { + bpf_put_file(old); + return 0; + } + + file = bpf_kptr_xchg(&mapval->file, NULL); + if (file) + bpf_put_file(file); + + return 0; +} + +char _license[] SEC("license") = "GPL"; -- 2.53.0