From patchwork Tue Dec 10 22:06:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13902341 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A1481EE7A5; Tue, 10 Dec 2024 22:06:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868416; cv=none; b=dABkFdodfiokMSQFOF8KdAy4preHob0dLlVs3Xd8/EBDXGdDSCSGfpag+c+33lrbfZMRBvZ8bmS+M2+J6zJDBALm6ryg3HNvvWgNlVrtxtPFIYvFbLIiSbPkn5Gk/HjwwpL63OXgOjVxBEvDUNCZTkN0zmeGHG93I0Tmcb5sRg8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868416; c=relaxed/simple; bh=IJFC1v25WWLbFn6yORvMGUMUMr97ljm73SJMJjYoIY8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VJv/n/SVt44I65DcGexui5PDze8T6dZPwY48oM49SbiMB8VEmZXU2iI8TCpVSW2En1IXq/+Qs3tGYUurq8Uf3jBvKr4AF89mM8eyFF6U2+5mVY4nMisxbe7qprvxkfESxTsa6s3EqFJY2m3GnYyKWdEpVEB/MCtVqdkG7tLYAow= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DtJZ5oVe; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DtJZ5oVe" Received: by smtp.kernel.org (Postfix) with ESMTPSA id ECD28C4CED6; Tue, 10 Dec 2024 22:06:51 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1733868414; bh=IJFC1v25WWLbFn6yORvMGUMUMr97ljm73SJMJjYoIY8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DtJZ5oVee6w9DAJzUiF20aWTP88/e1g/at8agvh21hPWmNKPuTmOy/0VmqRtRuPlt x1TS0pvLK5rY5Eh+/dWRoeMZFqVIeawMFy3WJfrxWv8XwJQp/delEqWC1qmRvrXrti XUG9VvVAXSsIupDd7/juvSwuivLRpq8+WZKoxdxh1QhlWa/mAaAfDKBK56c+M+qEVH /7+BAjgDLqREerApS8O0ipdLBr/e+hkWKnG6jkdN9EfEtho2liW33wvi7tzBS78rCq PGq5KxyunZAA2xjjIKHApo9s21YLuX5opCe+uyDTyCXFjwtgGHVMWt+y6fhh6XRC1D +OaQX4GOmbx1g== From: Song Liu To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, kpsingh@kernel.org, mattbobrowski@google.com, liamwisehart@meta.com, shankaran@meta.com, Song Liu Subject: [PATCH v3 bpf-next 1/6] fs/xattr: bpf: Introduce security.bpf. xattr name prefix Date: Tue, 10 Dec 2024 14:06:22 -0800 Message-ID: <20241210220627.2800362-2-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241210220627.2800362-1-song@kernel.org> References: <20241210220627.2800362-1-song@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Introduct new xattr name prefix security.bpf., and enable reading these xattrs from bpf kfuncs bpf_get_[file|dentry]_xattr(). As we are on it, correct the comments for return value of bpf_get_[file|dentry]_xattr(), i.e. return length the xattr value on success. Signed-off-by: Song Liu Acked-by: Christian Brauner --- fs/bpf_fs_kfuncs.c | 19 ++++++++++++++----- include/uapi/linux/xattr.h | 4 ++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 3fe9f59ef867..8a65184c8c2c 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -93,6 +93,11 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) return len; } +static bool match_security_bpf_prefix(const char *name__str) +{ + return !strncmp(name__str, XATTR_NAME_BPF_LSM, XATTR_NAME_BPF_LSM_LEN); +} + /** * bpf_get_dentry_xattr - get xattr of a dentry * @dentry: dentry to get xattr from @@ -101,9 +106,10 @@ __bpf_kfunc int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) * * Get xattr *name__str* of *dentry* and store the output in *value_ptr*. * - * For security reasons, only *name__str* with prefix "user." is allowed. + * For security reasons, only *name__str* with prefix "user." or + * "security.bpf." is allowed. * - * Return: 0 on success, a negative value on error. + * Return: length of the xattr value on success, a negative value on error. */ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__str, struct bpf_dynptr *value_p) @@ -117,7 +123,9 @@ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st if (WARN_ON(!inode)) return -EINVAL; - if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) + /* Allow reading xattr with user. and security.bpf. prefix */ + if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + !match_security_bpf_prefix(name__str)) return -EPERM; value_len = __bpf_dynptr_size(value_ptr); @@ -139,9 +147,10 @@ __bpf_kfunc int bpf_get_dentry_xattr(struct dentry *dentry, const char *name__st * * Get xattr *name__str* of *file* and store the output in *value_ptr*. * - * For security reasons, only *name__str* with prefix "user." is allowed. + * For security reasons, only *name__str* with prefix "user." or + * "security.bpf." is allowed. * - * Return: 0 on success, a negative value on error. + * Return: length of the xattr value on success, a negative value on error. */ __bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str, struct bpf_dynptr *value_p) diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h index 9854f9cff3c6..c7c85bb504ba 100644 --- a/include/uapi/linux/xattr.h +++ b/include/uapi/linux/xattr.h @@ -83,6 +83,10 @@ struct xattr_args { #define XATTR_CAPS_SUFFIX "capability" #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX +#define XATTR_BPF_LSM_SUFFIX "bpf." +#define XATTR_NAME_BPF_LSM (XATTR_SECURITY_PREFIX XATTR_BPF_LSM_SUFFIX) +#define XATTR_NAME_BPF_LSM_LEN (sizeof(XATTR_NAME_BPF_LSM) - 1) + #define XATTR_POSIX_ACL_ACCESS "posix_acl_access" #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS #define XATTR_POSIX_ACL_DEFAULT "posix_acl_default" From patchwork Tue Dec 10 22:06:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13902342 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B9D711EE7A0; Tue, 10 Dec 2024 22:07:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868422; cv=none; b=VXfIX2QLl3OIN+jE1OLpwbEwBZmstK0g2WrJbOMWEEeoO9HSNerDeWiTLWQbAvLWHjZ9Olr602/sw4NQQJK9T/dtITaHKjBMo5vsnewDzTg/BfzTxGUD9MocRG7RVpnAtJo1ZXa27TXZaoVtNYSWdGUwZFS064z/Yo3MkubU8+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868422; c=relaxed/simple; bh=46MoxRliv3cRnyQt0Z0/+IJ+vRdQg9CV9gODL3IHbNo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kXQDuCOcPCYBqFcisOE5IxnavkMfdLe18OrjLyUFQQVUo8hMjlH06nL8wEoCJlOZrA8DY9bb1G6fxwcV2vKG22z0HHkt85CKOaT1JYqesGvFwUDJbTGHPM8HijH/DRj/+EQIZNiyqpU/e6nMUHiCqrwQkFvdNDnvxxE0KwGRT6w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=suNB2HAz; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="suNB2HAz" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5B0A1C4CEDF; Tue, 10 Dec 2024 22:06:59 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1733868422; bh=46MoxRliv3cRnyQt0Z0/+IJ+vRdQg9CV9gODL3IHbNo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=suNB2HAzJzCLclDDSsyL5s5987EGrJ50BtvdvHXVbMAfvouwz0vuXpKNpvF7Y7RmH WynwxZiGvVj2i94oxEC2mLbzgWmo4CpbIp3M5TqkxuakpgDZBpSH7uDSCGNnGuvFro GG9R1k0wMCxDDe0piKxKf/RUgVPza6f1Wj1fi9PwEEWy9qLYcTeP39Wh8n9uu2VILD c28d5C73bYDbMKeb954WKiqRtSVsfPPRu1At3Avl4Sju0H3wk0N9z1NyXrrvIL5R8/ yM7rQNvN28U0rwMHJ9dkGH8GC0j1xrNLSvbwhmrHSW/mFRB8Vau3u9JIqARYetsq4x BEz81cOWzNz8A== From: Song Liu To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, kpsingh@kernel.org, mattbobrowski@google.com, liamwisehart@meta.com, shankaran@meta.com, Song Liu Subject: [PATCH v3 bpf-next 2/6] selftests/bpf: Extend test fs_kfuncs to cover security.bpf. xattr names Date: Tue, 10 Dec 2024 14:06:23 -0800 Message-ID: <20241210220627.2800362-3-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241210220627.2800362-1-song@kernel.org> References: <20241210220627.2800362-1-song@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Extend test_progs fs_kfuncs to cover different xattr names. Specifically: xattr name "user.kfuncs" and "security.bpf.xxx" can be read from BPF program with kfuncs bpf_get_[file|dentry]_xattr(); while "security.bpf" and "security.selinux" cannot be read. Signed-off-by: Song Liu --- .../selftests/bpf/prog_tests/fs_kfuncs.c | 37 ++++++++++++++----- .../selftests/bpf/progs/test_get_xattr.c | 28 ++++++++++++-- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c index 5a0b51157451..419f45b56472 100644 --- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c +++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c @@ -12,7 +12,7 @@ static const char testfile[] = "/tmp/test_progs_fs_kfuncs"; -static void test_xattr(void) +static void test_get_xattr(const char *name, const char *value, bool allow_access) { struct test_get_xattr *skel = NULL; int fd = -1, err; @@ -25,7 +25,7 @@ static void test_xattr(void) close(fd); fd = -1; - err = setxattr(testfile, "user.kfuncs", "hello", sizeof("hello"), 0); + err = setxattr(testfile, name, value, strlen(value) + 1, 0); if (err && errno == EOPNOTSUPP) { printf("%s:SKIP:local fs doesn't support xattr (%d)\n" "To run this test, make sure /tmp filesystem supports xattr.\n", @@ -48,16 +48,23 @@ static void test_xattr(void) goto out; fd = open(testfile, O_RDONLY, 0644); + if (!ASSERT_GE(fd, 0, "open_file")) goto out; - ASSERT_EQ(skel->bss->found_xattr_from_file, 1, "found_xattr_from_file"); - /* Trigger security_inode_getxattr */ - err = getxattr(testfile, "user.kfuncs", v, sizeof(v)); - ASSERT_EQ(err, -1, "getxattr_return"); - ASSERT_EQ(errno, EINVAL, "getxattr_errno"); - ASSERT_EQ(skel->bss->found_xattr_from_dentry, 1, "found_xattr_from_dentry"); + err = getxattr(testfile, name, v, sizeof(v)); + + if (allow_access) { + ASSERT_EQ(err, -1, "getxattr_return"); + ASSERT_EQ(errno, EINVAL, "getxattr_errno"); + ASSERT_EQ(skel->bss->found_xattr_from_file, 1, "found_xattr_from_file"); + ASSERT_EQ(skel->bss->found_xattr_from_dentry, 1, "found_xattr_from_dentry"); + } else { + ASSERT_EQ(err, strlen(value) + 1, "getxattr_return"); + ASSERT_EQ(skel->bss->found_xattr_from_file, 0, "found_xattr_from_file"); + ASSERT_EQ(skel->bss->found_xattr_from_dentry, 0, "found_xattr_from_dentry"); + } out: close(fd); @@ -141,8 +148,18 @@ static void test_fsverity(void) void test_fs_kfuncs(void) { - if (test__start_subtest("xattr")) - test_xattr(); + /* Matches xattr_names in progs/test_get_xattr.c */ + if (test__start_subtest("user_xattr")) + test_get_xattr("user.kfuncs", "hello", true); + + if (test__start_subtest("security_bpf_xattr")) + test_get_xattr("security.bpf.xxx", "hello", true); + + if (test__start_subtest("security_bpf_xattr_error")) + test_get_xattr("security.bpf", "hello", false); + + if (test__start_subtest("security_selinux_xattr_error")) + test_get_xattr("security.selinux", "hello", false); if (test__start_subtest("fsverity")) test_fsverity(); diff --git a/tools/testing/selftests/bpf/progs/test_get_xattr.c b/tools/testing/selftests/bpf/progs/test_get_xattr.c index 66e737720f7c..358e3506e5b0 100644 --- a/tools/testing/selftests/bpf/progs/test_get_xattr.c +++ b/tools/testing/selftests/bpf/progs/test_get_xattr.c @@ -6,6 +6,7 @@ #include #include #include "bpf_kfuncs.h" +#include "bpf_misc.h" char _license[] SEC("license") = "GPL"; @@ -17,12 +18,23 @@ static const char expected_value[] = "hello"; char value1[32]; char value2[32]; +/* Matches caller of test_get_xattr() in prog_tests/fs_kfuncs.c */ +static const char * const xattr_names[] = { + /* The following work. */ + "user.kfuncs", + "security.bpf.xxx", + + /* The following do not work. */ + "security.bpf", + "security.selinux" +}; + SEC("lsm.s/file_open") int BPF_PROG(test_file_open, struct file *f) { struct bpf_dynptr value_ptr; __u32 pid; - int ret; + int ret, i; pid = bpf_get_current_pid_tgid() >> 32; if (pid != monitored_pid) @@ -30,7 +42,11 @@ int BPF_PROG(test_file_open, struct file *f) bpf_dynptr_from_mem(value1, sizeof(value1), 0, &value_ptr); - ret = bpf_get_file_xattr(f, "user.kfuncs", &value_ptr); + for (i = 0; i < ARRAY_SIZE(xattr_names); i++) { + ret = bpf_get_file_xattr(f, xattr_names[i], &value_ptr); + if (ret == sizeof(expected_value)) + break; + } if (ret != sizeof(expected_value)) return 0; if (bpf_strncmp(value1, ret, expected_value)) @@ -44,7 +60,7 @@ int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name) { struct bpf_dynptr value_ptr; __u32 pid; - int ret; + int ret, i; pid = bpf_get_current_pid_tgid() >> 32; if (pid != monitored_pid) @@ -52,7 +68,11 @@ int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name) bpf_dynptr_from_mem(value2, sizeof(value2), 0, &value_ptr); - ret = bpf_get_dentry_xattr(dentry, "user.kfuncs", &value_ptr); + for (i = 0; i < ARRAY_SIZE(xattr_names); i++) { + ret = bpf_get_dentry_xattr(dentry, xattr_names[i], &value_ptr); + if (ret == sizeof(expected_value)) + break; + } if (ret != sizeof(expected_value)) return 0; if (bpf_strncmp(value2, ret, expected_value)) From patchwork Tue Dec 10 22:06:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13902343 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1563B1EE7A2; Tue, 10 Dec 2024 22:07:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868430; cv=none; b=R9PKUUzymPZxvNkz9qKeK/U7A59U695Ygi0stmOffWZJqtnzJxoclFOhnd2X1A9NxSi6vJYATdG/PJXq3UpEsbRr3qNwvR+ZrGg5uxOQpS5XSuczCST/83M+KT/y1l+vPcKJS4uUgwHWt5qAPAAslck7/R+lEKENr15NNMnTmm0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868430; c=relaxed/simple; bh=wFf35YVFFsrsYjOIPa/lLhJz+32cyCFyYbyRWYcEsrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=B0T/AZF85o+suRaM5Mm/B/GZunhXol00FgOVQo9ZFaOkUzzx3Wnq1iPi7DVnPM05My+NQjGw2nEIGb5ZoPCRyxifB/8MnsAFiX/JE84tWWSvW2zJRjWxx/gLv76eKUrrtBxwpnPV5XgI/JTovnihTaog1E0059whCB51F5RWg20= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SA4lYaoi; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="SA4lYaoi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D14E4C4CEE0; Tue, 10 Dec 2024 22:07:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1733868429; bh=wFf35YVFFsrsYjOIPa/lLhJz+32cyCFyYbyRWYcEsrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SA4lYaoiOHoiWsUKL5TDYkbHiRgbrZoKvyRzZH98llm9HOpFy4VTbyIHTIwNF2GDc zpN7KgQ41KxIKkvhlAVOYV8RTwQ0G3qynngijax+Ynxx8U0MEwYi7Wj5cehp8Bxrm7 7c1HWSj70KxE7KbHAE35jdC5kNQAH6tAQwZ0q8seDOLmve170fcX5PdmElTSCFXwjI BoqKhsmGYla1wDkJrVH08kSBeXV7clnmwWjobaijKAYNGy9+4NP4vksMqu14ewgsOQ PSZc0CHUkqAW8btiwkN3RdAYCbvCHUtKsGNAQtLKWcfarVKusSB4rdvrHzbzH6SYZy TTMl59f6wyW9w== From: Song Liu To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, kpsingh@kernel.org, mattbobrowski@google.com, liamwisehart@meta.com, shankaran@meta.com, Song Liu Subject: [PATCH v3 bpf-next 3/6] bpf: lsm: Add two more sleepable hooks Date: Tue, 10 Dec 2024 14:06:24 -0800 Message-ID: <20241210220627.2800362-4-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241210220627.2800362-1-song@kernel.org> References: <20241210220627.2800362-1-song@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Add bpf_lsm_inode_removexattr and bpf_lsm_inode_post_removexattr to list sleepable_lsm_hooks. These two hooks are always called from sleepable context. Signed-off-by: Song Liu --- kernel/bpf/bpf_lsm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 967492b65185..0a59df1c550a 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -316,7 +316,9 @@ BTF_ID(func, bpf_lsm_inode_getxattr) BTF_ID(func, bpf_lsm_inode_mknod) BTF_ID(func, bpf_lsm_inode_need_killpriv) BTF_ID(func, bpf_lsm_inode_post_setxattr) +BTF_ID(func, bpf_lsm_inode_post_removexattr) BTF_ID(func, bpf_lsm_inode_readlink) +BTF_ID(func, bpf_lsm_inode_removexattr) BTF_ID(func, bpf_lsm_inode_rename) BTF_ID(func, bpf_lsm_inode_rmdir) BTF_ID(func, bpf_lsm_inode_setattr) From patchwork Tue Dec 10 22:06:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13902344 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3330022C354; Tue, 10 Dec 2024 22:07:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868437; cv=none; b=LlTFvMcw/f/4enuZHg9FB82Um8Ry+18zwliLkwx+1+e9UInzi1DAJnt0FzAdT4kapbXIdd6a0UKr5Kp9QG56vzLl/l82MBB3r4IQXB3j/uRDxyhfEU5ryA5T2c7Ta/EggUxxgtEU9/k25J2U/f5qWwJHsqaRvVEtdmjZ9Q5O30Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868437; c=relaxed/simple; bh=RuCjSYs5JzJbAvz/Oao573nca0OJn1GdgHw8fi7yWz4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QQlinhYjWLodTAiR/lsXzF8UU0qYgajyfBQEV+sNLMC7Iq0wn1Vc0dl5rEDNAX8kZFOXS192XbDkro4p1EDm+AZijNU3+/Vrca4osaPo4poBFISO9fNB9KeJc4kRNdBw/wW/5UeiPXBvvGxBZnGWJAEhWEHNcVbXFZ0aYeIjblg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VRUWq52i; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="VRUWq52i" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 28BA2C4CED6; Tue, 10 Dec 2024 22:07:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1733868436; bh=RuCjSYs5JzJbAvz/Oao573nca0OJn1GdgHw8fi7yWz4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VRUWq52iSgkftYxE/EcXGaZKzJSlXX8JY/0vDlil9ybEsXKIgxiL5xTt7cVYHusxA /KJFkO7ROF+GUhOuInTXySHXnsCWrUyjqjfBLOQdWLNTXVVE9tJ73WsgGOLKmID0W5 kFjkC09yngj/z2Z2lov1ri2o6qD7NNTIeurlScnjebN6KaQc4gDrrTKdGDmBiIn3N5 WYs2qpapgRxFmPHz9PT6BJSV7iq+oWIsqCX+hi6M9rpUpZjJUD3hWKBgOtUX5jK9lJ g8Ge7p/6ccND+eLyzR0b/fUVTRdn2hvW11bxTvjc52J2FDdc0y6HFoN3Xn4zEcQcTx p9ButVLD04y2A== From: Song Liu To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, kpsingh@kernel.org, mattbobrowski@google.com, liamwisehart@meta.com, shankaran@meta.com, Song Liu Subject: [PATCH v3 bpf-next 4/6] bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs Date: Tue, 10 Dec 2024 14:06:25 -0800 Message-ID: <20241210220627.2800362-5-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241210220627.2800362-1-song@kernel.org> References: <20241210220627.2800362-1-song@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Add the following kfuncs to set and remove xattrs from BPF programs: bpf_set_dentry_xattr bpf_remove_dentry_xattr bpf_set_dentry_xattr_locked bpf_remove_dentry_xattr_locked The _locked version of these kfuncs are called from hooks where dentry->d_inode is already locked. Signed-off-by: Song Liu --- fs/bpf_fs_kfuncs.c | 239 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 1 deletion(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 8a65184c8c2c..2b6e5be57ff3 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -161,6 +162,160 @@ __bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str, return bpf_get_dentry_xattr(dentry, name__str, value_p); } +static int bpf_xattr_write_permission(const char *name, struct inode *inode) +{ + if (WARN_ON(!inode)) + return -EINVAL; + + /* Only allow setting and removing security.bpf. xattrs */ + if (!match_security_bpf_prefix(name)) + return -EPERM; + + return inode_permission(&nop_mnt_idmap, inode, MAY_WRITE); +} + +static int __bpf_set_dentry_xattr(struct dentry *dentry, const char *name, + const struct bpf_dynptr *value_p, int flags, bool lock_inode) +{ + struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p; + struct inode *inode = d_inode(dentry); + const void *value; + u32 value_len; + int ret; + + ret = bpf_xattr_write_permission(name, inode); + if (ret) + return ret; + + value_len = __bpf_dynptr_size(value_ptr); + value = __bpf_dynptr_data(value_ptr, value_len); + if (!value) + return -EINVAL; + + if (lock_inode) + inode_lock(inode); + ret = __vfs_setxattr(&nop_mnt_idmap, dentry, inode, name, + value, value_len, flags); + if (!ret) { + fsnotify_xattr(dentry); + + /* This xattr is set by BPF LSM, so we do not call + * security_inode_post_setxattr. This is the same as + * security_inode_setsecurity(). + */ + } + if (lock_inode) + inode_unlock(inode); + return ret; +} + +/** + * bpf_set_dentry_xattr - set a xattr of a dentry + * @dentry: dentry to get xattr from + * @name__str: name of the xattr + * @value_p: xattr value + * @flags: flags to pass into filesystem operations + * + * Set xattr *name__str* of *dentry* to the value in *value_ptr*. + * + * For security reasons, only *name__str* with prefix "security.bpf." + * is allowed. + * + * The caller has not locked dentry->d_inode. + * + * Return: 0 on success, a negative value on error. + */ +__bpf_kfunc int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str, + const struct bpf_dynptr *value_p, int flags) +{ + return __bpf_set_dentry_xattr(dentry, name__str, value_p, flags, true); +} + +/** + * bpf_set_dentry_xattr_locked - set a xattr of a dentry + * @dentry: dentry to get xattr from + * @name__str: name of the xattr + * @value_p: xattr value + * @flags: flags to pass into filesystem operations + * + * Set xattr *name__str* of *dentry* to the value in *value_ptr*. + * + * For security reasons, only *name__str* with prefix "security.bpf." + * is allowed. + * + * The caller already locked dentry->d_inode. + * + * Return: 0 on success, a negative value on error. + */ +__bpf_kfunc int bpf_set_dentry_xattr_locked(struct dentry *dentry, const char *name__str, + const struct bpf_dynptr *value_p, int flags) +{ + return __bpf_set_dentry_xattr(dentry, name__str, value_p, flags, false); +} + +static int __bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str, + bool lock_inode) +{ + struct inode *inode = d_inode(dentry); + int ret; + + ret = bpf_xattr_write_permission(name__str, inode); + if (ret) + return ret; + + if (lock_inode) + inode_lock(inode); + ret = __vfs_removexattr(&nop_mnt_idmap, dentry, name__str); + if (!ret) { + fsnotify_xattr(dentry); + + /* This xattr is removed by BPF LSM, so we do not call + * security_inode_post_removexattr. + */ + } + if (lock_inode) + inode_unlock(inode); + return ret; +} + +/** + * bpf_remove_dentry_xattr - remove a xattr of a dentry + * @dentry: dentry to get xattr from + * @name__str: name of the xattr + * + * Rmove xattr *name__str* of *dentry*. + * + * For security reasons, only *name__str* with prefix "security.bpf." + * is allowed. + * + * The caller has not locked dentry->d_inode. + * + * Return: 0 on success, a negative value on error. + */ +__bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) +{ + return __bpf_remove_dentry_xattr(dentry, name__str, true); +} + +/** + * bpf_remove_dentry_xattr_locked - remove a xattr of a dentry + * @dentry: dentry to get xattr from + * @name__str: name of the xattr + * + * Rmove xattr *name__str* of *dentry*. + * + * For security reasons, only *name__str* with prefix "security.bpf." + * is allowed. + * + * The caller already locked dentry->d_inode. + * + * Return: 0 on success, a negative value on error. + */ +__bpf_kfunc int bpf_remove_dentry_xattr_locked(struct dentry *dentry, const char *name__str) +{ + return __bpf_remove_dentry_xattr(dentry, name__str, false); +} + __bpf_kfunc_end_defs(); BTF_KFUNCS_START(bpf_fs_kfunc_set_ids) @@ -186,9 +341,91 @@ static const struct btf_kfunc_id_set bpf_fs_kfunc_set = { .filter = bpf_fs_kfuncs_filter, }; +/* bpf_[set|remove]_dentry_xattr.* hooks have KF_TRUSTED_ARGS and + * KF_SLEEPABLE, so they are only available to sleepable hooks with + * dentry arguments. + * + * Setting and removing xattr requires exclusive lock on dentry->d_inode. + * Some hooks already locked d_inode, while some hooks have not locked + * d_inode. Therefore, we need different kfuncs for different hooks. + * Specifically, hooks in the following list (d_inode_locked_hooks) + * should call bpf_[set|remove]_dentry_xattr_locked; while other hooks + * should call bpf_[set|remove]_dentry_xattr. + */ +BTF_SET_START(d_inode_locked_hooks) +BTF_ID(func, bpf_lsm_inode_post_removexattr) +BTF_ID(func, bpf_lsm_inode_post_setattr) +BTF_ID(func, bpf_lsm_inode_post_setxattr) +BTF_ID(func, bpf_lsm_inode_removexattr) +BTF_ID(func, bpf_lsm_inode_rmdir) +BTF_ID(func, bpf_lsm_inode_setattr) +BTF_ID(func, bpf_lsm_inode_setxattr) +BTF_ID(func, bpf_lsm_inode_unlink) +#ifdef CONFIG_SECURITY_PATH +BTF_ID(func, bpf_lsm_path_unlink) +BTF_ID(func, bpf_lsm_path_rmdir) +#endif /* CONFIG_SECURITY_PATH */ +BTF_SET_END(d_inode_locked_hooks) + +static bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog) +{ + return btf_id_set_contains(&d_inode_locked_hooks, prog->aux->attach_btf_id); +} + +BTF_KFUNCS_START(bpf_write_xattr_set_ids) +BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) +BTF_KFUNCS_END(bpf_write_xattr_set_ids) + +static int bpf_write_xattr_filter(const struct bpf_prog *prog, u32 kfunc_id) +{ + if (!btf_id_set8_contains(&bpf_write_xattr_set_ids, kfunc_id)) + return 0; + if (prog->type != BPF_PROG_TYPE_LSM) + return -EACCES; + + if (bpf_lsm_has_d_inode_locked(prog)) + return -EINVAL; + return 0; +} + +static const struct btf_kfunc_id_set bpf_write_xattr_set = { + .owner = THIS_MODULE, + .set = &bpf_write_xattr_set_ids, + .filter = bpf_write_xattr_filter, +}; + +BTF_KFUNCS_START(bpf_write_xattr_locked_set_ids) +BTF_ID_FLAGS(func, bpf_set_dentry_xattr_locked, KF_SLEEPABLE | KF_TRUSTED_ARGS) +BTF_ID_FLAGS(func, bpf_remove_dentry_xattr_locked, KF_SLEEPABLE | KF_TRUSTED_ARGS) +BTF_KFUNCS_END(bpf_write_xattr_locked_set_ids) + +static int bpf_write_xattr_locked_filter(const struct bpf_prog *prog, u32 kfunc_id) +{ + if (!btf_id_set8_contains(&bpf_write_xattr_locked_set_ids, kfunc_id)) + return 0; + if (prog->type != BPF_PROG_TYPE_LSM) + return -EACCES; + + if (!bpf_lsm_has_d_inode_locked(prog)) + return -EINVAL; + return 0; +} + +static const struct btf_kfunc_id_set bpf_write_xattr_locked_set = { + .owner = THIS_MODULE, + .set = &bpf_write_xattr_locked_set_ids, + .filter = bpf_write_xattr_locked_filter, +}; + static int __init bpf_fs_kfuncs_init(void) { - return register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fs_kfunc_set); + int ret; + + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fs_kfunc_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_write_xattr_set); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_write_xattr_locked_set); + return ret; } late_initcall(bpf_fs_kfuncs_init); From patchwork Tue Dec 10 22:06:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13902345 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6959E22C34E; Tue, 10 Dec 2024 22:07:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868444; cv=none; b=fcuoqtSqREGlbBjDl1hxF6F2Ub0T0HcVWY4LsV3JWoFCIlGTRrtAjVUzAy63nbryu1aVICsfsETISxpMik2zpJV4Y9kcCIz7xnGIb5HdNrPkAuhKXQ3pAkD54wa+GlfiXvC67ASZPW4OjZSCbLPQDKTg7ezOuRZFthq+IYB3WKU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868444; c=relaxed/simple; bh=QoJfkslAfy1Bv8yBKAflVnbUwxV6932aFyQj1dQM+hs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gd3MYTLHMF+0isVeM+gha/WIHWxxJyuzs0rMtgNRzaQspO4X5ZJYwVKfV/sH687j6oz5dJ0SphkD2rOzSj8cGEvIXIlFr5S0ojsCKUBqCZ6EzHUhzborVyoy9qoeV7xdVRLJALM383qZrNGSJpYLCT7/rgyuLkaXCmYf4dquZaY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dr3HUg3W; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dr3HUg3W" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1F338C4CED6; Tue, 10 Dec 2024 22:07:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1733868443; bh=QoJfkslAfy1Bv8yBKAflVnbUwxV6932aFyQj1dQM+hs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dr3HUg3WuYrYgx80XLG3w9/fKMz8yuIPXxGqcxYgdzItzU/BlDoykodFYAah0WHU3 oYqOY7tNatBeA2fvG3xtT9ctmLRFsLS6grcim/qmExglE9jZRDHbTPElPMD+Hr//QU mtuUppTOSbN0QMswQHDYHM+UJvNv14QEf2MwzwqPI0/iv51c+X0Bqmo3cUx7/8jIdg H/OBzXITYeR3FG9h7e11/8wn3u5G2oTam/Za6OB8HX3gEJhBMHUoZiECuziBS5J0nk J4kFdQB/3sebsmRNENGk/f3lhU6yMaZOddXtObueBjZG2TuEmxHG4lsz1vOQz0wpeX /Q7UTThA/8XlA== From: Song Liu To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, kpsingh@kernel.org, mattbobrowski@google.com, liamwisehart@meta.com, shankaran@meta.com, Song Liu Subject: [PATCH v3 bpf-next 5/6] selftests/bpf: Test kfuncs that set and remove xattr from BPF programs Date: Tue, 10 Dec 2024 14:06:26 -0800 Message-ID: <20241210220627.2800362-6-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241210220627.2800362-1-song@kernel.org> References: <20241210220627.2800362-1-song@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Two sets of tests are added to exercise the not _locked and _locked version of the kfuncs. For both tests, user space accesses xattr security.bpf.foo on a testfile. The BPF program is triggered by user space access (on LSM hook inode_[set|get]_xattr) and sets or removes xattr security.bpf.bar. Then user space then validates that xattr security.bpf.bar is set or removed as expected. Signed-off-by: Song Liu --- tools/testing/selftests/bpf/bpf_kfuncs.h | 10 ++ .../selftests/bpf/prog_tests/fs_kfuncs.c | 125 +++++++++++++++++ .../bpf/progs/test_set_remove_xattr.c | 129 ++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/test_set_remove_xattr.c diff --git a/tools/testing/selftests/bpf/bpf_kfuncs.h b/tools/testing/selftests/bpf/bpf_kfuncs.h index 2eb3483f2fb0..e53f3f5304e2 100644 --- a/tools/testing/selftests/bpf/bpf_kfuncs.h +++ b/tools/testing/selftests/bpf/bpf_kfuncs.h @@ -87,4 +87,14 @@ struct dentry; */ extern int bpf_get_dentry_xattr(struct dentry *dentry, const char *name, struct bpf_dynptr *value_ptr) __ksym __weak; + +extern int bpf_set_dentry_xattr_locked(struct dentry *dentry, const char *name__str, + const struct bpf_dynptr *value_p, int flags) __ksym __weak; +extern int bpf_remove_dentry_xattr_locked(struct dentry *dentry, + const char *name__str) __ksym __weak; + +extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str, + const struct bpf_dynptr *value_p, int flags) __ksym __weak; +extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak; + #endif diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c index 419f45b56472..41532df79fdd 100644 --- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c +++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c @@ -8,6 +8,7 @@ #include #include #include "test_get_xattr.skel.h" +#include "test_set_remove_xattr.skel.h" #include "test_fsverity.skel.h" static const char testfile[] = "/tmp/test_progs_fs_kfuncs"; @@ -72,6 +73,127 @@ static void test_get_xattr(const char *name, const char *value, bool allow_acces remove(testfile); } +/* xattr value we will set to security.bpf.foo */ +static const char value_foo[] = "hello"; + +static inline void read_and_validate_foo(struct test_set_remove_xattr *skel) +{ + char value_out[32]; + int err; + + err = getxattr(testfile, skel->rodata->xattr_foo, value_out, sizeof(value_out)); + ASSERT_EQ(err, sizeof(value_foo), "getxattr size foo"); + ASSERT_EQ(strncmp(value_out, value_foo, sizeof(value_foo)), 0, "strncmp value_foo"); +} + +static inline void set_foo(struct test_set_remove_xattr *skel) +{ + ASSERT_OK(setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0), + "setxattr foo"); +} + +static inline void validate_bar_match(struct test_set_remove_xattr *skel) +{ + char value_out[32]; + int err; + + err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out)); + ASSERT_EQ(err, sizeof(skel->data->value_bar), "getxattr size bar"); + ASSERT_EQ(strncmp(value_out, skel->data->value_bar, sizeof(skel->data->value_bar)), 0, + "strncmp value_bar"); +} + +static inline void validate_bar_removed(struct test_set_remove_xattr *skel) +{ + char value_out[32]; + int err; + + err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out)); + ASSERT_LT(err, 0, "getxattr size bar should fail"); +} + +static void test_set_remove_xattr(void) +{ + struct test_set_remove_xattr *skel = NULL; + int fd = -1, err; + + fd = open(testfile, O_CREAT | O_RDONLY, 0644); + if (!ASSERT_GE(fd, 0, "create_file")) + return; + + close(fd); + fd = -1; + + skel = test_set_remove_xattr__open_and_load(); + if (!ASSERT_OK_PTR(skel, "test_set_remove_xattr__open_and_load")) + return; + + /* Set security.bpf.foo to "hello" */ + err = setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0); + if (err && errno == EOPNOTSUPP) { + printf("%s:SKIP:local fs doesn't support xattr (%d)\n" + "To run this test, make sure /tmp filesystem supports xattr.\n", + __func__, errno); + test__skip(); + goto out; + } + + if (!ASSERT_OK(err, "setxattr")) + goto out; + + skel->bss->monitored_pid = getpid(); + err = test_set_remove_xattr__attach(skel); + if (!ASSERT_OK(err, "test_set_remove_xattr__attach")) + goto out; + + /* First, test not _locked version of the kfuncs with getxattr. */ + + /* Read security.bpf.foo and trigger test_inode_getxattr. This + * bpf program will set security.bpf.bar to "world". + */ + read_and_validate_foo(skel); + validate_bar_match(skel); + + /* Read security.bpf.foo and trigger test_inode_getxattr again. + * This will remove xattr security.bpf.bar. + */ + read_and_validate_foo(skel); + validate_bar_removed(skel); + + ASSERT_TRUE(skel->bss->set_security_bpf_bar_success, "set_security_bpf_bar_success"); + ASSERT_TRUE(skel->bss->remove_security_bpf_bar_success, "remove_security_bpf_bar_success"); + ASSERT_TRUE(skel->bss->set_security_selinux_fail, "set_security_selinux_fail"); + ASSERT_TRUE(skel->bss->remove_security_selinux_fail, "remove_security_selinux_fail"); + + /* Second, test _locked version of the kfuncs, with setxattr */ + + /* Set security.bpf.foo and trigger test_inode_setxattr. This + * bpf program will set security.bpf.bar to "world". + */ + set_foo(skel); + validate_bar_match(skel); + + /* Set security.bpf.foo and trigger test_inode_setxattr again. + * This will remove xattr security.bpf.bar. + */ + set_foo(skel); + validate_bar_removed(skel); + + ASSERT_TRUE(skel->bss->locked_set_security_bpf_bar_success, + "locked_set_security_bpf_bar_success"); + ASSERT_TRUE(skel->bss->locked_remove_security_bpf_bar_success, + "locked_remove_security_bpf_bar_success"); + ASSERT_TRUE(skel->bss->locked_set_security_selinux_fail, + "locked_set_security_selinux_fail"); + ASSERT_TRUE(skel->bss->locked_remove_security_selinux_fail, + "locked_remove_security_selinux_fail"); + +out: + close(fd); + test_set_remove_xattr__destroy(skel); + remove(testfile); +} + #ifndef SHA256_DIGEST_SIZE #define SHA256_DIGEST_SIZE 32 #endif @@ -161,6 +283,9 @@ void test_fs_kfuncs(void) if (test__start_subtest("security_selinux_xattr_error")) test_get_xattr("security.selinux", "hello", false); + if (test__start_subtest("set_remove_xattr")) + test_set_remove_xattr(); + if (test__start_subtest("fsverity")) test_fsverity(); } diff --git a/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c new file mode 100644 index 000000000000..3ed5b489edfa --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include +#include "bpf_kfuncs.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +__u32 monitored_pid; + +const char xattr_foo[] = "security.bpf.foo"; +const char xattr_bar[] = "security.bpf.bar"; +const char xattr_linux[] = "security.selinux"; +char value_bar[] = "world"; +char read_value[32]; + +bool set_security_bpf_bar_success; +bool remove_security_bpf_bar_success; +bool set_security_selinux_fail; +bool remove_security_selinux_fail; + +char name_buf[32]; + +static inline bool name_match_foo(const char *name) +{ + bpf_probe_read_kernel(name_buf, sizeof(name_buf), name); + + return !bpf_strncmp(name_buf, sizeof(xattr_foo), xattr_foo); +} + +/* Test bpf_set_dentry_xattr and bpf_remove_dentry_xattr */ +SEC("lsm.s/inode_getxattr") +int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name) +{ + struct bpf_dynptr value_ptr; + __u32 pid; + int ret; + + pid = bpf_get_current_pid_tgid() >> 32; + if (pid != monitored_pid) + return 0; + + /* Only do the following for security.bpf.foo */ + if (!name_match_foo(name)) + return 0; + + bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr); + + /* read security.bpf.bar */ + ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr); + + if (ret < 0) { + /* If security.bpf.bar doesn't exist, set it */ + bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr); + + ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0); + if (!ret) + set_security_bpf_bar_success = true; + ret = bpf_set_dentry_xattr(dentry, xattr_linux, &value_ptr, 0); + if (ret) + set_security_selinux_fail = true; + } else { + /* If security.bpf.bar exists, remove it */ + ret = bpf_remove_dentry_xattr(dentry, xattr_bar); + if (!ret) + remove_security_bpf_bar_success = true; + + ret = bpf_remove_dentry_xattr(dentry, xattr_linux); + if (ret) + remove_security_selinux_fail = true; + } + + return 0; +} + +bool locked_set_security_bpf_bar_success; +bool locked_remove_security_bpf_bar_success; +bool locked_set_security_selinux_fail; +bool locked_remove_security_selinux_fail; + +/* Test bpf_set_dentry_xattr_locked and bpf_remove_dentry_xattr_locked */ +SEC("lsm.s/inode_setxattr") +int BPF_PROG(test_inode_setxattr, struct mnt_idmap *idmap, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct bpf_dynptr value_ptr; + __u32 pid; + int ret; + + pid = bpf_get_current_pid_tgid() >> 32; + if (pid != monitored_pid) + return 0; + + /* Only do the following for security.bpf.foo */ + if (!name_match_foo(name)) + return 0; + + bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr); + + /* read security.bpf.bar */ + ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr); + + if (ret < 0) { + /* If security.bpf.bar doesn't exist, set it */ + bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr); + + ret = bpf_set_dentry_xattr_locked(dentry, xattr_bar, &value_ptr, 0); + if (!ret) + locked_set_security_bpf_bar_success = true; + ret = bpf_set_dentry_xattr_locked(dentry, xattr_linux, &value_ptr, 0); + if (ret) + locked_set_security_selinux_fail = true; + } else { + /* If security.bpf.bar exists, remove it */ + ret = bpf_remove_dentry_xattr_locked(dentry, xattr_bar); + if (!ret) + locked_remove_security_bpf_bar_success = true; + + ret = bpf_remove_dentry_xattr_locked(dentry, xattr_linux); + if (ret) + locked_remove_security_selinux_fail = true; + } + + return 0; +} From patchwork Tue Dec 10 22:06:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13902346 X-Patchwork-Delegate: bpf@iogearbox.net Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D853C1EE7D1; Tue, 10 Dec 2024 22:07:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868452; cv=none; b=rWDDdTWpoHjtoX4AKrgvNc3QncW6Odnz5Rjx2Sypq9jwma2kJljnmfbh+dBqhyHfMn2sqyGPlcovWoI5aaQdVBPAXfO3j8P+PhnOUGu+1mzufpoTd3rXyRNgUf2Ggocq/oxxRuWld90eQCDlrRA4yusUd3VZlmA2IcqDHFdNVVs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733868452; c=relaxed/simple; bh=0fQ3Q90Pn0i+PpOwOA0VS4st230oZ1EKiYKxA/3sSK0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=o2d75L15qVp7rZM5J2rfa1+/BVicJ6QruLLAhvtAyCx3T4Uv6zqg7qn548yI927yUcQY/gdDqDiWTpdkYOEqEKM7FzW0X5ebEWdRDPGVWmfqGLulu16MgV11pAiebCo+W7mPx05lZRGMmuBADbpcsPdHROWBycIAOWlZpSdcyrY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TK/wDN4X; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TK/wDN4X" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6B896C4CED6; Tue, 10 Dec 2024 22:07:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1733868451; bh=0fQ3Q90Pn0i+PpOwOA0VS4st230oZ1EKiYKxA/3sSK0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TK/wDN4XAzYzcaam5iHqAQZjl/nEJ7DrrYhSk99c3QvDbimbj/ctpm9tzlgCU0ZXc xBAHYeqf0QEmS0GgedFcaOvy59tqTW8JlFyXA3PcCezSqaa2JbaGOpB+Sj0AYsysXF 4yQpepnlvvYq79YShcF/+0DdGJGpEoZ7CLg5YdLy9DXFO9Oetzq2uz/3F+2pBKLX4G X5l1wS7hhfuVhbrJoGxF9m/om1t0iKHMODdgl6TTj/KrfN6XmAfPKARih8/5c+M29T qiSteYYaxFjypRHla/AzQtGDmfVzQpSd6+21ixsjEOgx8qwgWc4jkAsoZ5gFsX8aMF nRF88BUYXbMhQ== From: Song Liu To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz, kpsingh@kernel.org, mattbobrowski@google.com, liamwisehart@meta.com, shankaran@meta.com, Song Liu Subject: [PATCH v3 bpf-next 6/6] selftests/bpf: Add __failure tests for set/remove xattr kfuncs Date: Tue, 10 Dec 2024 14:06:27 -0800 Message-ID: <20241210220627.2800362-7-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241210220627.2800362-1-song@kernel.org> References: <20241210220627.2800362-1-song@kernel.org> Precedence: bulk X-Mailing-List: bpf@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Delegate: bpf@iogearbox.net Different LSM hooks should call different versions of set/remove xattr kfuncs (with _locked or not). Add __failure tests to make sure the verifier can detect when the user uses the wrong kfuncs. Signed-off-by: Song Liu --- .../selftests/bpf/prog_tests/fs_kfuncs.c | 3 + .../bpf/progs/test_set_remove_xattr_failure.c | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/test_set_remove_xattr_failure.c diff --git a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c index 41532df79fdd..614335a3ff53 100644 --- a/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c +++ b/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c @@ -9,6 +9,7 @@ #include #include "test_get_xattr.skel.h" #include "test_set_remove_xattr.skel.h" +#include "test_set_remove_xattr_failure.skel.h" #include "test_fsverity.skel.h" static const char testfile[] = "/tmp/test_progs_fs_kfuncs"; @@ -286,6 +287,8 @@ void test_fs_kfuncs(void) if (test__start_subtest("set_remove_xattr")) test_set_remove_xattr(); + RUN_TESTS(test_set_remove_xattr_failure); + if (test__start_subtest("fsverity")) test_fsverity(); } diff --git a/tools/testing/selftests/bpf/progs/test_set_remove_xattr_failure.c b/tools/testing/selftests/bpf/progs/test_set_remove_xattr_failure.c new file mode 100644 index 000000000000..ee9c7df27a93 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_set_remove_xattr_failure.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ + +#include "vmlinux.h" +#include +#include "bpf_kfuncs.h" +#include "bpf_misc.h" + +char _license[] SEC("license") = "GPL"; + +static const char xattr_bar[] = "security.bpf.bar"; +char v[32]; + +SEC("lsm.s/inode_getxattr") +__failure __msg("calling kernel function bpf_set_dentry_xattr_locked is not allowed") +int BPF_PROG(test_getxattr_failure_a, struct dentry *dentry, char *name) +{ + struct bpf_dynptr value_ptr; + + bpf_dynptr_from_mem(v, sizeof(v), 0, &value_ptr); + + bpf_set_dentry_xattr_locked(dentry, xattr_bar, &value_ptr, 0); + return 0; +} + +SEC("lsm.s/inode_getxattr") +__failure __msg("calling kernel function bpf_remove_dentry_xattr_locked is not allowed") +int BPF_PROG(test_getxattr_failure_b, struct dentry *dentry, char *name) +{ + bpf_remove_dentry_xattr_locked(dentry, xattr_bar); + return 0; +} + +SEC("lsm.s/inode_setxattr") +__failure __msg("calling kernel function bpf_set_dentry_xattr is not allowed") +int BPF_PROG(test_inode_setxattr_failure_a, struct mnt_idmap *idmap, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct bpf_dynptr value_ptr; + + bpf_dynptr_from_mem(v, sizeof(v), 0, &value_ptr); + + bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0); + return 0; +} + +SEC("lsm.s/inode_setxattr") +__failure __msg("calling kernel function bpf_remove_dentry_xattr is not allowed") +int BPF_PROG(test_inode_setxattr_failure_b, struct mnt_idmap *idmap, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + bpf_remove_dentry_xattr(dentry, xattr_bar); + return 0; +}