@@ -28,6 +28,10 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog);
bool bpf_lsm_is_sleepable_hook(u32 btf_id);
+bool bpf_lsm_can_ret_pos_value(u32 btf_id);
+bool bpf_lsm_can_ret_only_one_as_pos_value(u32 btf_id);
+bool bpf_lsm_cannot_ret_zero(u32 btf_id);
+bool bpf_lsm_cannot_ret_neg_value(u32 btf_id);
static inline struct bpf_storage_blob *bpf_inode(
const struct inode *inode)
@@ -51,6 +55,26 @@ static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id)
return false;
}
+static inline bool bpf_lsm_can_ret_pos_value(u32 btf_id)
+{
+ return false;
+}
+
+static inline bool bpf_lsm_can_ret_only_one_as_pos_value(u32 btf_id)
+{
+ return false;
+}
+
+static inline bool bpf_lsm_cannot_ret_zero(u32 btf_id)
+{
+ return false;
+}
+
+static inline bool bpf_lsm_cannot_ret_neg_value(u32 btf_id)
+{
+ return false;
+}
+
static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
{
@@ -348,6 +348,62 @@ bool bpf_lsm_is_sleepable_hook(u32 btf_id)
return btf_id_set_contains(&sleepable_lsm_hooks, btf_id);
}
+/* The set of hooks which are allowed to return a positive value. */
+BTF_SET_START(pos_ret_value_lsm_hooks)
+BTF_ID(func, bpf_lsm_vm_enough_memory)
+BTF_ID(func, bpf_lsm_inode_getsecurity)
+BTF_ID(func, bpf_lsm_inode_listsecurity)
+BTF_ID(func, bpf_lsm_inode_need_killpriv)
+BTF_ID(func, bpf_lsm_inode_copy_up_xattr)
+BTF_ID(func, bpf_lsm_getprocattr)
+BTF_ID(func, bpf_lsm_setprocattr)
+BTF_ID(func, bpf_lsm_xfrm_state_pol_flow_match)
+BTF_ID(func, bpf_lsm_key_getsecurity)
+BTF_ID(func, bpf_lsm_ismaclabel)
+BTF_ID(func, bpf_lsm_audit_rule_known)
+BTF_ID(func, bpf_lsm_audit_rule_match)
+BTF_SET_END(pos_ret_value_lsm_hooks)
+
+bool bpf_lsm_can_ret_pos_value(u32 btf_id)
+{
+ return btf_id_set_contains(&pos_ret_value_lsm_hooks, btf_id);
+}
+
+BTF_SET_START(one_ret_value_lsm_hooks)
+BTF_ID(func, bpf_lsm_vm_enough_memory)
+BTF_ID(func, bpf_lsm_inode_copy_up_xattr)
+BTF_ID(func, bpf_lsm_xfrm_state_pol_flow_match)
+BTF_ID(func, bpf_lsm_ismaclabel)
+BTF_ID(func, bpf_lsm_audit_rule_known)
+BTF_ID(func, bpf_lsm_audit_rule_match)
+BTF_SET_END(one_ret_value_lsm_hooks)
+
+bool bpf_lsm_can_ret_only_one_as_pos_value(u32 btf_id)
+{
+ return btf_id_set_contains(&one_ret_value_lsm_hooks, btf_id);
+}
+
+/* The set of hooks which are not allowed to return zero. */
+BTF_SET_START(not_zero_ret_value_lsm_hooks)
+BTF_ID(func, bpf_lsm_inode_init_security)
+BTF_SET_END(not_zero_ret_value_lsm_hooks)
+
+bool bpf_lsm_cannot_ret_zero(u32 btf_id)
+{
+ return btf_id_set_contains(¬_zero_ret_value_lsm_hooks, btf_id);
+}
+
+/* The set of hooks which are not allowed to return a negative value. */
+BTF_SET_START(not_neg_ret_value_lsm_hooks)
+BTF_ID(func, bpf_lsm_vm_enough_memory)
+BTF_ID(func, bpf_lsm_audit_rule_known)
+BTF_SET_END(not_neg_ret_value_lsm_hooks)
+
+bool bpf_lsm_cannot_ret_neg_value(u32 btf_id)
+{
+ return btf_id_set_contains(¬_neg_ret_value_lsm_hooks, btf_id);
+}
+
const struct bpf_prog_ops lsm_prog_ops = {
};
@@ -10622,9 +10622,38 @@ 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.
- */
+ /* < 0 */
+ if (tnum_in(tnum_range((u64)(~0) << 31, (u64)(~0)), reg->var_off)) {
+ if (bpf_lsm_cannot_ret_neg_value(env->prog->aux->attach_btf_id)) {
+ verbose(env, "Invalid R0, cannot return negative value\n");
+ return -EINVAL;
+ }
+ /* = 0 */
+ } else if (tnum_equals_const(reg->var_off, 0)) {
+ if (bpf_lsm_cannot_ret_zero(env->prog->aux->attach_btf_id)) {
+ verbose(env, "Invalid R0, cannot return zero value\n");
+ return -EINVAL;
+ }
+ /* = 1 */
+ } else if (tnum_equals_const(reg->var_off, 1)) {
+ if (!bpf_lsm_can_ret_pos_value(env->prog->aux->attach_btf_id)) {
+ verbose(env, "Invalid R0, cannot return positive value\n");
+ return -EINVAL;
+ }
+ /* > 1 */
+ } else {
+ if (!bpf_lsm_can_ret_pos_value(env->prog->aux->attach_btf_id)) {
+ verbose(env, "Invalid R0, cannot return positive value\n");
+ return -EINVAL;
+ }
+
+ if (bpf_lsm_can_ret_only_one_as_pos_value(env->prog->aux->attach_btf_id)) {
+ verbose(env,
+ "Invalid R0, can return only one as positive value\n");
+ return -EINVAL;
+ }
+ }
+
return 0;
}
if (!env->prog->aux->attach_func_proto->type) {