forked from mirrors/linux
		
	bpf: Do not mark certain LSM hook arguments as trusted
Martin mentioned that the verifier cannot assume arguments from
LSM hook sk_alloc_security being trusted since after the hook
is called, the sk ref_count is set to 1. This will overwrite
the ref_count changed by the bpf program and may cause ref_count
underflow later on.
I then further checked some other hooks. For example,
for bpf_lsm_file_alloc() hook in fs/file_table.c,
        f->f_cred = get_cred(cred);
        error = security_file_alloc(f);
        if (unlikely(error)) {
                file_free_rcu(&f->f_rcuhead);
                return ERR_PTR(error);
        }
        atomic_long_set(&f->f_count, 1);
The input parameter 'f' to security_file_alloc() cannot be trusted
as well.
Specifically, I investiaged bpf_map/bpf_prog/file/sk/task alloc/free
lsm hooks. Except bpf_map_alloc and task_alloc, arguments for all other
hooks should not be considered as trusted. This may not be a complete
list, but it covers common usage for sk and task.
Fixes: 3f00c52393 ("bpf: Allow trusted pointers to be passed to KF_TRUSTED_ARGS kfuncs")
Signed-off-by: Yonghong Song <yhs@fb.com>
Link: https://lore.kernel.org/r/20221203204954.2043348-1-yhs@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
			
			
This commit is contained in:
		
							parent
							
								
									1910676cc1
								
							
						
					
					
						commit
						c0c852dd18
					
				
					 5 changed files with 36 additions and 0 deletions
				
			
		|  | @ -28,6 +28,7 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, | ||||||
| 			const struct bpf_prog *prog); | 			const struct bpf_prog *prog); | ||||||
| 
 | 
 | ||||||
| bool bpf_lsm_is_sleepable_hook(u32 btf_id); | bool bpf_lsm_is_sleepable_hook(u32 btf_id); | ||||||
|  | bool bpf_lsm_is_trusted(const struct bpf_prog *prog); | ||||||
| 
 | 
 | ||||||
| static inline struct bpf_storage_blob *bpf_inode( | static inline struct bpf_storage_blob *bpf_inode( | ||||||
| 	const struct inode *inode) | 	const struct inode *inode) | ||||||
|  | @ -51,6 +52,11 @@ static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id) | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline bool bpf_lsm_is_trusted(const struct bpf_prog *prog) | ||||||
|  | { | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, | static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog, | ||||||
| 				      const struct bpf_prog *prog) | 				      const struct bpf_prog *prog) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -345,11 +345,27 @@ BTF_ID(func, bpf_lsm_task_to_inode) | ||||||
| BTF_ID(func, bpf_lsm_userns_create) | BTF_ID(func, bpf_lsm_userns_create) | ||||||
| BTF_SET_END(sleepable_lsm_hooks) | BTF_SET_END(sleepable_lsm_hooks) | ||||||
| 
 | 
 | ||||||
|  | BTF_SET_START(untrusted_lsm_hooks) | ||||||
|  | BTF_ID(func, bpf_lsm_bpf_map_free_security) | ||||||
|  | BTF_ID(func, bpf_lsm_bpf_prog_alloc_security) | ||||||
|  | BTF_ID(func, bpf_lsm_bpf_prog_free_security) | ||||||
|  | BTF_ID(func, bpf_lsm_file_alloc_security) | ||||||
|  | BTF_ID(func, bpf_lsm_file_free_security) | ||||||
|  | BTF_ID(func, bpf_lsm_sk_alloc_security) | ||||||
|  | BTF_ID(func, bpf_lsm_sk_free_security) | ||||||
|  | BTF_ID(func, bpf_lsm_task_free) | ||||||
|  | BTF_SET_END(untrusted_lsm_hooks) | ||||||
|  | 
 | ||||||
| bool bpf_lsm_is_sleepable_hook(u32 btf_id) | bool bpf_lsm_is_sleepable_hook(u32 btf_id) | ||||||
| { | { | ||||||
| 	return btf_id_set_contains(&sleepable_lsm_hooks, btf_id); | 	return btf_id_set_contains(&sleepable_lsm_hooks, btf_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool bpf_lsm_is_trusted(const struct bpf_prog *prog) | ||||||
|  | { | ||||||
|  | 	return !btf_id_set_contains(&untrusted_lsm_hooks, prog->aux->attach_btf_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const struct bpf_prog_ops lsm_prog_ops = { | const struct bpf_prog_ops lsm_prog_ops = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ | ||||||
| #include <linux/bpf_verifier.h> | #include <linux/bpf_verifier.h> | ||||||
| #include <linux/btf.h> | #include <linux/btf.h> | ||||||
| #include <linux/btf_ids.h> | #include <linux/btf_ids.h> | ||||||
|  | #include <linux/bpf_lsm.h> | ||||||
| #include <linux/skmsg.h> | #include <linux/skmsg.h> | ||||||
| #include <linux/perf_event.h> | #include <linux/perf_event.h> | ||||||
| #include <linux/bsearch.h> | #include <linux/bsearch.h> | ||||||
|  | @ -5829,6 +5830,7 @@ static bool prog_args_trusted(const struct bpf_prog *prog) | ||||||
| 	case BPF_PROG_TYPE_TRACING: | 	case BPF_PROG_TYPE_TRACING: | ||||||
| 		return atype == BPF_TRACE_RAW_TP || atype == BPF_TRACE_ITER; | 		return atype == BPF_TRACE_RAW_TP || atype == BPF_TRACE_ITER; | ||||||
| 	case BPF_PROG_TYPE_LSM: | 	case BPF_PROG_TYPE_LSM: | ||||||
|  | 		return bpf_lsm_is_trusted(prog); | ||||||
| 	case BPF_PROG_TYPE_STRUCT_OPS: | 	case BPF_PROG_TYPE_STRUCT_OPS: | ||||||
| 		return true; | 		return true; | ||||||
| 	default: | 	default: | ||||||
|  |  | ||||||
|  | @ -103,6 +103,7 @@ static struct { | ||||||
| 	{"task_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, | 	{"task_kfunc_release_null", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, | ||||||
| 	{"task_kfunc_release_unacquired", "release kernel function bpf_task_release expects"}, | 	{"task_kfunc_release_unacquired", "release kernel function bpf_task_release expects"}, | ||||||
| 	{"task_kfunc_from_pid_no_null_check", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, | 	{"task_kfunc_from_pid_no_null_check", "arg#0 is ptr_or_null_ expected ptr_ or socket"}, | ||||||
|  | 	{"task_kfunc_from_lsm_task_free", "reg type unsupported for arg#0 function"}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void verify_fail(const char *prog_name, const char *expected_err_msg) | static void verify_fail(const char *prog_name, const char *expected_err_msg) | ||||||
|  |  | ||||||
|  | @ -271,3 +271,14 @@ int BPF_PROG(task_kfunc_from_pid_no_null_check, struct task_struct *task, u64 cl | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | SEC("lsm/task_free") | ||||||
|  | int BPF_PROG(task_kfunc_from_lsm_task_free, struct task_struct *task) | ||||||
|  | { | ||||||
|  | 	struct task_struct *acquired; | ||||||
|  | 
 | ||||||
|  | 	/* the argument of lsm task_free hook is untrusted. */ | ||||||
|  | 	acquired = bpf_task_acquire(task); | ||||||
|  | 	bpf_task_release(acquired); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Yonghong Song
						Yonghong Song