From patchwork Thu Dec 19 22:14:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915895 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 426DD1C5CB6; Thu, 19 Dec 2024 22:14:52 +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=1734646492; cv=none; b=t8i5YZXjjLi3gn3vndhk7o4cqhgwD8fJh38mlyqdWNWPFu780eEE0/X/zt8C1JMXjTopX7J3+cYf/iIdv7V5gaSWGQKm2OuYu7V2wvOYVCkqH5SYh17tvBch20VMaWlN3Z/d6nYC5uA8r9uURPQel1UO6dlkkpfEIXhVwguGd4Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646492; c=relaxed/simple; bh=5LaYnZ+gbcvsP52hIhnW1K3ketWNyG+MdzJx0uYzW8c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jv0Sn29J+coBY+2nMEB3/Pa8pJJl0HFmWAmtGDQu/lgHSA4kOqDDkRGioBGyXUj6v3MNiHn3Y/m5gi4bEqgJoATQPZbKFFFj5ZnoPRJTVpMP6xaGTLgcUXD18iK6B3XT7DCnOObfmwXXKARLQeNJ/oR2WsDbKtAyFVyQLxQpxQM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=E4htpseN; 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="E4htpseN" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 79350C4CED0; Thu, 19 Dec 2024 22:14:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646492; bh=5LaYnZ+gbcvsP52hIhnW1K3ketWNyG+MdzJx0uYzW8c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=E4htpseNUYZFqjOnwNvUi6IPyhmuRCGIWywzVVHS3/sf5YGy24G8kteSEgaWlqfLa 4YlTpoGdu61+w7NBtdk4020Anu47YNYHf0s8oT6kHZ/fBjIDQmsgUUpclvQHDfl+Ke bt6aQNjCY2kd2dX/UCOFzUdSikRZrCPH/kQigskdh72yzhO8hDcwZx7ofe/pcf15OM MN2DYDY5pBooIeianYVsme1x2Rc14ghQoncwG3dr0uYfPpcLxhs7SDjm1rYFUbtglV e6/Hw7/paBDrzVQ6zN99+doq3xw1o7lbAoEOGYPGrqdk7mcw4bPjCfRYP2QlMto2B+ FfmyCxBQlSdcA== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu , Christian Brauner , Jan Kara Subject: [PATCH v7 bpf-next 1/7] fs/xattr: bpf: Introduce security.bpf. xattr name prefix Date: Thu, 19 Dec 2024 14:14:33 -0800 Message-ID: <20241219221439.2455664-2-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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 Thu Dec 19 22:14:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915896 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 0BA451D8DF6; Thu, 19 Dec 2024 22:14: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=1734646499; cv=none; b=Z0NvztP9GHdLY0af5P8zaskwIFwge/OeztTrY10KCMhOciVtQSegDsvoPK7WauW2Ku5iubn/LimIRLQIJ7Egm0iHvYZFMZPYYtHeSX4SzZmSaKHIru1nPOQa+oWkP+9PTm/wC5tHmfdkFWEnzY95OD80mRh7sAVau4J04j5kguM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646499; c=relaxed/simple; bh=46MoxRliv3cRnyQt0Z0/+IJ+vRdQg9CV9gODL3IHbNo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=e+tK6gLl1Z/JzeJGlbkUt0GrzV5dCxl+Pwzt0gZEGGsu6DHU00Nw7gr63160MxAznbC0+xdQRilJlUt5MMCD3gd85LEzvB5l0Hv4rIDI6QRZxZWOcQkXeDgsMsfj++mO5+IkGjkPUBNQkWudJxbh4KvFaNpjq5h5EXzIz+9tGYU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YmnkVJzm; 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="YmnkVJzm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 817EAC4CED0; Thu, 19 Dec 2024 22:14:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646498; bh=46MoxRliv3cRnyQt0Z0/+IJ+vRdQg9CV9gODL3IHbNo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YmnkVJzm+M+XJhF0yG7N2sbZijPG3XT2pp2MuCFPmVQarGMJwG+eQozhfNI9AQ7CO k1NA6kFXpg8RagrFIr1wJbuBnQhEwoG0glf5XNCmw8cZQKCzsmWIsi5bgEv+i8rCOI j26HDOVoM0e8hRoHgbf5ICL1yqxpPtE6PtGo/fOSeVKg2jPtmR3CSHIB3apcWu/ZQf MvtuBtNN27sxTscaGlFi2jfdyH/hPG0muNJRvKlZxC3BSEs+liChlFItUVP7molokw XWTx41W7O/tFCi3tkTP0Dse2aUCkYwxSDzrSq4tQny8qmxYGUBr+DK0hOkc+wAzwlE v+ArtNmN2+dwQ== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu Subject: [PATCH v7 bpf-next 2/7] selftests/bpf: Extend test fs_kfuncs to cover security.bpf. xattr names Date: Thu, 19 Dec 2024 14:14:34 -0800 Message-ID: <20241219221439.2455664-3-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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 Thu Dec 19 22:14:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915897 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 15EDA1C07EC; Thu, 19 Dec 2024 22:15:05 +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=1734646506; cv=none; b=T5PTPvSixHdkdMzlsS31KQdfnbwinUI6rf5MQWl7jLZe2o/XToSsiGKAmRoS7LjT4jmdnnMmsk6XxyLS5vnAWMnXhoIkLQztm1wQz87sDeKLVZzItUpsbJm6aaB8VxWDk5Kv9RvZQfdAHqXjVlZ2DoHNv/pVYfLANOMya9lkntM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646506; c=relaxed/simple; bh=wFf35YVFFsrsYjOIPa/lLhJz+32cyCFyYbyRWYcEsrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=t6FTHFJoVjxPrh/EHCtyGU6L2MTyaWW6fKb6zjS+wY7EdBLrWgYazCVNEI6NZmtPVoWT8RUVT7ip/QuzjM5VtUfdvgo16OX2ipVtKBTR6YwAbGQ1wgdMYjbAjX2Act2RQxi4ybOz4DXqfBaDTHMajyu80BcW557OiQ8MV8z5Qms= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qsUAeEsk; 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="qsUAeEsk" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4C84CC4CECE; Thu, 19 Dec 2024 22:15:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646505; bh=wFf35YVFFsrsYjOIPa/lLhJz+32cyCFyYbyRWYcEsrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qsUAeEskZKQajnB6+BUGvLmFSo5s0ZtF1eygQXMk7CBWscze/1lPiOz1tECExjnJ0 1MyG/BV3qrBcFWyYIbmLcoRsfq9qAEOu/0a0TqyTlKJDmtpUIjrO+N50gDG2k2Tgys ViSDmotUzfG59e9qWSvXh/7hyeQhazIh7j5xYz94OrWebf7kWUREIgCnbUXplDchWK w/VAMY1EB2aNmJ2Mb7JU4HMty63urGwP+XGtAo3CbQ61mXNJ5hDujy2/8kM2//RAC4 P8Cenq53oxO8PnLCTzXAU9SACxBC3DecfUQs8P9eFYXcnE1KFkciPfmCZG9h6GIE9l 0AEt4uOq+kXpA== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu Subject: [PATCH v7 bpf-next 3/7] bpf: lsm: Add two more sleepable hooks Date: Thu, 19 Dec 2024 14:14:35 -0800 Message-ID: <20241219221439.2455664-4-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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 Thu Dec 19 22:14:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915898 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 0158D1D1724; Thu, 19 Dec 2024 22:15:12 +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=1734646513; cv=none; b=GFsmQB23uQpOCS8RZQan3k9w3bJ3PE5fy+j10QkbZznLjlm856Vv8b+DotEmeDRSO1N1xBpc3zxtmUADjOS2rwdDFEEs33UK64m/rcaf4OIFx0ia8I7lO557p8Nc4XR1mmUZokuIIH+UndHu8ujdscAQluQiJJRIF8wF+RPEblM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646513; c=relaxed/simple; bh=VT6STx1nkLhitziAY2X6lgn25F461WsE5mNsVM2MJeA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ku6geDQnoEqu0msVVLl6P7YQ6KfMBCrmejKDdViOJWiCUg+IUKVXhyE+Doyfn1jhcd2jBsnzzO0FcsV6Hfji8LqKNbXHo2AJ6Vdca9HoJiwSJX25b2L23UUtq6BmmtC7kVKvsj5M5VjRLhAa5kF0SxvjCcIIDct68ogZUr6oLmU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hSbFWsB4; 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="hSbFWsB4" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 21F53C4CECE; Thu, 19 Dec 2024 22:15:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646512; bh=VT6STx1nkLhitziAY2X6lgn25F461WsE5mNsVM2MJeA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hSbFWsB4RTPtuRbPC/3bNmeHdX61+G5TDzzOPjPc3JxLDHqXaIjHNhtPh+Wm5K6uS EIgSwMZ7fp3SWrGag4bKCFEAunmYReuU2dgCptH7s+8diymAQ26/Net81bq4aO99yw gG0R5zytCxhZk5ncXqpkd2b96CUaEroUEjGNGU5EkNFSBI/EDL3jZMp8Gs7ctabmZi ccgCotShiUymwLxglAfTtyaf8bOwEgPS/27mRMz69goO2wMO7tyQdLh+It53xH7CEm JIylN6WESd2KhqViEqYEUSubB2BYZCNEmLvHzDOg7aUMz4Tdm/rMQJUZcBJaP4hrsh M1pm248qe8+GA== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu Subject: [PATCH v7 bpf-next 4/7] bpf: Extend btf_kfunc_id_set to handle kfunc polymorphism Date: Thu, 19 Dec 2024 14:14:36 -0800 Message-ID: <20241219221439.2455664-5-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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 Polymorphism exists in kernel functions, BPF helpers, as well as kfuncs. When called from different contexts, it is necessary to pick the right version of a kfunc. One of such example is bpf_dynptr_from_skb vs. bpf_dynptr_from_skb_rdonly. To avoid the burden on the users, the verifier can inspect the calling context and select the right version of kfunc. However, with more kfuncs being added to the kernel, it is not scalable to push all these logic to the verifiler. Extend btf_kfunc_id_set to handle kfunc polymorphism. Specifically, a list of kfuncs, "hidden_set", and a new method "remap" is added to btf_kfunc_id_set. kfuncs in hidden_set do not have BTF_SET8_KFUNCS flag, and are not exposed in vmlinux.h. The remap method is used to inspect the calling context, and when necessary, remap the user visible kfuncs (for example, bpf_dynptr_from_skb), to its hidden version (for example, bpf_dynptr_from_skb_rdonly). The verifier calls in these remap logic via the new btf_kfunc_id_remap() API, and picks the right kfuncs for the context. Signed-off-by: Song Liu --- include/linux/btf.h | 20 +++++++ include/linux/btf_ids.h | 3 ++ kernel/bpf/btf.c | 117 ++++++++++++++++++++++++++++++++++------ kernel/bpf/verifier.c | 6 ++- 4 files changed, 127 insertions(+), 19 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index 4214e76c9168..64f57edba626 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -114,11 +114,23 @@ struct btf_id_set; struct bpf_prog; typedef int (*btf_kfunc_filter_t)(const struct bpf_prog *prog, u32 kfunc_id); +typedef u32 (*btf_kfunc_remap_t)(const struct bpf_prog *prog, u32 kfunc_id); struct btf_kfunc_id_set { struct module *owner; struct btf_id_set8 *set; + + /* *hidden_set* contains kfuncs that are not exposed as kfunc in + * vmlinux.h. These kfuncs are usually a variation of a kfunc + * in *set*. + */ + struct btf_id_set8 *hidden_set; btf_kfunc_filter_t filter; + + /* *remap* method remaps kfuncs in *set* to proper version in + * *hidden_set*. + */ + btf_kfunc_remap_t remap; }; struct btf_id_dtor_kfunc { @@ -570,6 +582,8 @@ u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, const struct btf_kfunc_id_set *s); int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset); +u32 btf_kfunc_id_remap(const struct btf *btf, u32 kfunc_btf_id, + const struct bpf_prog *prog); s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id); int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt, struct module *owner); @@ -632,6 +646,12 @@ static inline u32 *btf_kfunc_id_set_contains(const struct btf *btf, { return NULL; } +static inline u32 btf_kfunc_id_remap(const struct btf *btf, u32 kfunc_btf_id, + const struct bpf_prog *prog) +{ + return kfunc_btf_id; +} + static inline int register_btf_kfunc_id_set(enum bpf_prog_type prog_type, const struct btf_kfunc_id_set *s) { diff --git a/include/linux/btf_ids.h b/include/linux/btf_ids.h index 139bdececdcf..e95b72fbba48 100644 --- a/include/linux/btf_ids.h +++ b/include/linux/btf_ids.h @@ -212,6 +212,9 @@ extern struct btf_id_set8 name; #define BTF_KFUNCS_START(name) \ __BTF_SET8_START(name, local, BTF_SET8_KFUNCS) +#define BTF_HIDDEN_KFUNCS_START(name) \ +__BTF_SET8_START(name, local, 0) + #define BTF_KFUNCS_END(name) \ BTF_SET8_END(name) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 28246c59e12e..e7766f8e02ba 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -226,6 +226,7 @@ enum { BTF_KFUNC_SET_MAX_CNT = 256, BTF_DTOR_KFUNC_MAX_CNT = 256, BTF_KFUNC_FILTER_MAX_CNT = 16, + BTF_KFUNC_REMAP_MAX_CNT = 16, }; struct btf_kfunc_hook_filter { @@ -233,9 +234,15 @@ struct btf_kfunc_hook_filter { u32 nr_filters; }; +struct btf_kfunc_hook_remap { + btf_kfunc_remap_t remaps[BTF_KFUNC_REMAP_MAX_CNT]; + u32 nr_remaps; +}; + struct btf_kfunc_set_tab { struct btf_id_set8 *sets[BTF_KFUNC_HOOK_MAX]; struct btf_kfunc_hook_filter hook_filters[BTF_KFUNC_HOOK_MAX]; + struct btf_kfunc_hook_remap hook_remaps[BTF_KFUNC_HOOK_MAX]; }; struct btf_id_dtor_kfunc_tab { @@ -8377,16 +8384,35 @@ static int btf_check_kfunc_protos(struct btf *btf, u32 func_id, u32 func_flags) /* Kernel Function (kfunc) BTF ID set registration API */ +static void btf_add_kfunc_to_set(struct btf *btf, struct btf_id_set8 *set, + struct btf_id_set8 *add_set) +{ + u32 i; + + if (!add_set) + return; + /* Concatenate the two sets */ + memcpy(set->pairs + set->cnt, add_set->pairs, add_set->cnt * sizeof(set->pairs[0])); + /* Now that the set is copied, update with relocated BTF ids */ + for (i = set->cnt; i < set->cnt + add_set->cnt; i++) + set->pairs[i].id = btf_relocate_id(btf, set->pairs[i].id); + + set->cnt += add_set->cnt; + + sort(set->pairs, set->cnt, sizeof(set->pairs[0]), btf_id_cmp_func, NULL); +} + static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, const struct btf_kfunc_id_set *kset) { struct btf_kfunc_hook_filter *hook_filter; - struct btf_id_set8 *add_set = kset->set; + struct btf_kfunc_hook_remap *hook_remap; bool vmlinux_set = !btf_is_module(btf); bool add_filter = !!kset->filter; + bool add_remap = !!kset->remap; struct btf_kfunc_set_tab *tab; struct btf_id_set8 *set; - u32 set_cnt, i; + u32 set_cnt, add_cnt, i; int ret; if (hook >= BTF_KFUNC_HOOK_MAX) { @@ -8394,14 +8420,16 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, goto end; } - if (!add_set->cnt) + add_cnt = kset->set->cnt; + if (kset->hidden_set) + add_cnt += kset->hidden_set->cnt; + + if (!add_cnt) return 0; tab = btf->kfunc_set_tab; if (tab && add_filter) { - u32 i; - hook_filter = &tab->hook_filters[hook]; for (i = 0; i < hook_filter->nr_filters; i++) { if (hook_filter->filters[i] == kset->filter) { @@ -8416,6 +8444,21 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, } } + if (tab && add_remap) { + hook_remap = &tab->hook_remaps[hook]; + for (i = 0; i < hook_remap->nr_remaps; i++) { + if (hook_remap->remaps[i] == kset->remap) { + add_remap = false; + break; + } + } + + if (add_remap && hook_remap->nr_remaps == BTF_KFUNC_REMAP_MAX_CNT) { + ret = -E2BIG; + goto end; + } + } + if (!tab) { tab = kzalloc(sizeof(*tab), GFP_KERNEL | __GFP_NOWARN); if (!tab) @@ -8444,19 +8487,19 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, */ set_cnt = set ? set->cnt : 0; - if (set_cnt > U32_MAX - add_set->cnt) { + if (set_cnt > U32_MAX - add_cnt) { ret = -EOVERFLOW; goto end; } - if (set_cnt + add_set->cnt > BTF_KFUNC_SET_MAX_CNT) { + if (set_cnt + add_cnt > BTF_KFUNC_SET_MAX_CNT) { ret = -E2BIG; goto end; } /* Grow set */ set = krealloc(tab->sets[hook], - offsetof(struct btf_id_set8, pairs[set_cnt + add_set->cnt]), + offsetof(struct btf_id_set8, pairs[set_cnt + add_cnt]), GFP_KERNEL | __GFP_NOWARN); if (!set) { ret = -ENOMEM; @@ -8468,20 +8511,18 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, set->cnt = 0; tab->sets[hook] = set; - /* Concatenate the two sets */ - memcpy(set->pairs + set->cnt, add_set->pairs, add_set->cnt * sizeof(set->pairs[0])); - /* Now that the set is copied, update with relocated BTF ids */ - for (i = set->cnt; i < set->cnt + add_set->cnt; i++) - set->pairs[i].id = btf_relocate_id(btf, set->pairs[i].id); - - set->cnt += add_set->cnt; - - sort(set->pairs, set->cnt, sizeof(set->pairs[0]), btf_id_cmp_func, NULL); + btf_add_kfunc_to_set(btf, set, kset->set); + btf_add_kfunc_to_set(btf, set, kset->hidden_set); if (add_filter) { hook_filter = &tab->hook_filters[hook]; hook_filter->filters[hook_filter->nr_filters++] = kset->filter; } + + if (add_remap) { + hook_remap = &tab->hook_remaps[hook]; + hook_remap->remaps[hook_remap->nr_remaps++] = kset->remap; + } return 0; end: btf_free_kfunc_set_tab(btf); @@ -8516,6 +8557,28 @@ static u32 *__btf_kfunc_id_set_contains(const struct btf *btf, return id + 1; } +static u32 __btf_kfunc_id_remap(const struct btf *btf, + enum btf_kfunc_hook hook, + u32 kfunc_btf_id, + const struct bpf_prog *prog) +{ + struct btf_kfunc_hook_remap *hook_remap; + u32 i, remap_id = 0; + + if (hook >= BTF_KFUNC_HOOK_MAX) + return 0; + if (!btf->kfunc_set_tab) + return 0; + hook_remap = &btf->kfunc_set_tab->hook_remaps[hook]; + + for (i = 0; i < hook_remap->nr_remaps; i++) { + remap_id = hook_remap->remaps[i](prog, kfunc_btf_id); + if (remap_id) + break; + } + return remap_id; +} + static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type) { switch (prog_type) { @@ -8584,6 +8647,26 @@ u32 *btf_kfunc_id_set_contains(const struct btf *btf, return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id, prog); } +/* Reference to the module (obtained using btf_try_get_module) + * corresponding to the struct btf *MUST* be held when calling this + * function from the verifier + */ +u32 btf_kfunc_id_remap(const struct btf *btf, u32 kfunc_btf_id, + const struct bpf_prog *prog) +{ + enum bpf_prog_type prog_type = resolve_prog_type(prog); + enum btf_kfunc_hook hook; + u32 remap_id; + + remap_id = __btf_kfunc_id_remap(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog); + if (remap_id) + return remap_id; + + hook = bpf_prog_type_to_kfunc_hook(prog_type); + remap_id = __btf_kfunc_id_remap(btf, hook, kfunc_btf_id, prog); + return remap_id ?: kfunc_btf_id; +} + u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog) { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f27274e933e5..ac45c1f2837c 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3029,13 +3029,14 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset) return btf_vmlinux ?: ERR_PTR(-ENOENT); } -static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset) +static int add_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, s16 offset) { const struct btf_type *func, *func_proto; struct bpf_kfunc_btf_tab *btf_tab; struct bpf_kfunc_desc_tab *tab; struct bpf_prog_aux *prog_aux; struct bpf_kfunc_desc *desc; + u32 func_id = insn->imm; const char *func_name; struct btf *desc_btf; unsigned long call_imm; @@ -3094,6 +3095,7 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset) return PTR_ERR(desc_btf); } + func_id = insn->imm = btf_kfunc_id_remap(desc_btf, insn->imm, env->prog); if (find_kfunc_desc(env->prog, func_id, offset)) return 0; @@ -3227,7 +3229,7 @@ static int add_subprog_and_kfunc(struct bpf_verifier_env *env) if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn)) ret = add_subprog(env, i + insn->imm + 1); else - ret = add_kfunc_call(env, insn->imm, insn->off); + ret = add_kfunc_call(env, insn, insn->off); if (ret < 0) return ret; From patchwork Thu Dec 19 22:14:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915899 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 107101D014E; Thu, 19 Dec 2024 22:15:19 +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=1734646520; cv=none; b=GcUEvF9njXtyByCoiz6KKtkPmQmDi6RllT6QKodyK3U6H9RzGB8nsn47cxZk0WBL8EI7hZx3bQaWTi/K9brK2xc919BsnAGtCorcBYkfUriGcpsMVFLDNrmk158SGEzOhbnA+3wB6yzAM+gQGmBi942J71CHHm57dEKv/SeYf0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646520; c=relaxed/simple; bh=0X9kUtsvLKkTifAc//9RFrImSq3U8YKJ4Fg+rCv1zno=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ExuWNdhyfFP6X2uOS0raOmtBa5GARCJfA58vwlOvKmemNKByY1lGUpe+suCWhhm805u/nWdfs5C3tLlp2lDEZdJBXWJAcSZcjzQXkwoSo8/lYchbfqkCUvHl69Y6lw+pkRQNCVqo6llwaX1KtqF914HFqyHCNzHXU8NEct0SC7A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=f12iwa4z; 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="f12iwa4z" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E8B3BC4CECE; Thu, 19 Dec 2024 22:15:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646519; bh=0X9kUtsvLKkTifAc//9RFrImSq3U8YKJ4Fg+rCv1zno=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=f12iwa4zck7k6pmC4mxljKdrp7EOUyosSBR3ffOhd4pH5y2gx/x/9w0iKXqPkxypI NWVJ0KKl3482393i2YoGrKMZ5x//2LrguupkTZFLElVwQzX35h+hbiuAkXLWXUeW9c Eblh55/cDQsap1NEIwi0CjI4APWoxUexFMoYlc/zUTNkebBW38Q/Ne1zNkB+d4h/bR 1qzqXl9+EUsm70hzdf/3XPllfC6Knt7bYW5J0IDhxXGFDIg+rhuryg/v+7l9Fi2TQy B9pKjXPf7s/HWSlH3LOQA8AH/Klzaoxe5wiyjeEjFav19/ub3MRZ93HbsljIdks1Cq TSNv+i3n5xwFQ== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu Subject: [PATCH v7 bpf-next 5/7] bpf: Use btf_kfunc_id_set.remap logic for bpf_dynptr_from_skb Date: Thu, 19 Dec 2024 14:14:37 -0800 Message-ID: <20241219221439.2455664-6-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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 btf_kfunc_id_set.remap can pick proper version of a kfunc for the calling context. Use this logic to select bpf_dynptr_from_skb or bpf_dynptr_from_skb_rdonly. This will make the verifier simpler. Unfortunately, btf_kfunc_id_set.remap cannot cover the DYNPTR_TYPE_SKB logic in check_kfunc_args(). This can be addressed later. Signed-off-by: Song Liu --- kernel/bpf/verifier.c | 28 ++++++++----------------- net/core/filter.c | 49 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ac45c1f2837c..e841ba22a6c6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -11677,6 +11677,7 @@ enum special_kfunc_type { KF_bpf_rbtree_add_impl, KF_bpf_rbtree_first, KF_bpf_dynptr_from_skb, + KF_bpf_dynptr_from_skb_rdonly, KF_bpf_dynptr_from_xdp, KF_bpf_dynptr_slice, KF_bpf_dynptr_slice_rdwr, @@ -11709,6 +11710,7 @@ BTF_ID(func, bpf_rbtree_add_impl) BTF_ID(func, bpf_rbtree_first) #ifdef CONFIG_NET BTF_ID(func, bpf_dynptr_from_skb) +BTF_ID(func, bpf_dynptr_from_skb_rdonly) BTF_ID(func, bpf_dynptr_from_xdp) #endif BTF_ID(func, bpf_dynptr_slice) @@ -11740,7 +11742,12 @@ BTF_ID(func, bpf_rbtree_add_impl) BTF_ID(func, bpf_rbtree_first) #ifdef CONFIG_NET BTF_ID(func, bpf_dynptr_from_skb) +BTF_ID(func, bpf_dynptr_from_skb_rdonly) BTF_ID(func, bpf_dynptr_from_xdp) +#else +BTF_ID_UNUSED +BTF_ID_UNUSED +BTF_ID_UNUSED #endif BTF_ID(func, bpf_dynptr_slice) BTF_ID(func, bpf_dynptr_slice_rdwr) @@ -12647,7 +12654,8 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ if (is_kfunc_arg_uninit(btf, &args[i])) dynptr_arg_type |= MEM_UNINIT; - if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb]) { + if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb] || + meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb_rdonly]) { dynptr_arg_type |= DYNPTR_TYPE_SKB; } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp]) { dynptr_arg_type |= DYNPTR_TYPE_XDP; @@ -20808,9 +20816,7 @@ static void specialize_kfunc(struct bpf_verifier_env *env, u32 func_id, u16 offset, unsigned long *addr) { struct bpf_prog *prog = env->prog; - bool seen_direct_write; void *xdp_kfunc; - bool is_rdonly; if (bpf_dev_bound_kfunc_id(func_id)) { xdp_kfunc = bpf_dev_bound_resolve_kfunc(prog, func_id); @@ -20820,22 +20826,6 @@ static void specialize_kfunc(struct bpf_verifier_env *env, } /* fallback to default kfunc when not supported by netdev */ } - - if (offset) - return; - - if (func_id == special_kfunc_list[KF_bpf_dynptr_from_skb]) { - seen_direct_write = env->seen_direct_write; - is_rdonly = !may_access_direct_pkt_data(env, NULL, BPF_WRITE); - - if (is_rdonly) - *addr = (unsigned long)bpf_dynptr_from_skb_rdonly; - - /* restore env->seen_direct_write to its original value, since - * may_access_direct_pkt_data mutates it - */ - env->seen_direct_write = seen_direct_write; - } } static void __fixup_collection_insert_kfunc(struct bpf_insn_aux_data *insn_aux, diff --git a/net/core/filter.c b/net/core/filter.c index 21131ec25f24..f12bcc1b21d1 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -12047,10 +12047,8 @@ __bpf_kfunc int bpf_sk_assign_tcp_reqsk(struct __sk_buff *s, struct sock *sk, #endif } -__bpf_kfunc_end_defs(); - -int bpf_dynptr_from_skb_rdonly(struct __sk_buff *skb, u64 flags, - struct bpf_dynptr *ptr__uninit) +__bpf_kfunc int bpf_dynptr_from_skb_rdonly(struct __sk_buff *skb, u64 flags, + struct bpf_dynptr *ptr__uninit) { struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)ptr__uninit; int err; @@ -12064,10 +12062,16 @@ int bpf_dynptr_from_skb_rdonly(struct __sk_buff *skb, u64 flags, return 0; } +__bpf_kfunc_end_defs(); + BTF_KFUNCS_START(bpf_kfunc_check_set_skb) BTF_ID_FLAGS(func, bpf_dynptr_from_skb, KF_TRUSTED_ARGS) BTF_KFUNCS_END(bpf_kfunc_check_set_skb) +BTF_HIDDEN_KFUNCS_START(bpf_kfunc_check_hidden_set_skb) +BTF_ID_FLAGS(func, bpf_dynptr_from_skb_rdonly, KF_TRUSTED_ARGS) +BTF_KFUNCS_END(bpf_kfunc_check_hidden_set_skb) + BTF_KFUNCS_START(bpf_kfunc_check_set_xdp) BTF_ID_FLAGS(func, bpf_dynptr_from_xdp) BTF_KFUNCS_END(bpf_kfunc_check_set_xdp) @@ -12080,9 +12084,46 @@ BTF_KFUNCS_START(bpf_kfunc_check_set_tcp_reqsk) BTF_ID_FLAGS(func, bpf_sk_assign_tcp_reqsk, KF_TRUSTED_ARGS) BTF_KFUNCS_END(bpf_kfunc_check_set_tcp_reqsk) +BTF_ID_LIST(bpf_dynptr_from_skb_list) +BTF_ID(func, bpf_dynptr_from_skb) +BTF_ID(func, bpf_dynptr_from_skb_rdonly) + +static u32 bpf_kfunc_set_skb_remap(const struct bpf_prog *prog, u32 kfunc_id) +{ + if (kfunc_id != bpf_dynptr_from_skb_list[0]) + return 0; + + switch (resolve_prog_type(prog)) { + /* Program types only with direct read access go here! */ + case BPF_PROG_TYPE_LWT_IN: + case BPF_PROG_TYPE_LWT_OUT: + case BPF_PROG_TYPE_LWT_SEG6LOCAL: + case BPF_PROG_TYPE_SK_REUSEPORT: + case BPF_PROG_TYPE_FLOW_DISSECTOR: + case BPF_PROG_TYPE_CGROUP_SKB: + return bpf_dynptr_from_skb_list[1]; + + /* Program types with direct read + write access go here! */ + case BPF_PROG_TYPE_SCHED_CLS: + case BPF_PROG_TYPE_SCHED_ACT: + case BPF_PROG_TYPE_XDP: + case BPF_PROG_TYPE_LWT_XMIT: + case BPF_PROG_TYPE_SK_SKB: + case BPF_PROG_TYPE_SK_MSG: + case BPF_PROG_TYPE_CGROUP_SOCKOPT: + return kfunc_id; + + default: + break; + } + return bpf_dynptr_from_skb_list[1]; +} + static const struct btf_kfunc_id_set bpf_kfunc_set_skb = { .owner = THIS_MODULE, .set = &bpf_kfunc_check_set_skb, + .hidden_set = &bpf_kfunc_check_hidden_set_skb, + .remap = &bpf_kfunc_set_skb_remap, }; static const struct btf_kfunc_id_set bpf_kfunc_set_xdp = { From patchwork Thu Dec 19 22:14:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915900 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 BE8C81CC8AD; Thu, 19 Dec 2024 22:15:26 +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=1734646527; cv=none; b=XGqav/oEBLpq74UlEHxGga15ZvOV/FBAmv8ukTYThDzVdgaWYqSucie9wIQVYqKEisjrac8MLcc6MlOrXzRKcm3QsycIy5+tN33gRmOFe3Y3RTLwvXbWxep4h5uDfLaUMziSLwU3k1hZb0axDv0FjWptPV9nBHRyb+v7zensIJ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646527; c=relaxed/simple; bh=kSxZsgViYjEOqqQgk3HdWxQuKcLYF3/PKCd1xMeMASc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LSEfmYqYUnPUguuJHx7hTHd+/9Vks2q3SKU6E5iYQW1QJeV/ylqXfl1qG4f8bxrU1wgrE1zS7wZKCSDxax3SGPNMqzOtw8rcDhl5BWId3rzfAkI92fcZqS4kD9LOiEfw7FE6TtCri7+aMpftYQaK5uH6xUXIDrhEUJ/r1c07DkE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hhGycL/A; 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="hhGycL/A" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C9AC6C4CECE; Thu, 19 Dec 2024 22:15:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646526; bh=kSxZsgViYjEOqqQgk3HdWxQuKcLYF3/PKCd1xMeMASc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hhGycL/AM4fniTm7AY639fpukX6EmWweUVxKDt3ycoBw7Alzv/AMxDKtE0xD+9LTY 9D6La/QdK5LMELoj0rhg2MNrBZRYm9c/vjSAHVwu+oypthSVcMEtXZQLARY7D8MIO/ OHoJ0A81/rM3TBbzi/oyAk/Ew0dO0QEak85RQEWg7szT5TCji7XDFtA3oy7MWDA61s 4QBe5qhyMXl5+tEP5uISFHDyV6GJ2NRy+2wVHulLEXMCbSkjH9Y+ZXGqdXlbkor62T O8MbmeL60TI7EzXBQYxvK0mjYV7xJldWBgR/URs3Opx3u7DHJGg2XFpdWIA7IgQGPp mkAE1DhsaCiZQ== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu Subject: [PATCH v7 bpf-next 6/7] bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs Date: Thu, 19 Dec 2024 14:14:38 -0800 Message-ID: <20241219221439.2455664-7-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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. Instead of requiring the user to know which version of the kfuncs to use, the verifier will pick the proper kfunc based on the calling hook. Signed-off-by: Song Liu --- fs/bpf_fs_kfuncs.c | 227 +++++++++++++++++++++++++++++++++++++++- include/linux/bpf_lsm.h | 2 + 2 files changed, 227 insertions(+), 2 deletions(-) diff --git a/fs/bpf_fs_kfuncs.c b/fs/bpf_fs_kfuncs.c index 8a65184c8c2c..f68c83bfb93f 100644 --- a/fs/bpf_fs_kfuncs.c +++ b/fs/bpf_fs_kfuncs.c @@ -2,10 +2,12 @@ /* Copyright (c) 2024 Google LLC. */ #include +#include #include #include #include #include +#include #include #include #include @@ -161,6 +163,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) @@ -170,20 +330,83 @@ BTF_ID_FLAGS(func, bpf_put_file, KF_RELEASE) BTF_ID_FLAGS(func, bpf_path_d_path, KF_TRUSTED_ARGS) BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS) +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_fs_kfunc_set_ids) +BTF_HIDDEN_KFUNCS_START(bpf_fs_kfunc_hidden_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_fs_kfunc_hidden_set_ids) + static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id) { - if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) || - prog->type == BPF_PROG_TYPE_LSM) + if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) && + !btf_id_set8_contains(&bpf_fs_kfunc_hidden_set_ids, kfunc_id)) + return 0; + if (prog->type == BPF_PROG_TYPE_LSM) return 0; return -EACCES; } +/* 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_ID_LIST(not_locked_fs_kfuncs) +BTF_ID(func, bpf_set_dentry_xattr) +BTF_ID(func, bpf_remove_dentry_xattr) + +BTF_ID_LIST(locked_fs_kfuncs) +BTF_ID(func, bpf_set_dentry_xattr_locked) +BTF_ID(func, bpf_remove_dentry_xattr_locked) + +static u32 bpf_fs_kfunc_remap(const struct bpf_prog *prog, u32 kfunc_id) +{ + if (!bpf_lsm_has_d_inode_locked(prog)) + return 0; + + if (kfunc_id == not_locked_fs_kfuncs[0]) + return locked_fs_kfuncs[0]; + if (kfunc_id == not_locked_fs_kfuncs[1]) + return locked_fs_kfuncs[1]; + + return 0; +} + static const struct btf_kfunc_id_set bpf_fs_kfunc_set = { .owner = THIS_MODULE, .set = &bpf_fs_kfunc_set_ids, + .hidden_set = &bpf_fs_kfunc_hidden_set_ids, .filter = bpf_fs_kfuncs_filter, + .remap = bpf_fs_kfunc_remap, }; static int __init bpf_fs_kfuncs_init(void) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index aefcd6564251..f4ab0dc1df69 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -48,6 +48,7 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, bpf_func_t *bpf_func) int bpf_lsm_get_retval_range(const struct bpf_prog *prog, struct bpf_retval_range *range); + #else /* !CONFIG_BPF_LSM */ static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) @@ -86,6 +87,7 @@ static inline int bpf_lsm_get_retval_range(const struct bpf_prog *prog, { return -EOPNOTSUPP; } + #endif /* CONFIG_BPF_LSM */ #endif /* _LINUX_BPF_LSM_H */ From patchwork Thu Dec 19 22:14:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 13915901 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 2B1261EBFF8; Thu, 19 Dec 2024 22:15:33 +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=1734646533; cv=none; b=kdtINfWQ4SbkfeHqZGdB7BJoFXtaNkEK5awmj9vqvSDs9xDxolX6W6ynAEWucaN2mmtdUs5sbjmVs2/mE3RGGHeXqnfV02NuxNh7EfcfYB6Lm2oJUY4e8oFkSIZjp6+cvbZzbhGr8XNejYjrb+h92P9+A4CXGzAIGSLMOdXjOBQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1734646533; c=relaxed/simple; bh=FQmK/zjfrxygp/Pf6guBS6Z2EkiYTrDZoPCVgsbDZ0g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PVfuEcf5oti/IIW2juPePirkzAO8+Kdh//gCUuA6itU/7AWKSihErOtFJcZiEYoK6ie2CzQjYi8ELksSYfNaKNs2qVegOkpU1vOLzynCnfj0vq3ZPxaOG/ZweBsi169I6hgnrD5tt7xD8suWs+D0Elg8NAdpw5D9/EGOpVUwW6g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CnmYzW2T; 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="CnmYzW2T" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A91F7C4CECE; Thu, 19 Dec 2024 22:15:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1734646533; bh=FQmK/zjfrxygp/Pf6guBS6Z2EkiYTrDZoPCVgsbDZ0g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CnmYzW2T66w7Th/SZCRvKxyjp75QUpxPE3ejpUqU564DZK1OcHkOCqImV37EMSh6/ rc0vVODIwqU7We2E4Yhv1L2M55t1Dhos1+64SCv311e4uSAnawVOjK8zMUwrJSC3pH t7zGBmT26MPLBwxAevV9vuhDSedEBfXqRVRsYVx/wQIyX9k07Mo+bSrjUjjENouqAe jnWRsUqTF2ZguHPalFFIfnAksxjw37t/Y5CY5o8P09O256n+80f5h7552CYLj2fp9Q LSu+1t2mB4UkXXcVBfr7DgCgWctjfGhKAWLc5m0lRzqSYgMdU2ZdlUSP1/0Nm5NPY4 JDlB6swLem8vQ== From: Song Liu To: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org Cc: kernel-team@meta.com, andrii@kernel.org, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, kpsingh@kernel.org, mattbobrowski@google.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, memxor@gmail.com, Song Liu Subject: [PATCH v7 bpf-next 7/7] selftests/bpf: Test kfuncs that set and remove xattr from BPF programs Date: Thu, 19 Dec 2024 14:14:39 -0800 Message-ID: <20241219221439.2455664-8-song@kernel.org> X-Mailer: git-send-email 2.43.5 In-Reply-To: <20241219221439.2455664-1-song@kernel.org> References: <20241219221439.2455664-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. Note that, in both tests, the BPF programs use the not _locked kfuncs. The verifier picks the proper kfuncs based on the calling context. Signed-off-by: Song Liu --- tools/testing/selftests/bpf/bpf_kfuncs.h | 5 + .../selftests/bpf/prog_tests/fs_kfuncs.c | 125 ++++++++++++++++ .../bpf/progs/test_set_remove_xattr.c | 133 ++++++++++++++++++ 3 files changed, 263 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..8215c9b3115e 100644 --- a/tools/testing/selftests/bpf/bpf_kfuncs.h +++ b/tools/testing/selftests/bpf/bpf_kfuncs.h @@ -87,4 +87,9 @@ 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(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..e49be3cc4a33 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_set_remove_xattr.c @@ -0,0 +1,133 @@ +// 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. + * It not necessary to differentiate the _locked version and the + * not-_locked version in the BPF program. The verifier will fix them up + * properly. + */ +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(dentry, xattr_bar, &value_ptr, 0); + if (!ret) + locked_set_security_bpf_bar_success = true; + ret = bpf_set_dentry_xattr(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(dentry, xattr_bar); + if (!ret) + locked_remove_security_bpf_bar_success = true; + + ret = bpf_remove_dentry_xattr(dentry, xattr_linux); + if (ret) + locked_remove_security_selinux_fail = true; + } + + return 0; +}