From patchwork Tue Dec 17 06:38:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13911181 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 1F4C0170A15; Tue, 17 Dec 2024 06:38:36 +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=1734417517; cv=none; b=AE6I9DZPnXFwX058goU78omD7Ge+vI9RrCC5SJY6UViNdDpVGIYqaCQFWQChnz8bv92qMZgaRSygLOr+oXSbf9n1px3d2roM76c9Yzrp6Kfg79nyvaY1u5q0rnhArlSp3+HdwJQxBzxWzl7+CNKfr9dnLw9Rez9hpzjZ+AAArOs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734417517; c=relaxed/simple; bh=IJFC1v25WWLbFn6yORvMGUMUMr97ljm73SJMJjYoIY8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rb0s5G57xtZwISfU9kaPiv4ZSF5HZkhTx24nXGmUtfEt1kWv5X3lBQpS/xEJ1+8c67T6buIdqnNianK2Al25QEacdR4fZdiUfIxunmoUCsLx4Z6bLI7RG8askBgb10CkSQz37nr7ybhi/qQF6PzBAgaIAuL+bFqBZaNkAiMnuHA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CDJfcl6b; 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="CDJfcl6b" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C7C5CC4CED3; Tue, 17 Dec 2024 06:38:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734417516; bh=IJFC1v25WWLbFn6yORvMGUMUMr97ljm73SJMJjYoIY8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CDJfcl6bN2kNVWI+Oe/hwMPpWdXpZY0aBaYN4yQaItZBTcET49f34Kp2wkxQCNpp6 riFdsHTyvr6ENWfWJBe38Y5QIZ1+sV23sTelvrxOvI63Kv62tjHPOIV7tNaj/iPesr rVrmmT1TWzMihd65bmP2YOHZzEskJEMEDCpS4u5hYXZUY3MRnWtOJY8ZVT+peSD6H3 4pysgRfXKKhDFH7OyuVuFMqilWr3xnCLh4KSyL3FrT6FLx9+a7b9TBY5P9bPtk7cW4 jjSDHo48J/VzuqijMlcbGU/MBhLVF7iGfzh9mPg8JSbLFPKBbiFdZT6zJwexVeL2l9 mbUPPG660Xr1A== 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 v4 bpf-next 1/6] fs/xattr: bpf: Introduce security.bpf. xattr name prefix Date: Mon, 16 Dec 2024 22:38:16 -0800 Message-ID: <20241217063821.482857-2-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241217063821.482857-1-song@kernel.org> References: <20241217063821.482857-1-song@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 Reviewed-by: Jan Kara --- 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 17 06:38:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13911182 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 ADAD815E5C2; Tue, 17 Dec 2024 06:38:44 +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=1734417524; cv=none; b=Zl6MLrjUmmg5naz4tpLfoFU1/bkUm1GoXwrr1YRqYm+CeTMzwoJWcCpkQ545D0MK1hZxrw67JHOPhF1zdgOXkSR+EzslDC+V4/NolEl4Dml1LtN+V+2fTdc03aNf+uZfNEMJw7M5Ul9wJ70Ysay74Ywffn93hgMCItI5gjlhS5U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734417524; c=relaxed/simple; bh=46MoxRliv3cRnyQt0Z0/+IJ+vRdQg9CV9gODL3IHbNo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=o4pFlagJGQLLbIEWw4nC0CnkS2506xQKxRprlRQyWqZ0oyoD6ea+p2K/A4lg4Szr3LvSc258TMmzvKFTIwxo0kNk7gsZ31rGOaQloMwzUnPlZ/yz5iumKmMGkTBg7seWHceQy02hS+VSptZq7/vhur1Jm4yCLlbFNDV6EjKySUE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fnDqu897; 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="fnDqu897" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 438D5C4CED3; Tue, 17 Dec 2024 06:38:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734417524; bh=46MoxRliv3cRnyQt0Z0/+IJ+vRdQg9CV9gODL3IHbNo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fnDqu897hLDHcMt8NDEuAaETzt8raQaWo8K8lZ2BOdbG02JvRiQprx+OLYo8e8Ynq 7KvKOQY7nKtZjTAjQ2rv4VygNZBtVE40yvMQUtCu7ArJ37CMQJv7QebvmOYU5vkuLW QKcPVnMfq5dJEul/+klwUV9fQB2lF3glpduBKB8xPuQah/sr2NSUKw3zPKyHiha/LQ wwYako3z3vv8tlZ1hChxh6S/yqtBWF2U42gDgwjCb+gCuE7ATloLZrLzl6ZUBXFSbH 4KehKBGa+xWmVgDMUmDOAKamm6rhhrfZXDpW8Xk/EELn8k9tLzOld+EO8mdoyKqAFP vtAdRj+2SYHgA== 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 v4 bpf-next 2/6] selftests/bpf: Extend test fs_kfuncs to cover security.bpf. xattr names Date: Mon, 16 Dec 2024 22:38:17 -0800 Message-ID: <20241217063821.482857-3-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241217063821.482857-1-song@kernel.org> References: <20241217063821.482857-1-song@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 17 06:38:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13911183 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 0B0A718453F; Tue, 17 Dec 2024 06:38:51 +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=1734417532; cv=none; b=PgIqoRAGDMyNejoj5D57smOXmRRPOvo98ra3+FvMlQ9nLobr2X928rOdD5/ophpwa5PltlpaSp6LYmBwrzbjOYWYyuGCQwRav3MqfZcrlAzZHVcAYraXdMkAWd7Qxoq/z2KSKWguv2MRqOtICDdyZdJasC4JgUHeMQQ1JO69bZQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734417532; c=relaxed/simple; bh=wFf35YVFFsrsYjOIPa/lLhJz+32cyCFyYbyRWYcEsrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tuEjt1x7riWXmGftmFcVR59OETgjVL30I1S1me3ekOrQSoC84SIaAxmNWcKCzjyRjIOxs4e7ucBQgorD9ieOLJtOfHuo2poBzCqlzOGJkq37sSy6OJ7HKtV3HkYsnyuxlII+WFiVMCXF/wEhse0oHVI7EdyxxbWZra25B9fjmN0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cl7yLao3; 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="cl7yLao3" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BC4BCC4CED3; Tue, 17 Dec 2024 06:38:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734417531; bh=wFf35YVFFsrsYjOIPa/lLhJz+32cyCFyYbyRWYcEsrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cl7yLao3GfteqTdEyDwUWjMgtEec4bWw632duXx0uL947a6CrxYmc/oVvMWWYaYPx dovPqJV9YD8cAIHGoWp/Z3HYy+HlYC4fzU3iJ3J+SDBOGd22ulvCUyumg+zU/lEm79 XsdN2CLdHRiaHOljc8cd2tWDE3Up3Jp7yLFX2tUqiANoqqBXIINswowkNMuY6XWWL2 mPjaeozR+rLy4VMtECNR1SwA8hNLHIL4yyX8lfqnjE1x5gNfH0H0ebIhiLNbrRSwGE u2ivBcJ67Ui9OhJckVHCzO+KDJsISzH89HGn3wQjAJsz4jX7PdOIN6T4NVfG6Y9qZK d1ixnpfkAwKDw== 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 v4 bpf-next 3/6] bpf: lsm: Add two more sleepable hooks Date: Mon, 16 Dec 2024 22:38:18 -0800 Message-ID: <20241217063821.482857-4-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241217063821.482857-1-song@kernel.org> References: <20241217063821.482857-1-song@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 17 06:38:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13911212 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 BC1F616EB7C; Tue, 17 Dec 2024 06:38:59 +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=1734417539; cv=none; b=D7GPOmVNxl8bKdYlP1nBqulljK73afl6r4NQ6tD23PTYSQHBpnjI+NPA11kUAl/wubhHD/WIb9/tv2Ow7WBuLKi4fQk+iqwIpVRQea031NnPwVNkhKmyAJ5PW4dz1klUMi0avRpPbebomykz5V7pvBCSuUaj3fMy7P4iBo0lXnU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734417539; c=relaxed/simple; bh=f5+i1QTWEI38UXaXFdF3fywxj5TrxmIUcKHLqy0rKok=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fiUnsve5NogAyNxB7CddrU8gkjD/q1PnCT/XSObIMyEyHrMf/+szTG3jxPsbOnSTEf3EMk9j7Hz+HHM2FPxjH0Hruf1zSinHjWqTx3Iptmty3Pcrzb2VLnljoSTEq1cK+SjF9LguFi6caDWFgI8+dNXemh85QiFEFw1Z911x5Bk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=G4l8d8Sb; 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="G4l8d8Sb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 38ACAC4CED3; Tue, 17 Dec 2024 06:38:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734417539; bh=f5+i1QTWEI38UXaXFdF3fywxj5TrxmIUcKHLqy0rKok=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=G4l8d8SbqpR+Lssh3LDYhnVjgyzZJkMZrQJz4SFHPGzVtG7x9QSpVy1IkCTeGuS6B kNOIzckFSZyuvQQGNP55+C9XTTEZWctrKnLLkGZlRd0jHR6asE2YfHdLW2YUeNqgPk NJ4YzQVFt5Lrd58541s4hxuvaVkSVs8exBn1LF0lSCVNoS4rkZN5OcBleGYdFUCalb es3gCkFe+JTx9Ku2hFyJgChO811obkzTLuz38BLMEUDhAZ+siaoiLSj/ZOydhuAr9O Mb+ydtGJgCM6rmImZzAKdWYWhu1AgVwY5LOHZFuyBloCu8VoRIzuzRJxdC7M4179bh L/v4V2XrRW8Hw== 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 v4 bpf-next 4/6] bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs Date: Mon, 16 Dec 2024 22:38:19 -0800 Message-ID: <20241217063821.482857-5-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241217063821.482857-1-song@kernel.org> References: <20241217063821.482857-1-song@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 243 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 242 insertions(+), 1 deletion(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 8a65184c8c2c..3261324e956c 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,164 @@ __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; + + 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 = bpf_xattr_write_permission(name, inode); + if (ret) + goto out; + + 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(). + */ + } +out: + 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; + + if (lock_inode) + inode_lock(inode); + + ret = bpf_xattr_write_permission(name__str, inode); + if (ret) + goto out; + + 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. + */ + } +out: + 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 +345,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 17 06:38:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13911213 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 2D8A8170A2C; Tue, 17 Dec 2024 06:39:06 +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=1734417547; cv=none; b=l2jN0EDTG2Fea6nNM5W4JXqQ6tQJqqzZfD6jnuPG4Bzo5xBBOJ2X7E8BDwAfS5u0kyZSGp8Et4jwBLYYfJAvUy29UTEcKukbksdiw6h0+6dFrBkvq4/FQ4JBRRoEBHG1QpigWcukW1AbMczMqcmfxdBZhkTBm5jM9vR0nRrM5QA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734417547; c=relaxed/simple; bh=oKzNKonXb/Fif+DMF8BfCkcrGKCzKrFc7nNZh/ThqfA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tK86xSOUBWjSdbT6fzT58Hv1syzKNtuJyFSTKnKSg5VmHBMD2qxc7y+DJvJyZ3jPQnzkOekQ/2FUQ7vBH6PLHdMJEFSMKDPTvH3TT3oznCgovjHr/B2WArmf7RwGF1EFaPZ0xWjesdsjnZDsKUw3IVGutz6QlPEK+TezrLJOoic= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=aVlzaa0C; 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="aVlzaa0C" Received: by smtp.kernel.org (Postfix) with ESMTPSA id AD56EC4CED3; Tue, 17 Dec 2024 06:39:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734417546; bh=oKzNKonXb/Fif+DMF8BfCkcrGKCzKrFc7nNZh/ThqfA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aVlzaa0Clkbt8uvMAYvkmPcMBQmjrvIbWVpmsqD/1S0BLRhmFu8Q0tHOZx6aSl/Su s88nJQDNWBKfThrdYBgYMf6w4Oy7UxO63I3jL+9giv4b9+cwbyEfINyO7IwU0v9VK0 DmX3KRL6Zuj3Sfw/BVyQgRNpOZPBqHN0zLePjMdiCZ4P3sCmL8kDkqKbGMmlQ6cTgr RLFVpEb1RFt189BlFGB9JsNPNuXeiJf0mx9VHUnUnHvzPN/s7bS4LzCfHTn2lM1S+B ZU1R834UiSQvD77G4W+KqTUOJLywphspQwYg9YDqdQkBpxyBoLOpSsJ/gqp6Ak14Be NFdhogCmP8z7w== 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 v4 bpf-next 5/6] selftests/bpf: Test kfuncs that set and remove xattr from BPF programs Date: Mon, 16 Dec 2024 22:38:20 -0800 Message-ID: <20241217063821.482857-6-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241217063821.482857-1-song@kernel.org> References: <20241217063821.482857-1-song@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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..43a26ec69a8e 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 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 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 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 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 17 06:38:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13911214 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 A1BA717278D; Tue, 17 Dec 2024 06:39:14 +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=1734417554; cv=none; b=oojryqi68FPNx6RiiE56PMHEQP/sh1kqOTCb7Gig9T96atZs6AFiRc/uE3xUgv2dLdIoIbA2k2d+d5s2wJxTdmy+sgoyYGVf6VFZ6XTjFi6VI3anfAFZYqUv4sceZpsOs5rSmUS9dSWK/PabkNt7dcUJqjT8dvqWkDqfhD67a8w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734417554; c=relaxed/simple; bh=IqPKfOrMgp0tZi2PstSwCopMQvxaFrQgiqW8jHk0THg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PVgwq/8wLyuiZ4mF8TIy4xrmrqvL9hfBi3puyAbhwZuOZOdlRvVLTst72b/TXf+CgGxrT+w3BYCamNLRsrIcYKidLaMCvDdwPDFSuT9umnL4ZBHjD6KB8uQeP0WV3u4HCOhfdntRrLaHvNRd9HRHCAdheJLVeA6XTOju7YMosfo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ya2W0Dvj; 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="Ya2W0Dvj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3393CC4CEDD; Tue, 17 Dec 2024 06:39:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734417554; bh=IqPKfOrMgp0tZi2PstSwCopMQvxaFrQgiqW8jHk0THg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ya2W0Dvj1g15Vvm9w1s8zm6thZq71VCT9x0qdlg21bRgN0YYPHJnZA5WKiECYnJyh gKUzaJcWXbyFB0dRirNgRSkt68Cps8zdAX6rfPpQg2KBSRpFNByBZ1sxWKgzJsn2hi MGm0fxnA7jfgWOPLEL1pNu0uRRbD2wetofmrYLXgIW815Wuh1FrjVUd+ryxIjmzp/Q ayKK8ZottGXVv7caNAIU6FIWxOmyNAdFUKHTzv7aKAzo2fnnpj4Bmw/HGcXLveTgiN 3abSoFqWKaxyK7i/Roc0lgulZMIgF+ed+WoI4O+mdZOh4KP9fB3csE+FTXwkyXWgkl HG1F7z/XTjYDA== 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 v4 bpf-next 6/6] selftests/bpf: Add __failure tests for set/remove xattr kfuncs Date: Mon, 16 Dec 2024 22:38:21 -0800 Message-ID: <20241217063821.482857-7-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241217063821.482857-1-song@kernel.org> References: <20241217063821.482857-1-song@kernel.org> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 43a26ec69a8e..f24285ae8d43 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; +}