All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roberto Sassu <roberto.sassu@huaweicloud.com>
To: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
	martin.lau@linux.dev, song@kernel.org, yhs@fb.com,
	john.fastabend@gmail.com, kpsingh@kernel.org, sdf@google.com,
	haoluo@google.com, jolsa@kernel.org, revest@chromium.org,
	jackmanb@chromium.org, mykolal@fb.com, paul@paul-moore.com,
	jmorris@namei.org, serge@hallyn.com, shuah@kernel.org
Cc: bpf@vger.kernel.org, linux-security-module@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org,
	Roberto Sassu <roberto.sassu@huawei.com>,
	stable@vger.kernel.org
Subject: [RFC][PATCH v2 4/7] bpf-lsm: Enforce return value limitations on security modules
Date: Wed,  7 Dec 2022 18:24:31 +0100	[thread overview]
Message-ID: <20221207172434.435893-5-roberto.sassu@huaweicloud.com> (raw)
In-Reply-To: <20221207172434.435893-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

With the patch for the LSM infrastructure to redefine the LSM_HOOK() macro
and to introduce the return value flags, it becomes straightforward for
eBPF to leverage this information to enforce return values limitations on
eBPF programs implementing security hooks.

Update the bpf_lsm_hooks BTF ID set, by including the return value flags.
Then, introduce bpf_lsm_is_ret_value_allowed(), which determines in which
intervals the R0 register (which contains the return value) falls, and
checks if the corresponding return value flag is set for those intervals.

In addition, for the interval including zero, ensure that the hook is not
inode_init_security, otherwise report that zero is not allowed. By LSM
conventions, LSMs should return zero only if they set an xattr, which
currently eBPF programs cannot do.

Finally, expose the new function and add a call to it in the verifier.

Cc: stable@vger.kernel.org # 5.7.x
Fixes: 9d3fdea789c8 ("bpf: lsm: Provide attachment points for BPF LSM programs")
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/linux/bpf_lsm.h |  9 +++++
 kernel/bpf/bpf_lsm.c    | 78 ++++++++++++++++++++++++++++++++++++++---
 kernel/bpf/verifier.c   |  7 ++--
 3 files changed, 87 insertions(+), 7 deletions(-)

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 2f5757085dfd..0ce5948f3662 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -29,6 +29,8 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
 
 bool bpf_lsm_is_sleepable_hook(u32 btf_id);
 bool bpf_lsm_is_trusted(const struct bpf_prog *prog);
+bool bpf_lsm_is_ret_value_allowed(struct bpf_verifier_log *vlog,
+				  struct bpf_reg_state *reg, u32 btf_id);
 
 static inline struct bpf_storage_blob *bpf_inode(
 	const struct inode *inode)
@@ -57,6 +59,13 @@ static inline bool bpf_lsm_is_trusted(const struct bpf_prog *prog)
 	return false;
 }
 
