@@ -45,6 +45,8 @@ void bpf_inode_storage_free(struct inode *inode);
void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, bpf_func_t *bpf_func);
+bool bpf_lsm_has_retval_param(const struct bpf_prog *prog);
+
#else /* !CONFIG_BPF_LSM */
static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id)
@@ -78,6 +80,10 @@ static inline void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
{
}
+static inline bool bpf_lsm_has_retval_param(const struct bpf_prog *prog)
+{
+ return false;
+}
#endif /* CONFIG_BPF_LSM */
#endif /* _LINUX_BPF_LSM_H */
@@ -405,3 +405,18 @@ const struct bpf_verifier_ops lsm_verifier_ops = {
.get_func_proto = bpf_lsm_func_proto,
.is_valid_access = btf_ctx_access,
};
+
+BTF_SET_START(retval_param_lsm_hooks)
+BTF_ID(func, bpf_lsm_inode_need_killpriv)
+BTF_ID(func, bpf_lsm_inode_getsecurity)
+BTF_ID(func, bpf_lsm_inode_listsecurity)
+BTF_ID(func, bpf_lsm_getselfattr)
+BTF_ID(func, bpf_lsm_key_getsecurity)
+BTF_ID(func, bpf_lsm_audit_rule_match)
+BTF_SET_END(retval_param_lsm_hooks)
+
+bool bpf_lsm_has_retval_param(const struct bpf_prog *prog)
+{
+ return btf_id_set_contains(&retval_param_lsm_hooks,
+ prog->aux->attach_btf_id);
+}
@@ -6499,8 +6499,22 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
*/
return true;
- if (is_int_ptr(btf, t))
+
+ if (is_int_ptr(btf, t)) {
+ /* the retval param for LSM hook is always the last param. */
+ if (arg == nr_args - 1 &&
+ prog->expected_attach_type == BPF_LSM_MAC &&
+ bpf_lsm_has_retval_param(prog)) {
+ u32 id;
+
+ btf_type_skip_modifiers(btf, t->type, &id);
+ info->btf = btf;
+ /* the retval param should never be NULL */
+ info->reg_type = PTR_TO_BTF_ID | PTR_TRUSTED;
+ info->btf_id = id;
+ }
return true;
+ }
/* this is a pointer to another type */
for (i = 0; i < prog->aux->ctx_arg_info_size; i++) {
@@ -6527,7 +6527,38 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
return -EACCES;
}
- if (env->ops->btf_struct_access && !type_is_alloc(reg->type) && atype == BPF_WRITE) {
+ if (btf_type_is_int(t)) {
+ u32 tsize;
+ const char *tname;
+ const struct btf_type *err;
+ const char *access = atype == BPF_READ ? "read" : "write";
+
+ /* only BPF LSM is allowed */
+ if (WARN_ON_ONCE(env->prog->expected_attach_type != BPF_LSM_MAC)) {
+ verbose(env, "verifier internal error: not BPF LSM\n");
+ return -EACCES;
+ }
+
+ tname = btf_name_by_offset(reg->btf, t->name_off);
+ if (off != 0) {
+ verbose(env, "invalid %s offset: %d (expected 0, type=%s)\n",
+ access, off, tname);
+ return -EACCES;
+ }
+
+ err = btf_resolve_size(reg->btf, t, &tsize);
+ if (IS_ERR(err)) {
+ verbose(env, "unable to resolve the size of type '%s': %ld\n",
+ tname, PTR_ERR(err));
+ return PTR_ERR(err);
+ }
+ if (size != tsize) {
+ verbose(env, "invalid %s size: %d (expected %u, type=%s)\n",
+ access, size, tsize, tname);
+ return -EACCES;
+ }
+ ret = SCALAR_VALUE;
+ } else if (env->ops->btf_struct_access && !type_is_alloc(reg->type) && atype == BPF_WRITE) {
if (!btf_is_kernel(reg->btf)) {
verbose(env, "verifier internal error: reg->btf must be kernel btf\n");
return -EFAULT;