Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: expose bpf_{g,s}etsockopt to lsm cgroup

I don't see how to make it nice without introducing btf id lists
for the hooks where these helpers are allowed. Some LSM hooks
work on the locked sockets, some are triggering early and
don't grab any locks, so have two lists for now:

1. LSM hooks which trigger under socket lock - minority of the hooks,
but ideal case for us, we can expose existing BTF-based helpers
2. LSM hooks which trigger without socket lock, but they trigger
early in the socket creation path where it should be safe to
do setsockopt without any locks
3. The rest are prohibited. I'm thinking that this use-case might
be a good gateway to sleeping lsm cgroup hooks in the future.
We can either expose lock/unlock operations (and add tracking
to the verifier) or have another set of bpf_setsockopt
wrapper that grab the locks and might sleep.

Reviewed-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
Link: https://lore.kernel.org/r/20220628174314.1216643-7-sdf@google.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Stanislav Fomichev and committed by
Alexei Starovoitov
9113d7e4 b79c9fc9

+93 -7
+2
include/linux/bpf.h
··· 2386 2386 extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto; 2387 2387 extern const struct bpf_func_proto bpf_sk_setsockopt_proto; 2388 2388 extern const struct bpf_func_proto bpf_sk_getsockopt_proto; 2389 + extern const struct bpf_func_proto bpf_unlocked_sk_setsockopt_proto; 2390 + extern const struct bpf_func_proto bpf_unlocked_sk_getsockopt_proto; 2389 2391 extern const struct bpf_func_proto bpf_find_vma_proto; 2390 2392 extern const struct bpf_func_proto bpf_loop_proto; 2391 2393 extern const struct bpf_func_proto bpf_copy_from_user_task_proto;
+38
kernel/bpf/bpf_lsm.c
··· 45 45 BTF_ID(func, bpf_lsm_sk_free_security) 46 46 BTF_SET_END(bpf_lsm_current_hooks) 47 47 48 + /* List of LSM hooks that trigger while the socket is properly locked. 49 + */ 50 + BTF_SET_START(bpf_lsm_locked_sockopt_hooks) 51 + BTF_ID(func, bpf_lsm_socket_sock_rcv_skb) 52 + BTF_ID(func, bpf_lsm_sock_graft) 53 + BTF_ID(func, bpf_lsm_inet_csk_clone) 54 + BTF_ID(func, bpf_lsm_inet_conn_established) 55 + BTF_SET_END(bpf_lsm_locked_sockopt_hooks) 56 + 57 + /* List of LSM hooks that trigger while the socket is _not_ locked, 58 + * but it's ok to call bpf_{g,s}etsockopt because the socket is still 59 + * in the early init phase. 60 + */ 61 + BTF_SET_START(bpf_lsm_unlocked_sockopt_hooks) 62 + BTF_ID(func, bpf_lsm_socket_post_create) 63 + BTF_ID(func, bpf_lsm_socket_socketpair) 64 + BTF_SET_END(bpf_lsm_unlocked_sockopt_hooks) 65 + 48 66 void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, 49 67 bpf_func_t *bpf_func) 50 68 { ··· 219 201 case BPF_FUNC_get_retval: 220 202 return prog->expected_attach_type == BPF_LSM_CGROUP ? 221 203 &bpf_get_retval_proto : NULL; 204 + case BPF_FUNC_setsockopt: 205 + if (prog->expected_attach_type != BPF_LSM_CGROUP) 206 + return NULL; 207 + if (btf_id_set_contains(&bpf_lsm_locked_sockopt_hooks, 208 + prog->aux->attach_btf_id)) 209 + return &bpf_sk_setsockopt_proto; 210 + if (btf_id_set_contains(&bpf_lsm_unlocked_sockopt_hooks, 211 + prog->aux->attach_btf_id)) 212 + return &bpf_unlocked_sk_setsockopt_proto; 213 + return NULL; 214 + case BPF_FUNC_getsockopt: 215 + if (prog->expected_attach_type != BPF_LSM_CGROUP) 216 + return NULL; 217 + if (btf_id_set_contains(&bpf_lsm_locked_sockopt_hooks, 218 + prog->aux->attach_btf_id)) 219 + return &bpf_sk_getsockopt_proto; 220 + if (btf_id_set_contains(&bpf_lsm_unlocked_sockopt_hooks, 221 + prog->aux->attach_btf_id)) 222 + return &bpf_unlocked_sk_getsockopt_proto; 223 + return NULL; 222 224 default: 223 225 return tracing_prog_func_proto(func_id, prog); 224 226 }
+53 -7
net/core/filter.c
··· 5012 5012 .arg1_type = ARG_PTR_TO_CTX, 5013 5013 }; 5014 5014 5015 - static int _bpf_setsockopt(struct sock *sk, int level, int optname, 5016 - char *optval, int optlen) 5015 + static int __bpf_setsockopt(struct sock *sk, int level, int optname, 5016 + char *optval, int optlen) 5017 5017 { 5018 5018 char devname[IFNAMSIZ]; 5019 5019 int val, valbool; ··· 5023 5023 5024 5024 if (!sk_fullsock(sk)) 5025 5025 return -EINVAL; 5026 - 5027 - sock_owned_by_me(sk); 5028 5026 5029 5027 if (level == SOL_SOCKET) { 5030 5028 if (optlen != sizeof(int) && optname != SO_BINDTODEVICE) ··· 5256 5258 return ret; 5257 5259 } 5258 5260 5259 - static int _bpf_getsockopt(struct sock *sk, int level, int optname, 5261 + static int _bpf_setsockopt(struct sock *sk, int level, int optname, 5260 5262 char *optval, int optlen) 5263 + { 5264 + if (sk_fullsock(sk)) 5265 + sock_owned_by_me(sk); 5266 + return __bpf_setsockopt(sk, level, optname, optval, optlen); 5267 + } 5268 + 5269 + static int __bpf_getsockopt(struct sock *sk, int level, int optname, 5270 + char *optval, int optlen) 5261 5271 { 5262 5272 if (!sk_fullsock(sk)) 5263 5273 goto err_clear; 5264 - 5265 - sock_owned_by_me(sk); 5266 5274 5267 5275 if (level == SOL_SOCKET) { 5268 5276 if (optlen != sizeof(int)) ··· 5364 5360 return -EINVAL; 5365 5361 } 5366 5362 5363 + static int _bpf_getsockopt(struct sock *sk, int level, int optname, 5364 + char *optval, int optlen) 5365 + { 5366 + if (sk_fullsock(sk)) 5367 + sock_owned_by_me(sk); 5368 + return __bpf_getsockopt(sk, level, optname, optval, optlen); 5369 + } 5370 + 5367 5371 BPF_CALL_5(bpf_sk_setsockopt, struct sock *, sk, int, level, 5368 5372 int, optname, char *, optval, int, optlen) 5369 5373 { ··· 5403 5391 5404 5392 const struct bpf_func_proto bpf_sk_getsockopt_proto = { 5405 5393 .func = bpf_sk_getsockopt, 5394 + .gpl_only = false, 5395 + .ret_type = RET_INTEGER, 5396 + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, 5397 + .arg2_type = ARG_ANYTHING, 5398 + .arg3_type = ARG_ANYTHING, 5399 + .arg4_type = ARG_PTR_TO_UNINIT_MEM, 5400 + .arg5_type = ARG_CONST_SIZE, 5401 + }; 5402 + 5403 + BPF_CALL_5(bpf_unlocked_sk_setsockopt, struct sock *, sk, int, level, 5404 + int, optname, char *, optval, int, optlen) 5405 + { 5406 + return __bpf_setsockopt(sk, level, optname, optval, optlen); 5407 + } 5408 + 5409 + const struct bpf_func_proto bpf_unlocked_sk_setsockopt_proto = { 5410 + .func = bpf_unlocked_sk_setsockopt, 5411 + .gpl_only = false, 5412 + .ret_type = RET_INTEGER, 5413 + .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON, 5414 + .arg2_type = ARG_ANYTHING, 5415 + .arg3_type = ARG_ANYTHING, 5416 + .arg4_type = ARG_PTR_TO_MEM | MEM_RDONLY, 5417 + .arg5_type = ARG_CONST_SIZE, 5418 + }; 5419 + 5420 + BPF_CALL_5(bpf_unlocked_sk_getsockopt, struct sock *, sk, int, level, 5421 + int, optname, char *, optval, int, optlen) 5422 + { 5423 + return __bpf_getsockopt(sk, level, optname, optval, optlen); 5424 + } 5425 + 5426 + const struct bpf_func_proto bpf_unlocked_sk_getsockopt_proto = { 5427 + .func = bpf_unlocked_sk_getsockopt, 5406 5428 .gpl_only = false, 5407 5429 .ret_type = RET_INTEGER, 5408 5430 .arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,