+static inline bool bpf_lsm_is_ret_value_allowed(struct bpf_verifier_log *vlog,
+						struct bpf_reg_state *reg,
+						u32 btf_id)
+{
+	return false;
+}
+
 static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
 				      const struct bpf_prog *prog)
 {
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 98f810f661a6..39ddafc06021 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -31,11 +31,11 @@ noinline RET bpf_lsm_##NAME(__VA_ARGS__)	\
 #undef LSM_HOOK
 
 #define LSM_HOOK(RET, DEFAULT, RET_FLAGS, NAME, ...) \
-	BTF_ID(func, bpf_lsm_##NAME)
-BTF_SET_START(bpf_lsm_hooks)
+	BTF_ID_FLAGS(func, bpf_lsm_##NAME, RET_FLAGS)
+BTF_SET8_START(bpf_lsm_hooks)
 #include <linux/lsm_hook_defs.h>
 #undef LSM_HOOK
-BTF_SET_END(bpf_lsm_hooks)
+BTF_SET8_END(bpf_lsm_hooks)
 
 /* List of LSM hooks that should operate on 'current' cgroup regardless
  * of function signature.
@@ -105,7 +105,7 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
 		return -EINVAL;
 	}
 
-	if (!btf_id_set_contains(&bpf_lsm_hooks, prog->aux->attach_btf_id)) {
+	if (!btf_id_set8_contains(&bpf_lsm_hooks, prog->aux->attach_btf_id)) {
 		bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
 			prog->aux->attach_btf_id, prog->aux->attach_func_name);
 		return -EINVAL;
@@ -367,6 +367,76 @@ bool bpf_lsm_is_trusted(const struct bpf_prog *prog)
 	return !btf_id_set_contains(&untrusted_lsm_hooks, prog->aux->attach_btf_id);
 }
 
+BTF_SET_START(zero_forbidden_lsm_hooks)
+BTF_ID(func, bpf_lsm_inode_init_security)
+BTF_SET_END(zero_forbidden_lsm_hooks)
+
+bool bpf_lsm_is_ret_value_allowed(struct bpf_verifier_log *vlog,
+				  struct bpf_reg_state *reg, u32 btf_id)
+{
+	u32 *id = btf_id_set8_contains(&bpf_lsm_hooks, btf_id);
+	s64 smin_value = reg->smin_value;
+	s64 smax_value = reg->smax_value;
+	u32 *ret_flags = id + 1;
+
+	/* See no_alu32/test_bpf_cookie.bpf.o for how return -EPERM is compiled:
+	 *
+	 * 11:	18 06 00 00 ff ff ff ff 00 00 00 00 00 00 00 00	r6 = 4294967295 ll
+	 * 13:	67 00 00 00 20 00 00 00	r0 <<= 32
+	 * 14:	77 00 00 00 20 00 00 00	r0 >>= 32
+	 * 15:	5d 08 07 00 00 00 00 00	if r8 != r0 goto +7 <LBB11_3>
+	 *
+	 * This causes predicted values to be:
+	 * smin_value = 0xffffffff, smax_value = 0xffffffff,
+	 * s32_min_value = 0xffffffff, s32_max_value = 0xffffffff,
+	 *
+	 * despite it is an ALU64 operation. So, checking reg->alu32 is not
+	 * enough. Then, if after casting the 64 bit values they are equal to
+	 * the 32 bit ones, use the latter ones (the LSM infrastructure takes
+	 * an int).
+	 */
+	if ((reg->s32_min_value == (u32)smin_value &&
+	    reg->s32_max_value == (u32)smax_value) || reg->alu32) {
+		smin_value = reg->s32_min_value;
+		smax_value = reg->s32_max_value;
+	}
+
+	/* Interval includes < 0 values. */
+	if (smin_value < 0) {
+		if (!(*ret_flags & LSM_RET_NEG)) {
+			bpf_log(vlog, "Invalid R0, cannot return < 0\n");
+			return false;
+		}
+	}
+
+	/* Interval includes 0. */
+	if (smin_value <= 0 && smax_value >= 0) {
+		if (!(*ret_flags & LSM_RET_ZERO) ||
+		    btf_id_set_contains(&zero_forbidden_lsm_hooks, btf_id)) {
+			bpf_log(vlog, "Invalid R0, cannot return 0\n");
+			return false;
+		}
+	}
+
+	/* Interval includes 1. */
+	if (smin_value <= 1 && smax_value >= 1) {
+		if (!(*ret_flags & LSM_RET_ONE)) {
+			bpf_log(vlog, "Invalid R0, cannot return 1\n");
+			return false;
+		}
+	}
+
+	/* Interval includes > 1 values. */
+	if (smax_value > 1) {
+		if (!(*ret_flags & LSM_RET_GT_ONE)) {
+			bpf_log(vlog, "Invalid R0, cannot return > 1\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
 const struct bpf_prog_ops lsm_prog_ops = {
 };
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index edce85c425a2..5d13b7f42238 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12064,9 +12064,10 @@ static int check_return_code(struct bpf_verifier_env *env)
 
 	case BPF_PROG_TYPE_LSM:
 		if (env->prog->expected_attach_type != BPF_LSM_CGROUP) {
-			/* Regular BPF_PROG_TYPE_LSM programs can return
-			 * any value.
-			 */
+			if (!bpf_lsm_is_ret_value_allowed(&env->log, reg,
+						env->prog->aux->attach_btf_id))
+				return -EINVAL;
+
 			return 0;
 		}
 		if (!env->prog->aux->attach_func_proto->type) {
-- 
2.25.1


  parent reply	other threads:[~2022-12-07 17:26 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-07 17:24 [RFC][PATCH v2 0/7] bpf-lsm: Check return values of security modules Roberto Sassu
2022-12-07 17:24 ` [RFC][PATCH v2 1/7] bpf: Remove superfluous btf_id_set_contains() declaration Roberto Sassu
2022-12-07 17:24 ` [RFC][PATCH v2 2/7] bpf: Mark ALU32 operations in bpf_reg_state structure Roberto Sassu
2022-12-11  2:28   ` Alexei Starovoitov
2022-12-12 12:44     ` Roberto Sassu
2022-12-12 17:04       ` Alexei Starovoitov
2022-12-12 18:10         ` Roberto Sassu
2022-12-07 17:24 ` [RFC][PATCH v2 3/7] lsm: Redefine LSM_HOOK() macro to add return value flags as argument Roberto Sassu
2022-12-07 17:24 ` Roberto Sassu [this message]
2022-12-07 17:24 ` [RFC][PATCH v2 5/7] selftests/bpf: Check if return values of LSM programs are allowed Roberto Sassu
2022-12-07 17:24 ` [RFC][PATCH v2 6/7] selftests/bpf: Prevent positive ret values in test_lsm and verify_pkcs7_sig Roberto Sassu
2022-12-07 17:24 ` [RFC][PATCH v2 7/7] selftests/bpf: Change return value in test_libbpf_get_fd_by_id_opts.c Roberto Sassu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221207172434.435893-5-roberto.sassu@huaweicloud.com \
    --to=roberto.sassu@huaweicloud.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=haoluo@google.com \
    --cc=jackmanb@chromium.org \
    --cc=jmorris@namei.org \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kpsingh@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=mykolal@fb.com \
    --cc=paul@paul-moore.com \
    --cc=revest@chromium.org \
    --cc=roberto.sassu@huawei.com \
    --cc=sdf@google.com \
    --cc=serge@hallyn.com \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.