@@ -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)
{
@@ -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 = {
};
@@ -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) {