18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Functions to manage eBPF programs attached to cgroups 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Daniel Mack 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/atomic.h> 108c2ecf20Sopenharmony_ci#include <linux/cgroup.h> 118c2ecf20Sopenharmony_ci#include <linux/filter.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/bpf.h> 168c2ecf20Sopenharmony_ci#include <linux/bpf-cgroup.h> 178c2ecf20Sopenharmony_ci#include <net/sock.h> 188c2ecf20Sopenharmony_ci#include <net/bpf_sk_storage.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "../cgroup/cgroup-internal.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(cgroup_bpf_enabled_key); 238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cgroup_bpf_enabled_key); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_civoid cgroup_bpf_offline(struct cgroup *cgrp) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci cgroup_get(cgrp); 288c2ecf20Sopenharmony_ci percpu_ref_kill(&cgrp->bpf.refcnt); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void bpf_cgroup_storages_free(struct bpf_cgroup_storage *storages[]) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci enum bpf_cgroup_storage_type stype; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for_each_cgroup_storage_type(stype) 368c2ecf20Sopenharmony_ci bpf_cgroup_storage_free(storages[stype]); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int bpf_cgroup_storages_alloc(struct bpf_cgroup_storage *storages[], 408c2ecf20Sopenharmony_ci struct bpf_cgroup_storage *new_storages[], 418c2ecf20Sopenharmony_ci enum bpf_attach_type type, 428c2ecf20Sopenharmony_ci struct bpf_prog *prog, 438c2ecf20Sopenharmony_ci struct cgroup *cgrp) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci enum bpf_cgroup_storage_type stype; 468c2ecf20Sopenharmony_ci struct bpf_cgroup_storage_key key; 478c2ecf20Sopenharmony_ci struct bpf_map *map; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci key.cgroup_inode_id = cgroup_id(cgrp); 508c2ecf20Sopenharmony_ci key.attach_type = type; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci for_each_cgroup_storage_type(stype) { 538c2ecf20Sopenharmony_ci map = prog->aux->cgroup_storage[stype]; 548c2ecf20Sopenharmony_ci if (!map) 558c2ecf20Sopenharmony_ci continue; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci storages[stype] = cgroup_storage_lookup((void *)map, &key, false); 588c2ecf20Sopenharmony_ci if (storages[stype]) 598c2ecf20Sopenharmony_ci continue; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci storages[stype] = bpf_cgroup_storage_alloc(prog, stype); 628c2ecf20Sopenharmony_ci if (IS_ERR(storages[stype])) { 638c2ecf20Sopenharmony_ci bpf_cgroup_storages_free(new_storages); 648c2ecf20Sopenharmony_ci return -ENOMEM; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci new_storages[stype] = storages[stype]; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void bpf_cgroup_storages_assign(struct bpf_cgroup_storage *dst[], 748c2ecf20Sopenharmony_ci struct bpf_cgroup_storage *src[]) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci enum bpf_cgroup_storage_type stype; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for_each_cgroup_storage_type(stype) 798c2ecf20Sopenharmony_ci dst[stype] = src[stype]; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void bpf_cgroup_storages_link(struct bpf_cgroup_storage *storages[], 838c2ecf20Sopenharmony_ci struct cgroup *cgrp, 848c2ecf20Sopenharmony_ci enum bpf_attach_type attach_type) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci enum bpf_cgroup_storage_type stype; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for_each_cgroup_storage_type(stype) 898c2ecf20Sopenharmony_ci bpf_cgroup_storage_link(storages[stype], cgrp, attach_type); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Called when bpf_cgroup_link is auto-detached from dying cgroup. 938c2ecf20Sopenharmony_ci * It drops cgroup and bpf_prog refcounts, and marks bpf_link as defunct. It 948c2ecf20Sopenharmony_ci * doesn't free link memory, which will eventually be done by bpf_link's 958c2ecf20Sopenharmony_ci * release() callback, when its last FD is closed. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic void bpf_cgroup_link_auto_detach(struct bpf_cgroup_link *link) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci cgroup_put(link->cgroup); 1008c2ecf20Sopenharmony_ci link->cgroup = NULL; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * cgroup_bpf_release() - put references of all bpf programs and 1058c2ecf20Sopenharmony_ci * release all cgroup bpf data 1068c2ecf20Sopenharmony_ci * @work: work structure embedded into the cgroup to modify 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic void cgroup_bpf_release(struct work_struct *work) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct cgroup *p, *cgrp = container_of(work, struct cgroup, 1118c2ecf20Sopenharmony_ci bpf.release_work); 1128c2ecf20Sopenharmony_ci struct bpf_prog_array *old_array; 1138c2ecf20Sopenharmony_ci struct list_head *storages = &cgrp->bpf.storages; 1148c2ecf20Sopenharmony_ci struct bpf_cgroup_storage *storage, *stmp; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci unsigned int type; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci mutex_lock(&cgroup_mutex); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (type = 0; type < ARRAY_SIZE(cgrp->bpf.progs); type++) { 1218c2ecf20Sopenharmony_ci struct list_head *progs = &cgrp->bpf.progs[type]; 1228c2ecf20Sopenharmony_ci struct bpf_prog_list *pl, *pltmp; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci list_for_each_entry_safe(pl, pltmp, progs, node) { 1258c2ecf20Sopenharmony_ci list_del(&pl->node); 1268c2ecf20Sopenharmony_ci if (pl->prog) 1278c2ecf20Sopenharmony_ci bpf_prog_put(pl->prog); 1288c2ecf20Sopenharmony_ci if (pl->link) 1298c2ecf20Sopenharmony_ci bpf_cgroup_link_auto_detach(pl->link); 1308c2ecf20Sopenharmony_ci kfree(pl); 1318c2ecf20Sopenharmony_ci static_branch_dec(&cgroup_bpf_enabled_key); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci old_array = rcu_dereference_protected( 1348c2ecf20Sopenharmony_ci cgrp->bpf.effective[type], 1358c2ecf20Sopenharmony_ci lockdep_is_held(&cgroup_mutex)); 1368c2ecf20Sopenharmony_ci bpf_prog_array_free(old_array); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci list_for_each_entry_safe(storage, stmp, storages, list_cg) { 1408c2ecf20Sopenharmony_ci bpf_cgroup_storage_unlink(storage); 1418c2ecf20Sopenharmony_ci bpf_cgroup_storage_free(storage); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci mutex_unlock(&cgroup_mutex); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p)) 1478c2ecf20Sopenharmony_ci cgroup_bpf_put(p); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci percpu_ref_exit(&cgrp->bpf.refcnt); 1508c2ecf20Sopenharmony_ci cgroup_put(cgrp); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * cgroup_bpf_release_fn() - callback used to schedule releasing 1558c2ecf20Sopenharmony_ci * of bpf cgroup data 1568c2ecf20Sopenharmony_ci * @ref: percpu ref counter structure 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_cistatic void cgroup_bpf_release_fn(struct percpu_ref *ref) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct cgroup *cgrp = container_of(ref, struct cgroup, bpf.refcnt); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci INIT_WORK(&cgrp->bpf.release_work, cgroup_bpf_release); 1638c2ecf20Sopenharmony_ci queue_work(system_wq, &cgrp->bpf.release_work); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* Get underlying bpf_prog of bpf_prog_list entry, regardless if it's through 1678c2ecf20Sopenharmony_ci * link or direct prog. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_cistatic struct bpf_prog *prog_list_prog(struct bpf_prog_list *pl) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (pl->prog) 1728c2ecf20Sopenharmony_ci return pl->prog; 1738c2ecf20Sopenharmony_ci if (pl->link) 1748c2ecf20Sopenharmony_ci return pl->link->link.prog; 1758c2ecf20Sopenharmony_ci return NULL; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* count number of elements in the list. 1798c2ecf20Sopenharmony_ci * it's slow but the list cannot be long 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic u32 prog_list_length(struct list_head *head) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 1848c2ecf20Sopenharmony_ci u32 cnt = 0; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci list_for_each_entry(pl, head, node) { 1878c2ecf20Sopenharmony_ci if (!prog_list_prog(pl)) 1888c2ecf20Sopenharmony_ci continue; 1898c2ecf20Sopenharmony_ci cnt++; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci return cnt; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* if parent has non-overridable prog attached, 1958c2ecf20Sopenharmony_ci * disallow attaching new programs to the descendent cgroup. 1968c2ecf20Sopenharmony_ci * if parent has overridable or multi-prog, allow attaching 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic bool hierarchy_allows_attach(struct cgroup *cgrp, 1998c2ecf20Sopenharmony_ci enum bpf_attach_type type) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct cgroup *p; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci p = cgroup_parent(cgrp); 2048c2ecf20Sopenharmony_ci if (!p) 2058c2ecf20Sopenharmony_ci return true; 2068c2ecf20Sopenharmony_ci do { 2078c2ecf20Sopenharmony_ci u32 flags = p->bpf.flags[type]; 2088c2ecf20Sopenharmony_ci u32 cnt; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (flags & BPF_F_ALLOW_MULTI) 2118c2ecf20Sopenharmony_ci return true; 2128c2ecf20Sopenharmony_ci cnt = prog_list_length(&p->bpf.progs[type]); 2138c2ecf20Sopenharmony_ci WARN_ON_ONCE(cnt > 1); 2148c2ecf20Sopenharmony_ci if (cnt == 1) 2158c2ecf20Sopenharmony_ci return !!(flags & BPF_F_ALLOW_OVERRIDE); 2168c2ecf20Sopenharmony_ci p = cgroup_parent(p); 2178c2ecf20Sopenharmony_ci } while (p); 2188c2ecf20Sopenharmony_ci return true; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* compute a chain of effective programs for a given cgroup: 2228c2ecf20Sopenharmony_ci * start from the list of programs in this cgroup and add 2238c2ecf20Sopenharmony_ci * all parent programs. 2248c2ecf20Sopenharmony_ci * Note that parent's F_ALLOW_OVERRIDE-type program is yielding 2258c2ecf20Sopenharmony_ci * to programs in this cgroup 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic int compute_effective_progs(struct cgroup *cgrp, 2288c2ecf20Sopenharmony_ci enum bpf_attach_type type, 2298c2ecf20Sopenharmony_ci struct bpf_prog_array **array) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct bpf_prog_array_item *item; 2328c2ecf20Sopenharmony_ci struct bpf_prog_array *progs; 2338c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 2348c2ecf20Sopenharmony_ci struct cgroup *p = cgrp; 2358c2ecf20Sopenharmony_ci int cnt = 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* count number of effective programs by walking parents */ 2388c2ecf20Sopenharmony_ci do { 2398c2ecf20Sopenharmony_ci if (cnt == 0 || (p->bpf.flags[type] & BPF_F_ALLOW_MULTI)) 2408c2ecf20Sopenharmony_ci cnt += prog_list_length(&p->bpf.progs[type]); 2418c2ecf20Sopenharmony_ci p = cgroup_parent(p); 2428c2ecf20Sopenharmony_ci } while (p); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci progs = bpf_prog_array_alloc(cnt, GFP_KERNEL); 2458c2ecf20Sopenharmony_ci if (!progs) 2468c2ecf20Sopenharmony_ci return -ENOMEM; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* populate the array with effective progs */ 2498c2ecf20Sopenharmony_ci cnt = 0; 2508c2ecf20Sopenharmony_ci p = cgrp; 2518c2ecf20Sopenharmony_ci do { 2528c2ecf20Sopenharmony_ci if (cnt > 0 && !(p->bpf.flags[type] & BPF_F_ALLOW_MULTI)) 2538c2ecf20Sopenharmony_ci continue; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci list_for_each_entry(pl, &p->bpf.progs[type], node) { 2568c2ecf20Sopenharmony_ci if (!prog_list_prog(pl)) 2578c2ecf20Sopenharmony_ci continue; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci item = &progs->items[cnt]; 2608c2ecf20Sopenharmony_ci item->prog = prog_list_prog(pl); 2618c2ecf20Sopenharmony_ci bpf_cgroup_storages_assign(item->cgroup_storage, 2628c2ecf20Sopenharmony_ci pl->storage); 2638c2ecf20Sopenharmony_ci cnt++; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } while ((p = cgroup_parent(p))); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci *array = progs; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void activate_effective_progs(struct cgroup *cgrp, 2728c2ecf20Sopenharmony_ci enum bpf_attach_type type, 2738c2ecf20Sopenharmony_ci struct bpf_prog_array *old_array) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci old_array = rcu_replace_pointer(cgrp->bpf.effective[type], old_array, 2768c2ecf20Sopenharmony_ci lockdep_is_held(&cgroup_mutex)); 2778c2ecf20Sopenharmony_ci /* free prog array after grace period, since __cgroup_bpf_run_*() 2788c2ecf20Sopenharmony_ci * might be still walking the array 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci bpf_prog_array_free(old_array); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/** 2848c2ecf20Sopenharmony_ci * cgroup_bpf_inherit() - inherit effective programs from parent 2858c2ecf20Sopenharmony_ci * @cgrp: the cgroup to modify 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ciint cgroup_bpf_inherit(struct cgroup *cgrp) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci/* has to use marco instead of const int, since compiler thinks 2908c2ecf20Sopenharmony_ci * that array below is variable length 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci#define NR ARRAY_SIZE(cgrp->bpf.effective) 2938c2ecf20Sopenharmony_ci struct bpf_prog_array *arrays[NR] = {}; 2948c2ecf20Sopenharmony_ci struct cgroup *p; 2958c2ecf20Sopenharmony_ci int ret, i; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = percpu_ref_init(&cgrp->bpf.refcnt, cgroup_bpf_release_fn, 0, 2988c2ecf20Sopenharmony_ci GFP_KERNEL); 2998c2ecf20Sopenharmony_ci if (ret) 3008c2ecf20Sopenharmony_ci return ret; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p)) 3038c2ecf20Sopenharmony_ci cgroup_bpf_get(p); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci for (i = 0; i < NR; i++) 3068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cgrp->bpf.progs[i]); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cgrp->bpf.storages); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci for (i = 0; i < NR; i++) 3118c2ecf20Sopenharmony_ci if (compute_effective_progs(cgrp, i, &arrays[i])) 3128c2ecf20Sopenharmony_ci goto cleanup; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci for (i = 0; i < NR; i++) 3158c2ecf20Sopenharmony_ci activate_effective_progs(cgrp, i, arrays[i]); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_cicleanup: 3198c2ecf20Sopenharmony_ci for (i = 0; i < NR; i++) 3208c2ecf20Sopenharmony_ci bpf_prog_array_free(arrays[i]); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (p = cgroup_parent(cgrp); p; p = cgroup_parent(p)) 3238c2ecf20Sopenharmony_ci cgroup_bpf_put(p); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci percpu_ref_exit(&cgrp->bpf.refcnt); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return -ENOMEM; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int update_effective_progs(struct cgroup *cgrp, 3318c2ecf20Sopenharmony_ci enum bpf_attach_type type) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct cgroup_subsys_state *css; 3348c2ecf20Sopenharmony_ci int err; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* allocate and recompute effective prog arrays */ 3378c2ecf20Sopenharmony_ci css_for_each_descendant_pre(css, &cgrp->self) { 3388c2ecf20Sopenharmony_ci struct cgroup *desc = container_of(css, struct cgroup, self); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (percpu_ref_is_zero(&desc->bpf.refcnt)) 3418c2ecf20Sopenharmony_ci continue; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci err = compute_effective_progs(desc, type, &desc->bpf.inactive); 3448c2ecf20Sopenharmony_ci if (err) 3458c2ecf20Sopenharmony_ci goto cleanup; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* all allocations were successful. Activate all prog arrays */ 3498c2ecf20Sopenharmony_ci css_for_each_descendant_pre(css, &cgrp->self) { 3508c2ecf20Sopenharmony_ci struct cgroup *desc = container_of(css, struct cgroup, self); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (percpu_ref_is_zero(&desc->bpf.refcnt)) { 3538c2ecf20Sopenharmony_ci if (unlikely(desc->bpf.inactive)) { 3548c2ecf20Sopenharmony_ci bpf_prog_array_free(desc->bpf.inactive); 3558c2ecf20Sopenharmony_ci desc->bpf.inactive = NULL; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci continue; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci activate_effective_progs(desc, type, desc->bpf.inactive); 3618c2ecf20Sopenharmony_ci desc->bpf.inactive = NULL; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cicleanup: 3678c2ecf20Sopenharmony_ci /* oom while computing effective. Free all computed effective arrays 3688c2ecf20Sopenharmony_ci * since they were not activated 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci css_for_each_descendant_pre(css, &cgrp->self) { 3718c2ecf20Sopenharmony_ci struct cgroup *desc = container_of(css, struct cgroup, self); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci bpf_prog_array_free(desc->bpf.inactive); 3748c2ecf20Sopenharmony_ci desc->bpf.inactive = NULL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return err; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci#define BPF_CGROUP_MAX_PROGS 64 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic struct bpf_prog_list *find_attach_entry(struct list_head *progs, 3838c2ecf20Sopenharmony_ci struct bpf_prog *prog, 3848c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link, 3858c2ecf20Sopenharmony_ci struct bpf_prog *replace_prog, 3868c2ecf20Sopenharmony_ci bool allow_multi) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* single-attach case */ 3918c2ecf20Sopenharmony_ci if (!allow_multi) { 3928c2ecf20Sopenharmony_ci if (list_empty(progs)) 3938c2ecf20Sopenharmony_ci return NULL; 3948c2ecf20Sopenharmony_ci return list_first_entry(progs, typeof(*pl), node); 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci list_for_each_entry(pl, progs, node) { 3988c2ecf20Sopenharmony_ci if (prog && pl->prog == prog && prog != replace_prog) 3998c2ecf20Sopenharmony_ci /* disallow attaching the same prog twice */ 4008c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4018c2ecf20Sopenharmony_ci if (link && pl->link == link) 4028c2ecf20Sopenharmony_ci /* disallow attaching the same link twice */ 4038c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* direct prog multi-attach w/ replacement case */ 4078c2ecf20Sopenharmony_ci if (replace_prog) { 4088c2ecf20Sopenharmony_ci list_for_each_entry(pl, progs, node) { 4098c2ecf20Sopenharmony_ci if (pl->prog == replace_prog) 4108c2ecf20Sopenharmony_ci /* a match found */ 4118c2ecf20Sopenharmony_ci return pl; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci /* prog to replace not found for cgroup */ 4148c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return NULL; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/** 4218c2ecf20Sopenharmony_ci * __cgroup_bpf_attach() - Attach the program or the link to a cgroup, and 4228c2ecf20Sopenharmony_ci * propagate the change to descendants 4238c2ecf20Sopenharmony_ci * @cgrp: The cgroup which descendants to traverse 4248c2ecf20Sopenharmony_ci * @prog: A program to attach 4258c2ecf20Sopenharmony_ci * @link: A link to attach 4268c2ecf20Sopenharmony_ci * @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set 4278c2ecf20Sopenharmony_ci * @type: Type of attach operation 4288c2ecf20Sopenharmony_ci * @flags: Option flags 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * Exactly one of @prog or @link can be non-null. 4318c2ecf20Sopenharmony_ci * Must be called with cgroup_mutex held. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ciint __cgroup_bpf_attach(struct cgroup *cgrp, 4348c2ecf20Sopenharmony_ci struct bpf_prog *prog, struct bpf_prog *replace_prog, 4358c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link, 4368c2ecf20Sopenharmony_ci enum bpf_attach_type type, u32 flags) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI)); 4398c2ecf20Sopenharmony_ci struct list_head *progs = &cgrp->bpf.progs[type]; 4408c2ecf20Sopenharmony_ci struct bpf_prog *old_prog = NULL; 4418c2ecf20Sopenharmony_ci struct bpf_cgroup_storage *storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {}; 4428c2ecf20Sopenharmony_ci struct bpf_cgroup_storage *new_storage[MAX_BPF_CGROUP_STORAGE_TYPE] = {}; 4438c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 4448c2ecf20Sopenharmony_ci int err; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) || 4478c2ecf20Sopenharmony_ci ((flags & BPF_F_REPLACE) && !(flags & BPF_F_ALLOW_MULTI))) 4488c2ecf20Sopenharmony_ci /* invalid combination */ 4498c2ecf20Sopenharmony_ci return -EINVAL; 4508c2ecf20Sopenharmony_ci if (link && (prog || replace_prog)) 4518c2ecf20Sopenharmony_ci /* only either link or prog/replace_prog can be specified */ 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci if (!!replace_prog != !!(flags & BPF_F_REPLACE)) 4548c2ecf20Sopenharmony_ci /* replace_prog implies BPF_F_REPLACE, and vice versa */ 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (!hierarchy_allows_attach(cgrp, type)) 4588c2ecf20Sopenharmony_ci return -EPERM; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (!list_empty(progs) && cgrp->bpf.flags[type] != saved_flags) 4618c2ecf20Sopenharmony_ci /* Disallow attaching non-overridable on top 4628c2ecf20Sopenharmony_ci * of existing overridable in this cgroup. 4638c2ecf20Sopenharmony_ci * Disallow attaching multi-prog if overridable or none 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci return -EPERM; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (prog_list_length(progs) >= BPF_CGROUP_MAX_PROGS) 4688c2ecf20Sopenharmony_ci return -E2BIG; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci pl = find_attach_entry(progs, prog, link, replace_prog, 4718c2ecf20Sopenharmony_ci flags & BPF_F_ALLOW_MULTI); 4728c2ecf20Sopenharmony_ci if (IS_ERR(pl)) 4738c2ecf20Sopenharmony_ci return PTR_ERR(pl); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (bpf_cgroup_storages_alloc(storage, new_storage, type, 4768c2ecf20Sopenharmony_ci prog ? : link->link.prog, cgrp)) 4778c2ecf20Sopenharmony_ci return -ENOMEM; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (pl) { 4808c2ecf20Sopenharmony_ci old_prog = pl->prog; 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci pl = kmalloc(sizeof(*pl), GFP_KERNEL); 4838c2ecf20Sopenharmony_ci if (!pl) { 4848c2ecf20Sopenharmony_ci bpf_cgroup_storages_free(new_storage); 4858c2ecf20Sopenharmony_ci return -ENOMEM; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci list_add_tail(&pl->node, progs); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci pl->prog = prog; 4918c2ecf20Sopenharmony_ci pl->link = link; 4928c2ecf20Sopenharmony_ci bpf_cgroup_storages_assign(pl->storage, storage); 4938c2ecf20Sopenharmony_ci cgrp->bpf.flags[type] = saved_flags; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci err = update_effective_progs(cgrp, type); 4968c2ecf20Sopenharmony_ci if (err) 4978c2ecf20Sopenharmony_ci goto cleanup; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (old_prog) 5008c2ecf20Sopenharmony_ci bpf_prog_put(old_prog); 5018c2ecf20Sopenharmony_ci else 5028c2ecf20Sopenharmony_ci static_branch_inc(&cgroup_bpf_enabled_key); 5038c2ecf20Sopenharmony_ci bpf_cgroup_storages_link(new_storage, cgrp, type); 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cicleanup: 5078c2ecf20Sopenharmony_ci if (old_prog) { 5088c2ecf20Sopenharmony_ci pl->prog = old_prog; 5098c2ecf20Sopenharmony_ci pl->link = NULL; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci bpf_cgroup_storages_free(new_storage); 5128c2ecf20Sopenharmony_ci if (!old_prog) { 5138c2ecf20Sopenharmony_ci list_del(&pl->node); 5148c2ecf20Sopenharmony_ci kfree(pl); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci return err; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* Swap updated BPF program for given link in effective program arrays across 5208c2ecf20Sopenharmony_ci * all descendant cgroups. This function is guaranteed to succeed. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic void replace_effective_prog(struct cgroup *cgrp, 5238c2ecf20Sopenharmony_ci enum bpf_attach_type type, 5248c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct bpf_prog_array_item *item; 5278c2ecf20Sopenharmony_ci struct cgroup_subsys_state *css; 5288c2ecf20Sopenharmony_ci struct bpf_prog_array *progs; 5298c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 5308c2ecf20Sopenharmony_ci struct list_head *head; 5318c2ecf20Sopenharmony_ci struct cgroup *cg; 5328c2ecf20Sopenharmony_ci int pos; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci css_for_each_descendant_pre(css, &cgrp->self) { 5358c2ecf20Sopenharmony_ci struct cgroup *desc = container_of(css, struct cgroup, self); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (percpu_ref_is_zero(&desc->bpf.refcnt)) 5388c2ecf20Sopenharmony_ci continue; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* find position of link in effective progs array */ 5418c2ecf20Sopenharmony_ci for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { 5428c2ecf20Sopenharmony_ci if (pos && !(cg->bpf.flags[type] & BPF_F_ALLOW_MULTI)) 5438c2ecf20Sopenharmony_ci continue; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci head = &cg->bpf.progs[type]; 5468c2ecf20Sopenharmony_ci list_for_each_entry(pl, head, node) { 5478c2ecf20Sopenharmony_ci if (!prog_list_prog(pl)) 5488c2ecf20Sopenharmony_ci continue; 5498c2ecf20Sopenharmony_ci if (pl->link == link) 5508c2ecf20Sopenharmony_ci goto found; 5518c2ecf20Sopenharmony_ci pos++; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_cifound: 5558c2ecf20Sopenharmony_ci BUG_ON(!cg); 5568c2ecf20Sopenharmony_ci progs = rcu_dereference_protected( 5578c2ecf20Sopenharmony_ci desc->bpf.effective[type], 5588c2ecf20Sopenharmony_ci lockdep_is_held(&cgroup_mutex)); 5598c2ecf20Sopenharmony_ci item = &progs->items[pos]; 5608c2ecf20Sopenharmony_ci WRITE_ONCE(item->prog, link->link.prog); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/** 5658c2ecf20Sopenharmony_ci * __cgroup_bpf_replace() - Replace link's program and propagate the change 5668c2ecf20Sopenharmony_ci * to descendants 5678c2ecf20Sopenharmony_ci * @cgrp: The cgroup which descendants to traverse 5688c2ecf20Sopenharmony_ci * @link: A link for which to replace BPF program 5698c2ecf20Sopenharmony_ci * @type: Type of attach operation 5708c2ecf20Sopenharmony_ci * 5718c2ecf20Sopenharmony_ci * Must be called with cgroup_mutex held. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_cistatic int __cgroup_bpf_replace(struct cgroup *cgrp, 5748c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link, 5758c2ecf20Sopenharmony_ci struct bpf_prog *new_prog) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct list_head *progs = &cgrp->bpf.progs[link->type]; 5788c2ecf20Sopenharmony_ci struct bpf_prog *old_prog; 5798c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 5808c2ecf20Sopenharmony_ci bool found = false; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (link->link.prog->type != new_prog->type) 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci list_for_each_entry(pl, progs, node) { 5868c2ecf20Sopenharmony_ci if (pl->link == link) { 5878c2ecf20Sopenharmony_ci found = true; 5888c2ecf20Sopenharmony_ci break; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci if (!found) 5928c2ecf20Sopenharmony_ci return -ENOENT; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci old_prog = xchg(&link->link.prog, new_prog); 5958c2ecf20Sopenharmony_ci replace_effective_prog(cgrp, link->type, link); 5968c2ecf20Sopenharmony_ci bpf_prog_put(old_prog); 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *new_prog, 6018c2ecf20Sopenharmony_ci struct bpf_prog *old_prog) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct bpf_cgroup_link *cg_link; 6048c2ecf20Sopenharmony_ci int ret; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci cg_link = container_of(link, struct bpf_cgroup_link, link); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci mutex_lock(&cgroup_mutex); 6098c2ecf20Sopenharmony_ci /* link might have been auto-released by dying cgroup, so fail */ 6108c2ecf20Sopenharmony_ci if (!cg_link->cgroup) { 6118c2ecf20Sopenharmony_ci ret = -ENOLINK; 6128c2ecf20Sopenharmony_ci goto out_unlock; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci if (old_prog && link->prog != old_prog) { 6158c2ecf20Sopenharmony_ci ret = -EPERM; 6168c2ecf20Sopenharmony_ci goto out_unlock; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci ret = __cgroup_bpf_replace(cg_link->cgroup, cg_link, new_prog); 6198c2ecf20Sopenharmony_ciout_unlock: 6208c2ecf20Sopenharmony_ci mutex_unlock(&cgroup_mutex); 6218c2ecf20Sopenharmony_ci return ret; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic struct bpf_prog_list *find_detach_entry(struct list_head *progs, 6258c2ecf20Sopenharmony_ci struct bpf_prog *prog, 6268c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link, 6278c2ecf20Sopenharmony_ci bool allow_multi) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!allow_multi) { 6328c2ecf20Sopenharmony_ci if (list_empty(progs)) 6338c2ecf20Sopenharmony_ci /* report error when trying to detach and nothing is attached */ 6348c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* to maintain backward compatibility NONE and OVERRIDE cgroups 6378c2ecf20Sopenharmony_ci * allow detaching with invalid FD (prog==NULL) in legacy mode 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ci return list_first_entry(progs, typeof(*pl), node); 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (!prog && !link) 6438c2ecf20Sopenharmony_ci /* to detach MULTI prog the user has to specify valid FD 6448c2ecf20Sopenharmony_ci * of the program or link to be detached 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* find the prog or link and detach it */ 6498c2ecf20Sopenharmony_ci list_for_each_entry(pl, progs, node) { 6508c2ecf20Sopenharmony_ci if (pl->prog == prog && pl->link == link) 6518c2ecf20Sopenharmony_ci return pl; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/** 6578c2ecf20Sopenharmony_ci * purge_effective_progs() - After compute_effective_progs fails to alloc new 6588c2ecf20Sopenharmony_ci * cgrp->bpf.inactive table we can recover by 6598c2ecf20Sopenharmony_ci * recomputing the array in place. 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * @cgrp: The cgroup which descendants to travers 6628c2ecf20Sopenharmony_ci * @prog: A program to detach or NULL 6638c2ecf20Sopenharmony_ci * @link: A link to detach or NULL 6648c2ecf20Sopenharmony_ci * @type: Type of detach operation 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_cistatic void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, 6678c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link, 6688c2ecf20Sopenharmony_ci enum bpf_attach_type type) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct cgroup_subsys_state *css; 6718c2ecf20Sopenharmony_ci struct bpf_prog_array *progs; 6728c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 6738c2ecf20Sopenharmony_ci struct list_head *head; 6748c2ecf20Sopenharmony_ci struct cgroup *cg; 6758c2ecf20Sopenharmony_ci int pos; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* recompute effective prog array in place */ 6788c2ecf20Sopenharmony_ci css_for_each_descendant_pre(css, &cgrp->self) { 6798c2ecf20Sopenharmony_ci struct cgroup *desc = container_of(css, struct cgroup, self); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (percpu_ref_is_zero(&desc->bpf.refcnt)) 6828c2ecf20Sopenharmony_ci continue; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* find position of link or prog in effective progs array */ 6858c2ecf20Sopenharmony_ci for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { 6868c2ecf20Sopenharmony_ci if (pos && !(cg->bpf.flags[type] & BPF_F_ALLOW_MULTI)) 6878c2ecf20Sopenharmony_ci continue; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci head = &cg->bpf.progs[type]; 6908c2ecf20Sopenharmony_ci list_for_each_entry(pl, head, node) { 6918c2ecf20Sopenharmony_ci if (!prog_list_prog(pl)) 6928c2ecf20Sopenharmony_ci continue; 6938c2ecf20Sopenharmony_ci if (pl->prog == prog && pl->link == link) 6948c2ecf20Sopenharmony_ci goto found; 6958c2ecf20Sopenharmony_ci pos++; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* no link or prog match, skip the cgroup of this layer */ 7008c2ecf20Sopenharmony_ci continue; 7018c2ecf20Sopenharmony_cifound: 7028c2ecf20Sopenharmony_ci progs = rcu_dereference_protected( 7038c2ecf20Sopenharmony_ci desc->bpf.effective[type], 7048c2ecf20Sopenharmony_ci lockdep_is_held(&cgroup_mutex)); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* Remove the program from the array */ 7078c2ecf20Sopenharmony_ci WARN_ONCE(bpf_prog_array_delete_safe_at(progs, pos), 7088c2ecf20Sopenharmony_ci "Failed to purge a prog from array at index %d", pos); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/** 7138c2ecf20Sopenharmony_ci * __cgroup_bpf_detach() - Detach the program or link from a cgroup, and 7148c2ecf20Sopenharmony_ci * propagate the change to descendants 7158c2ecf20Sopenharmony_ci * @cgrp: The cgroup which descendants to traverse 7168c2ecf20Sopenharmony_ci * @prog: A program to detach or NULL 7178c2ecf20Sopenharmony_ci * @prog: A link to detach or NULL 7188c2ecf20Sopenharmony_ci * @type: Type of detach operation 7198c2ecf20Sopenharmony_ci * 7208c2ecf20Sopenharmony_ci * At most one of @prog or @link can be non-NULL. 7218c2ecf20Sopenharmony_ci * Must be called with cgroup_mutex held. 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ciint __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, 7248c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link, enum bpf_attach_type type) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct list_head *progs = &cgrp->bpf.progs[type]; 7278c2ecf20Sopenharmony_ci u32 flags = cgrp->bpf.flags[type]; 7288c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 7298c2ecf20Sopenharmony_ci struct bpf_prog *old_prog; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (prog && link) 7328c2ecf20Sopenharmony_ci /* only one of prog or link can be specified */ 7338c2ecf20Sopenharmony_ci return -EINVAL; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci pl = find_detach_entry(progs, prog, link, flags & BPF_F_ALLOW_MULTI); 7368c2ecf20Sopenharmony_ci if (IS_ERR(pl)) 7378c2ecf20Sopenharmony_ci return PTR_ERR(pl); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* mark it deleted, so it's ignored while recomputing effective */ 7408c2ecf20Sopenharmony_ci old_prog = pl->prog; 7418c2ecf20Sopenharmony_ci pl->prog = NULL; 7428c2ecf20Sopenharmony_ci pl->link = NULL; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (update_effective_progs(cgrp, type)) { 7458c2ecf20Sopenharmony_ci /* if update effective array failed replace the prog with a dummy prog*/ 7468c2ecf20Sopenharmony_ci pl->prog = old_prog; 7478c2ecf20Sopenharmony_ci pl->link = link; 7488c2ecf20Sopenharmony_ci purge_effective_progs(cgrp, old_prog, link, type); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* now can actually delete it from this cgroup list */ 7528c2ecf20Sopenharmony_ci list_del(&pl->node); 7538c2ecf20Sopenharmony_ci kfree(pl); 7548c2ecf20Sopenharmony_ci if (list_empty(progs)) 7558c2ecf20Sopenharmony_ci /* last program was detached, reset flags to zero */ 7568c2ecf20Sopenharmony_ci cgrp->bpf.flags[type] = 0; 7578c2ecf20Sopenharmony_ci if (old_prog) 7588c2ecf20Sopenharmony_ci bpf_prog_put(old_prog); 7598c2ecf20Sopenharmony_ci static_branch_dec(&cgroup_bpf_enabled_key); 7608c2ecf20Sopenharmony_ci return 0; 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci/* Must be called with cgroup_mutex held to avoid races. */ 7648c2ecf20Sopenharmony_ciint __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, 7658c2ecf20Sopenharmony_ci union bpf_attr __user *uattr) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); 7688c2ecf20Sopenharmony_ci enum bpf_attach_type type = attr->query.attach_type; 7698c2ecf20Sopenharmony_ci struct list_head *progs = &cgrp->bpf.progs[type]; 7708c2ecf20Sopenharmony_ci u32 flags = cgrp->bpf.flags[type]; 7718c2ecf20Sopenharmony_ci struct bpf_prog_array *effective; 7728c2ecf20Sopenharmony_ci struct bpf_prog *prog; 7738c2ecf20Sopenharmony_ci int cnt, ret = 0, i; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci effective = rcu_dereference_protected(cgrp->bpf.effective[type], 7768c2ecf20Sopenharmony_ci lockdep_is_held(&cgroup_mutex)); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) 7798c2ecf20Sopenharmony_ci cnt = bpf_prog_array_length(effective); 7808c2ecf20Sopenharmony_ci else 7818c2ecf20Sopenharmony_ci cnt = prog_list_length(progs); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) 7848c2ecf20Sopenharmony_ci return -EFAULT; 7858c2ecf20Sopenharmony_ci if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) 7868c2ecf20Sopenharmony_ci return -EFAULT; 7878c2ecf20Sopenharmony_ci if (attr->query.prog_cnt == 0 || !prog_ids || !cnt) 7888c2ecf20Sopenharmony_ci /* return early if user requested only program count + flags */ 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci if (attr->query.prog_cnt < cnt) { 7918c2ecf20Sopenharmony_ci cnt = attr->query.prog_cnt; 7928c2ecf20Sopenharmony_ci ret = -ENOSPC; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) { 7968c2ecf20Sopenharmony_ci return bpf_prog_array_copy_to_user(effective, prog_ids, cnt); 7978c2ecf20Sopenharmony_ci } else { 7988c2ecf20Sopenharmony_ci struct bpf_prog_list *pl; 7998c2ecf20Sopenharmony_ci u32 id; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci i = 0; 8028c2ecf20Sopenharmony_ci list_for_each_entry(pl, progs, node) { 8038c2ecf20Sopenharmony_ci prog = prog_list_prog(pl); 8048c2ecf20Sopenharmony_ci id = prog->aux->id; 8058c2ecf20Sopenharmony_ci if (copy_to_user(prog_ids + i, &id, sizeof(id))) 8068c2ecf20Sopenharmony_ci return -EFAULT; 8078c2ecf20Sopenharmony_ci if (++i == cnt) 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci return ret; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ciint cgroup_bpf_prog_attach(const union bpf_attr *attr, 8158c2ecf20Sopenharmony_ci enum bpf_prog_type ptype, struct bpf_prog *prog) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct bpf_prog *replace_prog = NULL; 8188c2ecf20Sopenharmony_ci struct cgroup *cgrp; 8198c2ecf20Sopenharmony_ci int ret; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci cgrp = cgroup_get_from_fd(attr->target_fd); 8228c2ecf20Sopenharmony_ci if (IS_ERR(cgrp)) 8238c2ecf20Sopenharmony_ci return PTR_ERR(cgrp); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if ((attr->attach_flags & BPF_F_ALLOW_MULTI) && 8268c2ecf20Sopenharmony_ci (attr->attach_flags & BPF_F_REPLACE)) { 8278c2ecf20Sopenharmony_ci replace_prog = bpf_prog_get_type(attr->replace_bpf_fd, ptype); 8288c2ecf20Sopenharmony_ci if (IS_ERR(replace_prog)) { 8298c2ecf20Sopenharmony_ci cgroup_put(cgrp); 8308c2ecf20Sopenharmony_ci return PTR_ERR(replace_prog); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ret = cgroup_bpf_attach(cgrp, prog, replace_prog, NULL, 8358c2ecf20Sopenharmony_ci attr->attach_type, attr->attach_flags); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (replace_prog) 8388c2ecf20Sopenharmony_ci bpf_prog_put(replace_prog); 8398c2ecf20Sopenharmony_ci cgroup_put(cgrp); 8408c2ecf20Sopenharmony_ci return ret; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ciint cgroup_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct bpf_prog *prog; 8468c2ecf20Sopenharmony_ci struct cgroup *cgrp; 8478c2ecf20Sopenharmony_ci int ret; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci cgrp = cgroup_get_from_fd(attr->target_fd); 8508c2ecf20Sopenharmony_ci if (IS_ERR(cgrp)) 8518c2ecf20Sopenharmony_ci return PTR_ERR(cgrp); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); 8548c2ecf20Sopenharmony_ci if (IS_ERR(prog)) 8558c2ecf20Sopenharmony_ci prog = NULL; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type); 8588c2ecf20Sopenharmony_ci if (prog) 8598c2ecf20Sopenharmony_ci bpf_prog_put(prog); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci cgroup_put(cgrp); 8628c2ecf20Sopenharmony_ci return ret; 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic void bpf_cgroup_link_release(struct bpf_link *link) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct bpf_cgroup_link *cg_link = 8688c2ecf20Sopenharmony_ci container_of(link, struct bpf_cgroup_link, link); 8698c2ecf20Sopenharmony_ci struct cgroup *cg; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* link might have been auto-detached by dying cgroup already, 8728c2ecf20Sopenharmony_ci * in that case our work is done here 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_ci if (!cg_link->cgroup) 8758c2ecf20Sopenharmony_ci return; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci mutex_lock(&cgroup_mutex); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* re-check cgroup under lock again */ 8808c2ecf20Sopenharmony_ci if (!cg_link->cgroup) { 8818c2ecf20Sopenharmony_ci mutex_unlock(&cgroup_mutex); 8828c2ecf20Sopenharmony_ci return; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link, 8868c2ecf20Sopenharmony_ci cg_link->type)); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci cg = cg_link->cgroup; 8898c2ecf20Sopenharmony_ci cg_link->cgroup = NULL; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci mutex_unlock(&cgroup_mutex); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci cgroup_put(cg); 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic void bpf_cgroup_link_dealloc(struct bpf_link *link) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci struct bpf_cgroup_link *cg_link = 8998c2ecf20Sopenharmony_ci container_of(link, struct bpf_cgroup_link, link); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci kfree(cg_link); 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int bpf_cgroup_link_detach(struct bpf_link *link) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci bpf_cgroup_link_release(link); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci return 0; 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link, 9128c2ecf20Sopenharmony_ci struct seq_file *seq) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci struct bpf_cgroup_link *cg_link = 9158c2ecf20Sopenharmony_ci container_of(link, struct bpf_cgroup_link, link); 9168c2ecf20Sopenharmony_ci u64 cg_id = 0; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci mutex_lock(&cgroup_mutex); 9198c2ecf20Sopenharmony_ci if (cg_link->cgroup) 9208c2ecf20Sopenharmony_ci cg_id = cgroup_id(cg_link->cgroup); 9218c2ecf20Sopenharmony_ci mutex_unlock(&cgroup_mutex); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci seq_printf(seq, 9248c2ecf20Sopenharmony_ci "cgroup_id:\t%llu\n" 9258c2ecf20Sopenharmony_ci "attach_type:\t%d\n", 9268c2ecf20Sopenharmony_ci cg_id, 9278c2ecf20Sopenharmony_ci cg_link->type); 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic int bpf_cgroup_link_fill_link_info(const struct bpf_link *link, 9318c2ecf20Sopenharmony_ci struct bpf_link_info *info) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci struct bpf_cgroup_link *cg_link = 9348c2ecf20Sopenharmony_ci container_of(link, struct bpf_cgroup_link, link); 9358c2ecf20Sopenharmony_ci u64 cg_id = 0; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci mutex_lock(&cgroup_mutex); 9388c2ecf20Sopenharmony_ci if (cg_link->cgroup) 9398c2ecf20Sopenharmony_ci cg_id = cgroup_id(cg_link->cgroup); 9408c2ecf20Sopenharmony_ci mutex_unlock(&cgroup_mutex); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci info->cgroup.cgroup_id = cg_id; 9438c2ecf20Sopenharmony_ci info->cgroup.attach_type = cg_link->type; 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic const struct bpf_link_ops bpf_cgroup_link_lops = { 9488c2ecf20Sopenharmony_ci .release = bpf_cgroup_link_release, 9498c2ecf20Sopenharmony_ci .dealloc = bpf_cgroup_link_dealloc, 9508c2ecf20Sopenharmony_ci .detach = bpf_cgroup_link_detach, 9518c2ecf20Sopenharmony_ci .update_prog = cgroup_bpf_replace, 9528c2ecf20Sopenharmony_ci .show_fdinfo = bpf_cgroup_link_show_fdinfo, 9538c2ecf20Sopenharmony_ci .fill_link_info = bpf_cgroup_link_fill_link_info, 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ciint cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct bpf_link_primer link_primer; 9598c2ecf20Sopenharmony_ci struct bpf_cgroup_link *link; 9608c2ecf20Sopenharmony_ci struct cgroup *cgrp; 9618c2ecf20Sopenharmony_ci int err; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (attr->link_create.flags) 9648c2ecf20Sopenharmony_ci return -EINVAL; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci cgrp = cgroup_get_from_fd(attr->link_create.target_fd); 9678c2ecf20Sopenharmony_ci if (IS_ERR(cgrp)) 9688c2ecf20Sopenharmony_ci return PTR_ERR(cgrp); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci link = kzalloc(sizeof(*link), GFP_USER); 9718c2ecf20Sopenharmony_ci if (!link) { 9728c2ecf20Sopenharmony_ci err = -ENOMEM; 9738c2ecf20Sopenharmony_ci goto out_put_cgroup; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci bpf_link_init(&link->link, BPF_LINK_TYPE_CGROUP, &bpf_cgroup_link_lops, 9768c2ecf20Sopenharmony_ci prog); 9778c2ecf20Sopenharmony_ci link->cgroup = cgrp; 9788c2ecf20Sopenharmony_ci link->type = attr->link_create.attach_type; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci err = bpf_link_prime(&link->link, &link_primer); 9818c2ecf20Sopenharmony_ci if (err) { 9828c2ecf20Sopenharmony_ci kfree(link); 9838c2ecf20Sopenharmony_ci goto out_put_cgroup; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci err = cgroup_bpf_attach(cgrp, NULL, NULL, link, link->type, 9878c2ecf20Sopenharmony_ci BPF_F_ALLOW_MULTI); 9888c2ecf20Sopenharmony_ci if (err) { 9898c2ecf20Sopenharmony_ci bpf_link_cleanup(&link_primer); 9908c2ecf20Sopenharmony_ci goto out_put_cgroup; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return bpf_link_settle(&link_primer); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ciout_put_cgroup: 9968c2ecf20Sopenharmony_ci cgroup_put(cgrp); 9978c2ecf20Sopenharmony_ci return err; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ciint cgroup_bpf_prog_query(const union bpf_attr *attr, 10018c2ecf20Sopenharmony_ci union bpf_attr __user *uattr) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct cgroup *cgrp; 10048c2ecf20Sopenharmony_ci int ret; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci cgrp = cgroup_get_from_fd(attr->query.target_fd); 10078c2ecf20Sopenharmony_ci if (IS_ERR(cgrp)) 10088c2ecf20Sopenharmony_ci return PTR_ERR(cgrp); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci ret = cgroup_bpf_query(cgrp, attr, uattr); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci cgroup_put(cgrp); 10138c2ecf20Sopenharmony_ci return ret; 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci/** 10178c2ecf20Sopenharmony_ci * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering 10188c2ecf20Sopenharmony_ci * @sk: The socket sending or receiving traffic 10198c2ecf20Sopenharmony_ci * @skb: The skb that is being sent or received 10208c2ecf20Sopenharmony_ci * @type: The type of program to be exectuted 10218c2ecf20Sopenharmony_ci * 10228c2ecf20Sopenharmony_ci * If no socket is passed, or the socket is not of type INET or INET6, 10238c2ecf20Sopenharmony_ci * this function does nothing and returns 0. 10248c2ecf20Sopenharmony_ci * 10258c2ecf20Sopenharmony_ci * The program type passed in via @type must be suitable for network 10268c2ecf20Sopenharmony_ci * filtering. No further check is performed to assert that. 10278c2ecf20Sopenharmony_ci * 10288c2ecf20Sopenharmony_ci * For egress packets, this function can return: 10298c2ecf20Sopenharmony_ci * NET_XMIT_SUCCESS (0) - continue with packet output 10308c2ecf20Sopenharmony_ci * NET_XMIT_DROP (1) - drop packet and notify TCP to call cwr 10318c2ecf20Sopenharmony_ci * NET_XMIT_CN (2) - continue with packet output and notify TCP 10328c2ecf20Sopenharmony_ci * to call cwr 10338c2ecf20Sopenharmony_ci * -EPERM - drop packet 10348c2ecf20Sopenharmony_ci * 10358c2ecf20Sopenharmony_ci * For ingress packets, this function will return -EPERM if any 10368c2ecf20Sopenharmony_ci * attached program was found and if it returned != 1 during execution. 10378c2ecf20Sopenharmony_ci * Otherwise 0 is returned. 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_skb(struct sock *sk, 10408c2ecf20Sopenharmony_ci struct sk_buff *skb, 10418c2ecf20Sopenharmony_ci enum bpf_attach_type type) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci unsigned int offset = skb->data - skb_network_header(skb); 10448c2ecf20Sopenharmony_ci struct sock *save_sk; 10458c2ecf20Sopenharmony_ci void *saved_data_end; 10468c2ecf20Sopenharmony_ci struct cgroup *cgrp; 10478c2ecf20Sopenharmony_ci int ret; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (!sk || !sk_fullsock(sk)) 10508c2ecf20Sopenharmony_ci return 0; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6) 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 10568c2ecf20Sopenharmony_ci save_sk = skb->sk; 10578c2ecf20Sopenharmony_ci skb->sk = sk; 10588c2ecf20Sopenharmony_ci __skb_push(skb, offset); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* compute pointers for the bpf prog */ 10618c2ecf20Sopenharmony_ci bpf_compute_and_save_data_end(skb, &saved_data_end); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci if (type == BPF_CGROUP_INET_EGRESS) { 10648c2ecf20Sopenharmony_ci ret = BPF_PROG_CGROUP_INET_EGRESS_RUN_ARRAY( 10658c2ecf20Sopenharmony_ci cgrp->bpf.effective[type], skb, __bpf_prog_run_save_cb); 10668c2ecf20Sopenharmony_ci } else { 10678c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], skb, 10688c2ecf20Sopenharmony_ci __bpf_prog_run_save_cb); 10698c2ecf20Sopenharmony_ci ret = (ret == 1 ? 0 : -EPERM); 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci bpf_restore_data_end(skb, saved_data_end); 10728c2ecf20Sopenharmony_ci __skb_pull(skb, offset); 10738c2ecf20Sopenharmony_ci skb->sk = save_sk; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cgroup_bpf_run_filter_skb); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci/** 10808c2ecf20Sopenharmony_ci * __cgroup_bpf_run_filter_sk() - Run a program on a sock 10818c2ecf20Sopenharmony_ci * @sk: sock structure to manipulate 10828c2ecf20Sopenharmony_ci * @type: The type of program to be exectuted 10838c2ecf20Sopenharmony_ci * 10848c2ecf20Sopenharmony_ci * socket is passed is expected to be of type INET or INET6. 10858c2ecf20Sopenharmony_ci * 10868c2ecf20Sopenharmony_ci * The program type passed in via @type must be suitable for sock 10878c2ecf20Sopenharmony_ci * filtering. No further check is performed to assert that. 10888c2ecf20Sopenharmony_ci * 10898c2ecf20Sopenharmony_ci * This function will return %-EPERM if any if an attached program was found 10908c2ecf20Sopenharmony_ci * and if it returned != 1 during execution. In all other cases, 0 is returned. 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_sk(struct sock *sk, 10938c2ecf20Sopenharmony_ci enum bpf_attach_type type) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 10968c2ecf20Sopenharmony_ci int ret; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], sk, BPF_PROG_RUN); 10998c2ecf20Sopenharmony_ci return ret == 1 ? 0 : -EPERM; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci/** 11048c2ecf20Sopenharmony_ci * __cgroup_bpf_run_filter_sock_addr() - Run a program on a sock and 11058c2ecf20Sopenharmony_ci * provided by user sockaddr 11068c2ecf20Sopenharmony_ci * @sk: sock struct that will use sockaddr 11078c2ecf20Sopenharmony_ci * @uaddr: sockaddr struct provided by user 11088c2ecf20Sopenharmony_ci * @type: The type of program to be exectuted 11098c2ecf20Sopenharmony_ci * @t_ctx: Pointer to attach type specific context 11108c2ecf20Sopenharmony_ci * 11118c2ecf20Sopenharmony_ci * socket is expected to be of type INET or INET6. 11128c2ecf20Sopenharmony_ci * 11138c2ecf20Sopenharmony_ci * This function will return %-EPERM if an attached program is found and 11148c2ecf20Sopenharmony_ci * returned value != 1 during execution. In all other cases, 0 is returned. 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_sock_addr(struct sock *sk, 11178c2ecf20Sopenharmony_ci struct sockaddr *uaddr, 11188c2ecf20Sopenharmony_ci enum bpf_attach_type type, 11198c2ecf20Sopenharmony_ci void *t_ctx) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct bpf_sock_addr_kern ctx = { 11228c2ecf20Sopenharmony_ci .sk = sk, 11238c2ecf20Sopenharmony_ci .uaddr = uaddr, 11248c2ecf20Sopenharmony_ci .t_ctx = t_ctx, 11258c2ecf20Sopenharmony_ci }; 11268c2ecf20Sopenharmony_ci struct sockaddr_storage unspec; 11278c2ecf20Sopenharmony_ci struct cgroup *cgrp; 11288c2ecf20Sopenharmony_ci int ret; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Check socket family since not all sockets represent network 11318c2ecf20Sopenharmony_ci * endpoint (e.g. AF_UNIX). 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_ci if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6) 11348c2ecf20Sopenharmony_ci return 0; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (!ctx.uaddr) { 11378c2ecf20Sopenharmony_ci memset(&unspec, 0, sizeof(unspec)); 11388c2ecf20Sopenharmony_ci ctx.uaddr = (struct sockaddr *)&unspec; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 11428c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return ret == 1 ? 0 : -EPERM; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci/** 11498c2ecf20Sopenharmony_ci * __cgroup_bpf_run_filter_sock_ops() - Run a program on a sock 11508c2ecf20Sopenharmony_ci * @sk: socket to get cgroup from 11518c2ecf20Sopenharmony_ci * @sock_ops: bpf_sock_ops_kern struct to pass to program. Contains 11528c2ecf20Sopenharmony_ci * sk with connection information (IP addresses, etc.) May not contain 11538c2ecf20Sopenharmony_ci * cgroup info if it is a req sock. 11548c2ecf20Sopenharmony_ci * @type: The type of program to be exectuted 11558c2ecf20Sopenharmony_ci * 11568c2ecf20Sopenharmony_ci * socket passed is expected to be of type INET or INET6. 11578c2ecf20Sopenharmony_ci * 11588c2ecf20Sopenharmony_ci * The program type passed in via @type must be suitable for sock_ops 11598c2ecf20Sopenharmony_ci * filtering. No further check is performed to assert that. 11608c2ecf20Sopenharmony_ci * 11618c2ecf20Sopenharmony_ci * This function will return %-EPERM if any if an attached program was found 11628c2ecf20Sopenharmony_ci * and if it returned != 1 during execution. In all other cases, 0 is returned. 11638c2ecf20Sopenharmony_ci */ 11648c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_sock_ops(struct sock *sk, 11658c2ecf20Sopenharmony_ci struct bpf_sock_ops_kern *sock_ops, 11668c2ecf20Sopenharmony_ci enum bpf_attach_type type) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 11698c2ecf20Sopenharmony_ci int ret; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], sock_ops, 11728c2ecf20Sopenharmony_ci BPF_PROG_RUN); 11738c2ecf20Sopenharmony_ci return ret == 1 ? 0 : -EPERM; 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ciint __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, 11788c2ecf20Sopenharmony_ci short access, enum bpf_attach_type type) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci struct cgroup *cgrp; 11818c2ecf20Sopenharmony_ci struct bpf_cgroup_dev_ctx ctx = { 11828c2ecf20Sopenharmony_ci .access_type = (access << 16) | dev_type, 11838c2ecf20Sopenharmony_ci .major = major, 11848c2ecf20Sopenharmony_ci .minor = minor, 11858c2ecf20Sopenharmony_ci }; 11868c2ecf20Sopenharmony_ci int allow = 1; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci rcu_read_lock(); 11898c2ecf20Sopenharmony_ci cgrp = task_dfl_cgroup(current); 11908c2ecf20Sopenharmony_ci allow = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, 11918c2ecf20Sopenharmony_ci BPF_PROG_RUN); 11928c2ecf20Sopenharmony_ci rcu_read_unlock(); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return !allow; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic const struct bpf_func_proto * 11988c2ecf20Sopenharmony_cicgroup_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci switch (func_id) { 12018c2ecf20Sopenharmony_ci case BPF_FUNC_get_current_uid_gid: 12028c2ecf20Sopenharmony_ci return &bpf_get_current_uid_gid_proto; 12038c2ecf20Sopenharmony_ci case BPF_FUNC_get_local_storage: 12048c2ecf20Sopenharmony_ci return &bpf_get_local_storage_proto; 12058c2ecf20Sopenharmony_ci case BPF_FUNC_get_current_cgroup_id: 12068c2ecf20Sopenharmony_ci return &bpf_get_current_cgroup_id_proto; 12078c2ecf20Sopenharmony_ci case BPF_FUNC_perf_event_output: 12088c2ecf20Sopenharmony_ci return &bpf_event_output_data_proto; 12098c2ecf20Sopenharmony_ci default: 12108c2ecf20Sopenharmony_ci return bpf_base_func_proto(func_id); 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic const struct bpf_func_proto * 12158c2ecf20Sopenharmony_cicgroup_dev_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 12168c2ecf20Sopenharmony_ci{ 12178c2ecf20Sopenharmony_ci return cgroup_base_func_proto(func_id, prog); 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_cistatic bool cgroup_dev_is_valid_access(int off, int size, 12218c2ecf20Sopenharmony_ci enum bpf_access_type type, 12228c2ecf20Sopenharmony_ci const struct bpf_prog *prog, 12238c2ecf20Sopenharmony_ci struct bpf_insn_access_aux *info) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci const int size_default = sizeof(__u32); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (type == BPF_WRITE) 12288c2ecf20Sopenharmony_ci return false; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (off < 0 || off + size > sizeof(struct bpf_cgroup_dev_ctx)) 12318c2ecf20Sopenharmony_ci return false; 12328c2ecf20Sopenharmony_ci /* The verifier guarantees that size > 0. */ 12338c2ecf20Sopenharmony_ci if (off % size != 0) 12348c2ecf20Sopenharmony_ci return false; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci switch (off) { 12378c2ecf20Sopenharmony_ci case bpf_ctx_range(struct bpf_cgroup_dev_ctx, access_type): 12388c2ecf20Sopenharmony_ci bpf_ctx_record_field_size(info, size_default); 12398c2ecf20Sopenharmony_ci if (!bpf_ctx_narrow_access_ok(off, size, size_default)) 12408c2ecf20Sopenharmony_ci return false; 12418c2ecf20Sopenharmony_ci break; 12428c2ecf20Sopenharmony_ci default: 12438c2ecf20Sopenharmony_ci if (size != size_default) 12448c2ecf20Sopenharmony_ci return false; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci return true; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ciconst struct bpf_prog_ops cg_dev_prog_ops = { 12518c2ecf20Sopenharmony_ci}; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ciconst struct bpf_verifier_ops cg_dev_verifier_ops = { 12548c2ecf20Sopenharmony_ci .get_func_proto = cgroup_dev_func_proto, 12558c2ecf20Sopenharmony_ci .is_valid_access = cgroup_dev_is_valid_access, 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/** 12598c2ecf20Sopenharmony_ci * __cgroup_bpf_run_filter_sysctl - Run a program on sysctl 12608c2ecf20Sopenharmony_ci * 12618c2ecf20Sopenharmony_ci * @head: sysctl table header 12628c2ecf20Sopenharmony_ci * @table: sysctl table 12638c2ecf20Sopenharmony_ci * @write: sysctl is being read (= 0) or written (= 1) 12648c2ecf20Sopenharmony_ci * @buf: pointer to buffer (in and out) 12658c2ecf20Sopenharmony_ci * @pcount: value-result argument: value is size of buffer pointed to by @buf, 12668c2ecf20Sopenharmony_ci * result is size of @new_buf if program set new value, initial value 12678c2ecf20Sopenharmony_ci * otherwise 12688c2ecf20Sopenharmony_ci * @ppos: value-result argument: value is position at which read from or write 12698c2ecf20Sopenharmony_ci * to sysctl is happening, result is new position if program overrode it, 12708c2ecf20Sopenharmony_ci * initial value otherwise 12718c2ecf20Sopenharmony_ci * @type: type of program to be executed 12728c2ecf20Sopenharmony_ci * 12738c2ecf20Sopenharmony_ci * Program is run when sysctl is being accessed, either read or written, and 12748c2ecf20Sopenharmony_ci * can allow or deny such access. 12758c2ecf20Sopenharmony_ci * 12768c2ecf20Sopenharmony_ci * This function will return %-EPERM if an attached program is found and 12778c2ecf20Sopenharmony_ci * returned value != 1 during execution. In all other cases 0 is returned. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head, 12808c2ecf20Sopenharmony_ci struct ctl_table *table, int write, 12818c2ecf20Sopenharmony_ci char **buf, size_t *pcount, loff_t *ppos, 12828c2ecf20Sopenharmony_ci enum bpf_attach_type type) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci struct bpf_sysctl_kern ctx = { 12858c2ecf20Sopenharmony_ci .head = head, 12868c2ecf20Sopenharmony_ci .table = table, 12878c2ecf20Sopenharmony_ci .write = write, 12888c2ecf20Sopenharmony_ci .ppos = ppos, 12898c2ecf20Sopenharmony_ci .cur_val = NULL, 12908c2ecf20Sopenharmony_ci .cur_len = PAGE_SIZE, 12918c2ecf20Sopenharmony_ci .new_val = NULL, 12928c2ecf20Sopenharmony_ci .new_len = 0, 12938c2ecf20Sopenharmony_ci .new_updated = 0, 12948c2ecf20Sopenharmony_ci }; 12958c2ecf20Sopenharmony_ci struct cgroup *cgrp; 12968c2ecf20Sopenharmony_ci loff_t pos = 0; 12978c2ecf20Sopenharmony_ci int ret; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci ctx.cur_val = kmalloc_track_caller(ctx.cur_len, GFP_KERNEL); 13008c2ecf20Sopenharmony_ci if (!ctx.cur_val || 13018c2ecf20Sopenharmony_ci table->proc_handler(table, 0, ctx.cur_val, &ctx.cur_len, &pos)) { 13028c2ecf20Sopenharmony_ci /* Let BPF program decide how to proceed. */ 13038c2ecf20Sopenharmony_ci ctx.cur_len = 0; 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (write && *buf && *pcount) { 13078c2ecf20Sopenharmony_ci /* BPF program should be able to override new value with a 13088c2ecf20Sopenharmony_ci * buffer bigger than provided by user. 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_ci ctx.new_val = kmalloc_track_caller(PAGE_SIZE, GFP_KERNEL); 13118c2ecf20Sopenharmony_ci ctx.new_len = min_t(size_t, PAGE_SIZE, *pcount); 13128c2ecf20Sopenharmony_ci if (ctx.new_val) { 13138c2ecf20Sopenharmony_ci memcpy(ctx.new_val, *buf, ctx.new_len); 13148c2ecf20Sopenharmony_ci } else { 13158c2ecf20Sopenharmony_ci /* Let BPF program decide how to proceed. */ 13168c2ecf20Sopenharmony_ci ctx.new_len = 0; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci rcu_read_lock(); 13218c2ecf20Sopenharmony_ci cgrp = task_dfl_cgroup(current); 13228c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN); 13238c2ecf20Sopenharmony_ci rcu_read_unlock(); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci kfree(ctx.cur_val); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (ret == 1 && ctx.new_updated) { 13288c2ecf20Sopenharmony_ci kfree(*buf); 13298c2ecf20Sopenharmony_ci *buf = ctx.new_val; 13308c2ecf20Sopenharmony_ci *pcount = ctx.new_len; 13318c2ecf20Sopenharmony_ci } else { 13328c2ecf20Sopenharmony_ci kfree(ctx.new_val); 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci return ret == 1 ? 0 : -EPERM; 13368c2ecf20Sopenharmony_ci} 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci#ifdef CONFIG_NET 13398c2ecf20Sopenharmony_cistatic bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp, 13408c2ecf20Sopenharmony_ci enum bpf_attach_type attach_type) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci struct bpf_prog_array *prog_array; 13438c2ecf20Sopenharmony_ci bool empty; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci rcu_read_lock(); 13468c2ecf20Sopenharmony_ci prog_array = rcu_dereference(cgrp->bpf.effective[attach_type]); 13478c2ecf20Sopenharmony_ci empty = bpf_prog_array_is_empty(prog_array); 13488c2ecf20Sopenharmony_ci rcu_read_unlock(); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci return empty; 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci if (unlikely(max_optlen < 0)) 13568c2ecf20Sopenharmony_ci return -EINVAL; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (unlikely(max_optlen > PAGE_SIZE)) { 13598c2ecf20Sopenharmony_ci /* We don't expose optvals that are greater than PAGE_SIZE 13608c2ecf20Sopenharmony_ci * to the BPF program. 13618c2ecf20Sopenharmony_ci */ 13628c2ecf20Sopenharmony_ci max_optlen = PAGE_SIZE; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci ctx->optval = kzalloc(max_optlen, GFP_USER); 13668c2ecf20Sopenharmony_ci if (!ctx->optval) 13678c2ecf20Sopenharmony_ci return -ENOMEM; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci ctx->optval_end = ctx->optval + max_optlen; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci return max_optlen; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_cistatic void sockopt_free_buf(struct bpf_sockopt_kern *ctx) 13758c2ecf20Sopenharmony_ci{ 13768c2ecf20Sopenharmony_ci kfree(ctx->optval); 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, 13808c2ecf20Sopenharmony_ci int *optname, char __user *optval, 13818c2ecf20Sopenharmony_ci int *optlen, char **kernel_optval) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 13848c2ecf20Sopenharmony_ci struct bpf_sockopt_kern ctx = { 13858c2ecf20Sopenharmony_ci .sk = sk, 13868c2ecf20Sopenharmony_ci .level = *level, 13878c2ecf20Sopenharmony_ci .optname = *optname, 13888c2ecf20Sopenharmony_ci }; 13898c2ecf20Sopenharmony_ci int ret, max_optlen; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* Opportunistic check to see whether we have any BPF program 13928c2ecf20Sopenharmony_ci * attached to the hook so we don't waste time allocating 13938c2ecf20Sopenharmony_ci * memory and locking the socket. 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci if (!cgroup_bpf_enabled || 13968c2ecf20Sopenharmony_ci __cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_SETSOCKOPT)) 13978c2ecf20Sopenharmony_ci return 0; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* Allocate a bit more than the initial user buffer for 14008c2ecf20Sopenharmony_ci * BPF program. The canonical use case is overriding 14018c2ecf20Sopenharmony_ci * TCP_CONGESTION(nv) to TCP_CONGESTION(cubic). 14028c2ecf20Sopenharmony_ci */ 14038c2ecf20Sopenharmony_ci max_optlen = max_t(int, 16, *optlen); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci max_optlen = sockopt_alloc_buf(&ctx, max_optlen); 14068c2ecf20Sopenharmony_ci if (max_optlen < 0) 14078c2ecf20Sopenharmony_ci return max_optlen; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci ctx.optlen = *optlen; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (copy_from_user(ctx.optval, optval, min(*optlen, max_optlen)) != 0) { 14128c2ecf20Sopenharmony_ci ret = -EFAULT; 14138c2ecf20Sopenharmony_ci goto out; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci lock_sock(sk); 14178c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[BPF_CGROUP_SETSOCKOPT], 14188c2ecf20Sopenharmony_ci &ctx, BPF_PROG_RUN); 14198c2ecf20Sopenharmony_ci release_sock(sk); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (!ret) { 14228c2ecf20Sopenharmony_ci ret = -EPERM; 14238c2ecf20Sopenharmony_ci goto out; 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (ctx.optlen == -1) { 14278c2ecf20Sopenharmony_ci /* optlen set to -1, bypass kernel */ 14288c2ecf20Sopenharmony_ci ret = 1; 14298c2ecf20Sopenharmony_ci } else if (ctx.optlen > max_optlen || ctx.optlen < -1) { 14308c2ecf20Sopenharmony_ci /* optlen is out of bounds */ 14318c2ecf20Sopenharmony_ci ret = -EFAULT; 14328c2ecf20Sopenharmony_ci } else { 14338c2ecf20Sopenharmony_ci /* optlen within bounds, run kernel handler */ 14348c2ecf20Sopenharmony_ci ret = 0; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci /* export any potential modifications */ 14378c2ecf20Sopenharmony_ci *level = ctx.level; 14388c2ecf20Sopenharmony_ci *optname = ctx.optname; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* optlen == 0 from BPF indicates that we should 14418c2ecf20Sopenharmony_ci * use original userspace data. 14428c2ecf20Sopenharmony_ci */ 14438c2ecf20Sopenharmony_ci if (ctx.optlen != 0) { 14448c2ecf20Sopenharmony_ci *optlen = ctx.optlen; 14458c2ecf20Sopenharmony_ci *kernel_optval = ctx.optval; 14468c2ecf20Sopenharmony_ci /* export and don't free sockopt buf */ 14478c2ecf20Sopenharmony_ci return 0; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ciout: 14528c2ecf20Sopenharmony_ci sockopt_free_buf(&ctx); 14538c2ecf20Sopenharmony_ci return ret; 14548c2ecf20Sopenharmony_ci} 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, 14578c2ecf20Sopenharmony_ci int optname, char __user *optval, 14588c2ecf20Sopenharmony_ci int __user *optlen, int max_optlen, 14598c2ecf20Sopenharmony_ci int retval) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 14628c2ecf20Sopenharmony_ci struct bpf_sockopt_kern ctx = { 14638c2ecf20Sopenharmony_ci .sk = sk, 14648c2ecf20Sopenharmony_ci .level = level, 14658c2ecf20Sopenharmony_ci .optname = optname, 14668c2ecf20Sopenharmony_ci .retval = retval, 14678c2ecf20Sopenharmony_ci }; 14688c2ecf20Sopenharmony_ci int ret; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Opportunistic check to see whether we have any BPF program 14718c2ecf20Sopenharmony_ci * attached to the hook so we don't waste time allocating 14728c2ecf20Sopenharmony_ci * memory and locking the socket. 14738c2ecf20Sopenharmony_ci */ 14748c2ecf20Sopenharmony_ci if (!cgroup_bpf_enabled || 14758c2ecf20Sopenharmony_ci __cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_GETSOCKOPT)) 14768c2ecf20Sopenharmony_ci return retval; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci ctx.optlen = max_optlen; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci max_optlen = sockopt_alloc_buf(&ctx, max_optlen); 14818c2ecf20Sopenharmony_ci if (max_optlen < 0) 14828c2ecf20Sopenharmony_ci return max_optlen; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if (!retval) { 14858c2ecf20Sopenharmony_ci /* If kernel getsockopt finished successfully, 14868c2ecf20Sopenharmony_ci * copy whatever was returned to the user back 14878c2ecf20Sopenharmony_ci * into our temporary buffer. Set optlen to the 14888c2ecf20Sopenharmony_ci * one that kernel returned as well to let 14898c2ecf20Sopenharmony_ci * BPF programs inspect the value. 14908c2ecf20Sopenharmony_ci */ 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (get_user(ctx.optlen, optlen)) { 14938c2ecf20Sopenharmony_ci ret = -EFAULT; 14948c2ecf20Sopenharmony_ci goto out; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci if (ctx.optlen < 0) { 14988c2ecf20Sopenharmony_ci ret = -EFAULT; 14998c2ecf20Sopenharmony_ci goto out; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (copy_from_user(ctx.optval, optval, 15038c2ecf20Sopenharmony_ci min(ctx.optlen, max_optlen)) != 0) { 15048c2ecf20Sopenharmony_ci ret = -EFAULT; 15058c2ecf20Sopenharmony_ci goto out; 15068c2ecf20Sopenharmony_ci } 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci lock_sock(sk); 15108c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[BPF_CGROUP_GETSOCKOPT], 15118c2ecf20Sopenharmony_ci &ctx, BPF_PROG_RUN); 15128c2ecf20Sopenharmony_ci release_sock(sk); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (!ret) { 15158c2ecf20Sopenharmony_ci ret = -EPERM; 15168c2ecf20Sopenharmony_ci goto out; 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci if (optval && (ctx.optlen > max_optlen || ctx.optlen < 0)) { 15208c2ecf20Sopenharmony_ci ret = -EFAULT; 15218c2ecf20Sopenharmony_ci goto out; 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci /* BPF programs only allowed to set retval to 0, not some 15258c2ecf20Sopenharmony_ci * arbitrary value. 15268c2ecf20Sopenharmony_ci */ 15278c2ecf20Sopenharmony_ci if (ctx.retval != 0 && ctx.retval != retval) { 15288c2ecf20Sopenharmony_ci ret = -EFAULT; 15298c2ecf20Sopenharmony_ci goto out; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci if (ctx.optlen != 0) { 15338c2ecf20Sopenharmony_ci if (optval && copy_to_user(optval, ctx.optval, ctx.optlen)) { 15348c2ecf20Sopenharmony_ci ret = -EFAULT; 15358c2ecf20Sopenharmony_ci goto out; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci if (put_user(ctx.optlen, optlen)) { 15388c2ecf20Sopenharmony_ci ret = -EFAULT; 15398c2ecf20Sopenharmony_ci goto out; 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci ret = ctx.retval; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ciout: 15468c2ecf20Sopenharmony_ci sockopt_free_buf(&ctx); 15478c2ecf20Sopenharmony_ci return ret; 15488c2ecf20Sopenharmony_ci} 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ciint __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level, 15518c2ecf20Sopenharmony_ci int optname, void *optval, 15528c2ecf20Sopenharmony_ci int *optlen, int retval) 15538c2ecf20Sopenharmony_ci{ 15548c2ecf20Sopenharmony_ci struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); 15558c2ecf20Sopenharmony_ci struct bpf_sockopt_kern ctx = { 15568c2ecf20Sopenharmony_ci .sk = sk, 15578c2ecf20Sopenharmony_ci .level = level, 15588c2ecf20Sopenharmony_ci .optname = optname, 15598c2ecf20Sopenharmony_ci .retval = retval, 15608c2ecf20Sopenharmony_ci .optlen = *optlen, 15618c2ecf20Sopenharmony_ci .optval = optval, 15628c2ecf20Sopenharmony_ci .optval_end = optval + *optlen, 15638c2ecf20Sopenharmony_ci }; 15648c2ecf20Sopenharmony_ci int ret; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci /* Note that __cgroup_bpf_run_filter_getsockopt doesn't copy 15678c2ecf20Sopenharmony_ci * user data back into BPF buffer when reval != 0. This is 15688c2ecf20Sopenharmony_ci * done as an optimization to avoid extra copy, assuming 15698c2ecf20Sopenharmony_ci * kernel won't populate the data in case of an error. 15708c2ecf20Sopenharmony_ci * Here we always pass the data and memset() should 15718c2ecf20Sopenharmony_ci * be called if that data shouldn't be "exported". 15728c2ecf20Sopenharmony_ci */ 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[BPF_CGROUP_GETSOCKOPT], 15758c2ecf20Sopenharmony_ci &ctx, BPF_PROG_RUN); 15768c2ecf20Sopenharmony_ci if (!ret) 15778c2ecf20Sopenharmony_ci return -EPERM; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (ctx.optlen > *optlen) 15808c2ecf20Sopenharmony_ci return -EFAULT; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci /* BPF programs only allowed to set retval to 0, not some 15838c2ecf20Sopenharmony_ci * arbitrary value. 15848c2ecf20Sopenharmony_ci */ 15858c2ecf20Sopenharmony_ci if (ctx.retval != 0 && ctx.retval != retval) 15868c2ecf20Sopenharmony_ci return -EFAULT; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* BPF programs can shrink the buffer, export the modifications. 15898c2ecf20Sopenharmony_ci */ 15908c2ecf20Sopenharmony_ci if (ctx.optlen != 0) 15918c2ecf20Sopenharmony_ci *optlen = ctx.optlen; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci return ctx.retval; 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci#endif 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic ssize_t sysctl_cpy_dir(const struct ctl_dir *dir, char **bufp, 15988c2ecf20Sopenharmony_ci size_t *lenp) 15998c2ecf20Sopenharmony_ci{ 16008c2ecf20Sopenharmony_ci ssize_t tmp_ret = 0, ret; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci if (dir->header.parent) { 16038c2ecf20Sopenharmony_ci tmp_ret = sysctl_cpy_dir(dir->header.parent, bufp, lenp); 16048c2ecf20Sopenharmony_ci if (tmp_ret < 0) 16058c2ecf20Sopenharmony_ci return tmp_ret; 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci ret = strscpy(*bufp, dir->header.ctl_table[0].procname, *lenp); 16098c2ecf20Sopenharmony_ci if (ret < 0) 16108c2ecf20Sopenharmony_ci return ret; 16118c2ecf20Sopenharmony_ci *bufp += ret; 16128c2ecf20Sopenharmony_ci *lenp -= ret; 16138c2ecf20Sopenharmony_ci ret += tmp_ret; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci /* Avoid leading slash. */ 16168c2ecf20Sopenharmony_ci if (!ret) 16178c2ecf20Sopenharmony_ci return ret; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci tmp_ret = strscpy(*bufp, "/", *lenp); 16208c2ecf20Sopenharmony_ci if (tmp_ret < 0) 16218c2ecf20Sopenharmony_ci return tmp_ret; 16228c2ecf20Sopenharmony_ci *bufp += tmp_ret; 16238c2ecf20Sopenharmony_ci *lenp -= tmp_ret; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci return ret + tmp_ret; 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ciBPF_CALL_4(bpf_sysctl_get_name, struct bpf_sysctl_kern *, ctx, char *, buf, 16298c2ecf20Sopenharmony_ci size_t, buf_len, u64, flags) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci ssize_t tmp_ret = 0, ret; 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci if (!buf) 16348c2ecf20Sopenharmony_ci return -EINVAL; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci if (!(flags & BPF_F_SYSCTL_BASE_NAME)) { 16378c2ecf20Sopenharmony_ci if (!ctx->head) 16388c2ecf20Sopenharmony_ci return -EINVAL; 16398c2ecf20Sopenharmony_ci tmp_ret = sysctl_cpy_dir(ctx->head->parent, &buf, &buf_len); 16408c2ecf20Sopenharmony_ci if (tmp_ret < 0) 16418c2ecf20Sopenharmony_ci return tmp_ret; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci ret = strscpy(buf, ctx->table->procname, buf_len); 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci return ret < 0 ? ret : tmp_ret + ret; 16478c2ecf20Sopenharmony_ci} 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_cistatic const struct bpf_func_proto bpf_sysctl_get_name_proto = { 16508c2ecf20Sopenharmony_ci .func = bpf_sysctl_get_name, 16518c2ecf20Sopenharmony_ci .gpl_only = false, 16528c2ecf20Sopenharmony_ci .ret_type = RET_INTEGER, 16538c2ecf20Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 16548c2ecf20Sopenharmony_ci .arg2_type = ARG_PTR_TO_MEM, 16558c2ecf20Sopenharmony_ci .arg3_type = ARG_CONST_SIZE, 16568c2ecf20Sopenharmony_ci .arg4_type = ARG_ANYTHING, 16578c2ecf20Sopenharmony_ci}; 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic int copy_sysctl_value(char *dst, size_t dst_len, char *src, 16608c2ecf20Sopenharmony_ci size_t src_len) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci if (!dst) 16638c2ecf20Sopenharmony_ci return -EINVAL; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if (!dst_len) 16668c2ecf20Sopenharmony_ci return -E2BIG; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (!src || !src_len) { 16698c2ecf20Sopenharmony_ci memset(dst, 0, dst_len); 16708c2ecf20Sopenharmony_ci return -EINVAL; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci memcpy(dst, src, min(dst_len, src_len)); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (dst_len > src_len) { 16768c2ecf20Sopenharmony_ci memset(dst + src_len, '\0', dst_len - src_len); 16778c2ecf20Sopenharmony_ci return src_len; 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci dst[dst_len - 1] = '\0'; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci return -E2BIG; 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ciBPF_CALL_3(bpf_sysctl_get_current_value, struct bpf_sysctl_kern *, ctx, 16868c2ecf20Sopenharmony_ci char *, buf, size_t, buf_len) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci return copy_sysctl_value(buf, buf_len, ctx->cur_val, ctx->cur_len); 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic const struct bpf_func_proto bpf_sysctl_get_current_value_proto = { 16928c2ecf20Sopenharmony_ci .func = bpf_sysctl_get_current_value, 16938c2ecf20Sopenharmony_ci .gpl_only = false, 16948c2ecf20Sopenharmony_ci .ret_type = RET_INTEGER, 16958c2ecf20Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 16968c2ecf20Sopenharmony_ci .arg2_type = ARG_PTR_TO_UNINIT_MEM, 16978c2ecf20Sopenharmony_ci .arg3_type = ARG_CONST_SIZE, 16988c2ecf20Sopenharmony_ci}; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ciBPF_CALL_3(bpf_sysctl_get_new_value, struct bpf_sysctl_kern *, ctx, char *, buf, 17018c2ecf20Sopenharmony_ci size_t, buf_len) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci if (!ctx->write) { 17048c2ecf20Sopenharmony_ci if (buf && buf_len) 17058c2ecf20Sopenharmony_ci memset(buf, '\0', buf_len); 17068c2ecf20Sopenharmony_ci return -EINVAL; 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci return copy_sysctl_value(buf, buf_len, ctx->new_val, ctx->new_len); 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cistatic const struct bpf_func_proto bpf_sysctl_get_new_value_proto = { 17128c2ecf20Sopenharmony_ci .func = bpf_sysctl_get_new_value, 17138c2ecf20Sopenharmony_ci .gpl_only = false, 17148c2ecf20Sopenharmony_ci .ret_type = RET_INTEGER, 17158c2ecf20Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 17168c2ecf20Sopenharmony_ci .arg2_type = ARG_PTR_TO_UNINIT_MEM, 17178c2ecf20Sopenharmony_ci .arg3_type = ARG_CONST_SIZE, 17188c2ecf20Sopenharmony_ci}; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ciBPF_CALL_3(bpf_sysctl_set_new_value, struct bpf_sysctl_kern *, ctx, 17218c2ecf20Sopenharmony_ci const char *, buf, size_t, buf_len) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci if (!ctx->write || !ctx->new_val || !ctx->new_len || !buf || !buf_len) 17248c2ecf20Sopenharmony_ci return -EINVAL; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (buf_len > PAGE_SIZE - 1) 17278c2ecf20Sopenharmony_ci return -E2BIG; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci memcpy(ctx->new_val, buf, buf_len); 17308c2ecf20Sopenharmony_ci ctx->new_len = buf_len; 17318c2ecf20Sopenharmony_ci ctx->new_updated = 1; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci return 0; 17348c2ecf20Sopenharmony_ci} 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_cistatic const struct bpf_func_proto bpf_sysctl_set_new_value_proto = { 17378c2ecf20Sopenharmony_ci .func = bpf_sysctl_set_new_value, 17388c2ecf20Sopenharmony_ci .gpl_only = false, 17398c2ecf20Sopenharmony_ci .ret_type = RET_INTEGER, 17408c2ecf20Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 17418c2ecf20Sopenharmony_ci .arg2_type = ARG_PTR_TO_MEM | MEM_RDONLY, 17428c2ecf20Sopenharmony_ci .arg3_type = ARG_CONST_SIZE, 17438c2ecf20Sopenharmony_ci}; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic const struct bpf_func_proto * 17468c2ecf20Sopenharmony_cisysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci switch (func_id) { 17498c2ecf20Sopenharmony_ci case BPF_FUNC_strtol: 17508c2ecf20Sopenharmony_ci return &bpf_strtol_proto; 17518c2ecf20Sopenharmony_ci case BPF_FUNC_strtoul: 17528c2ecf20Sopenharmony_ci return &bpf_strtoul_proto; 17538c2ecf20Sopenharmony_ci case BPF_FUNC_sysctl_get_name: 17548c2ecf20Sopenharmony_ci return &bpf_sysctl_get_name_proto; 17558c2ecf20Sopenharmony_ci case BPF_FUNC_sysctl_get_current_value: 17568c2ecf20Sopenharmony_ci return &bpf_sysctl_get_current_value_proto; 17578c2ecf20Sopenharmony_ci case BPF_FUNC_sysctl_get_new_value: 17588c2ecf20Sopenharmony_ci return &bpf_sysctl_get_new_value_proto; 17598c2ecf20Sopenharmony_ci case BPF_FUNC_sysctl_set_new_value: 17608c2ecf20Sopenharmony_ci return &bpf_sysctl_set_new_value_proto; 17618c2ecf20Sopenharmony_ci default: 17628c2ecf20Sopenharmony_ci return cgroup_base_func_proto(func_id, prog); 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic bool sysctl_is_valid_access(int off, int size, enum bpf_access_type type, 17678c2ecf20Sopenharmony_ci const struct bpf_prog *prog, 17688c2ecf20Sopenharmony_ci struct bpf_insn_access_aux *info) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci const int size_default = sizeof(__u32); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (off < 0 || off + size > sizeof(struct bpf_sysctl) || off % size) 17738c2ecf20Sopenharmony_ci return false; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci switch (off) { 17768c2ecf20Sopenharmony_ci case bpf_ctx_range(struct bpf_sysctl, write): 17778c2ecf20Sopenharmony_ci if (type != BPF_READ) 17788c2ecf20Sopenharmony_ci return false; 17798c2ecf20Sopenharmony_ci bpf_ctx_record_field_size(info, size_default); 17808c2ecf20Sopenharmony_ci return bpf_ctx_narrow_access_ok(off, size, size_default); 17818c2ecf20Sopenharmony_ci case bpf_ctx_range(struct bpf_sysctl, file_pos): 17828c2ecf20Sopenharmony_ci if (type == BPF_READ) { 17838c2ecf20Sopenharmony_ci bpf_ctx_record_field_size(info, size_default); 17848c2ecf20Sopenharmony_ci return bpf_ctx_narrow_access_ok(off, size, size_default); 17858c2ecf20Sopenharmony_ci } else { 17868c2ecf20Sopenharmony_ci return size == size_default; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci default: 17898c2ecf20Sopenharmony_ci return false; 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_cistatic u32 sysctl_convert_ctx_access(enum bpf_access_type type, 17948c2ecf20Sopenharmony_ci const struct bpf_insn *si, 17958c2ecf20Sopenharmony_ci struct bpf_insn *insn_buf, 17968c2ecf20Sopenharmony_ci struct bpf_prog *prog, u32 *target_size) 17978c2ecf20Sopenharmony_ci{ 17988c2ecf20Sopenharmony_ci struct bpf_insn *insn = insn_buf; 17998c2ecf20Sopenharmony_ci u32 read_size; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci switch (si->off) { 18028c2ecf20Sopenharmony_ci case offsetof(struct bpf_sysctl, write): 18038c2ecf20Sopenharmony_ci *insn++ = BPF_LDX_MEM( 18048c2ecf20Sopenharmony_ci BPF_SIZE(si->code), si->dst_reg, si->src_reg, 18058c2ecf20Sopenharmony_ci bpf_target_off(struct bpf_sysctl_kern, write, 18068c2ecf20Sopenharmony_ci sizeof_field(struct bpf_sysctl_kern, 18078c2ecf20Sopenharmony_ci write), 18088c2ecf20Sopenharmony_ci target_size)); 18098c2ecf20Sopenharmony_ci break; 18108c2ecf20Sopenharmony_ci case offsetof(struct bpf_sysctl, file_pos): 18118c2ecf20Sopenharmony_ci /* ppos is a pointer so it should be accessed via indirect 18128c2ecf20Sopenharmony_ci * loads and stores. Also for stores additional temporary 18138c2ecf20Sopenharmony_ci * register is used since neither src_reg nor dst_reg can be 18148c2ecf20Sopenharmony_ci * overridden. 18158c2ecf20Sopenharmony_ci */ 18168c2ecf20Sopenharmony_ci if (type == BPF_WRITE) { 18178c2ecf20Sopenharmony_ci int treg = BPF_REG_9; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (si->src_reg == treg || si->dst_reg == treg) 18208c2ecf20Sopenharmony_ci --treg; 18218c2ecf20Sopenharmony_ci if (si->src_reg == treg || si->dst_reg == treg) 18228c2ecf20Sopenharmony_ci --treg; 18238c2ecf20Sopenharmony_ci *insn++ = BPF_STX_MEM( 18248c2ecf20Sopenharmony_ci BPF_DW, si->dst_reg, treg, 18258c2ecf20Sopenharmony_ci offsetof(struct bpf_sysctl_kern, tmp_reg)); 18268c2ecf20Sopenharmony_ci *insn++ = BPF_LDX_MEM( 18278c2ecf20Sopenharmony_ci BPF_FIELD_SIZEOF(struct bpf_sysctl_kern, ppos), 18288c2ecf20Sopenharmony_ci treg, si->dst_reg, 18298c2ecf20Sopenharmony_ci offsetof(struct bpf_sysctl_kern, ppos)); 18308c2ecf20Sopenharmony_ci *insn++ = BPF_STX_MEM( 18318c2ecf20Sopenharmony_ci BPF_SIZEOF(u32), treg, si->src_reg, 18328c2ecf20Sopenharmony_ci bpf_ctx_narrow_access_offset( 18338c2ecf20Sopenharmony_ci 0, sizeof(u32), sizeof(loff_t))); 18348c2ecf20Sopenharmony_ci *insn++ = BPF_LDX_MEM( 18358c2ecf20Sopenharmony_ci BPF_DW, treg, si->dst_reg, 18368c2ecf20Sopenharmony_ci offsetof(struct bpf_sysctl_kern, tmp_reg)); 18378c2ecf20Sopenharmony_ci } else { 18388c2ecf20Sopenharmony_ci *insn++ = BPF_LDX_MEM( 18398c2ecf20Sopenharmony_ci BPF_FIELD_SIZEOF(struct bpf_sysctl_kern, ppos), 18408c2ecf20Sopenharmony_ci si->dst_reg, si->src_reg, 18418c2ecf20Sopenharmony_ci offsetof(struct bpf_sysctl_kern, ppos)); 18428c2ecf20Sopenharmony_ci read_size = bpf_size_to_bytes(BPF_SIZE(si->code)); 18438c2ecf20Sopenharmony_ci *insn++ = BPF_LDX_MEM( 18448c2ecf20Sopenharmony_ci BPF_SIZE(si->code), si->dst_reg, si->dst_reg, 18458c2ecf20Sopenharmony_ci bpf_ctx_narrow_access_offset( 18468c2ecf20Sopenharmony_ci 0, read_size, sizeof(loff_t))); 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci *target_size = sizeof(u32); 18498c2ecf20Sopenharmony_ci break; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci return insn - insn_buf; 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ciconst struct bpf_verifier_ops cg_sysctl_verifier_ops = { 18568c2ecf20Sopenharmony_ci .get_func_proto = sysctl_func_proto, 18578c2ecf20Sopenharmony_ci .is_valid_access = sysctl_is_valid_access, 18588c2ecf20Sopenharmony_ci .convert_ctx_access = sysctl_convert_ctx_access, 18598c2ecf20Sopenharmony_ci}; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ciconst struct bpf_prog_ops cg_sysctl_prog_ops = { 18628c2ecf20Sopenharmony_ci}; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_cistatic const struct bpf_func_proto * 18658c2ecf20Sopenharmony_cicg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci switch (func_id) { 18688c2ecf20Sopenharmony_ci#ifdef CONFIG_NET 18698c2ecf20Sopenharmony_ci case BPF_FUNC_sk_storage_get: 18708c2ecf20Sopenharmony_ci return &bpf_sk_storage_get_proto; 18718c2ecf20Sopenharmony_ci case BPF_FUNC_sk_storage_delete: 18728c2ecf20Sopenharmony_ci return &bpf_sk_storage_delete_proto; 18738c2ecf20Sopenharmony_ci#endif 18748c2ecf20Sopenharmony_ci#ifdef CONFIG_INET 18758c2ecf20Sopenharmony_ci case BPF_FUNC_tcp_sock: 18768c2ecf20Sopenharmony_ci return &bpf_tcp_sock_proto; 18778c2ecf20Sopenharmony_ci#endif 18788c2ecf20Sopenharmony_ci default: 18798c2ecf20Sopenharmony_ci return cgroup_base_func_proto(func_id, prog); 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci} 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cistatic bool cg_sockopt_is_valid_access(int off, int size, 18848c2ecf20Sopenharmony_ci enum bpf_access_type type, 18858c2ecf20Sopenharmony_ci const struct bpf_prog *prog, 18868c2ecf20Sopenharmony_ci struct bpf_insn_access_aux *info) 18878c2ecf20Sopenharmony_ci{ 18888c2ecf20Sopenharmony_ci const int size_default = sizeof(__u32); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (off < 0 || off >= sizeof(struct bpf_sockopt)) 18918c2ecf20Sopenharmony_ci return false; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci if (off % size != 0) 18948c2ecf20Sopenharmony_ci return false; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci if (type == BPF_WRITE) { 18978c2ecf20Sopenharmony_ci switch (off) { 18988c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, retval): 18998c2ecf20Sopenharmony_ci if (size != size_default) 19008c2ecf20Sopenharmony_ci return false; 19018c2ecf20Sopenharmony_ci return prog->expected_attach_type == 19028c2ecf20Sopenharmony_ci BPF_CGROUP_GETSOCKOPT; 19038c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optname): 19048c2ecf20Sopenharmony_ci fallthrough; 19058c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, level): 19068c2ecf20Sopenharmony_ci if (size != size_default) 19078c2ecf20Sopenharmony_ci return false; 19088c2ecf20Sopenharmony_ci return prog->expected_attach_type == 19098c2ecf20Sopenharmony_ci BPF_CGROUP_SETSOCKOPT; 19108c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optlen): 19118c2ecf20Sopenharmony_ci return size == size_default; 19128c2ecf20Sopenharmony_ci default: 19138c2ecf20Sopenharmony_ci return false; 19148c2ecf20Sopenharmony_ci } 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci switch (off) { 19188c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, sk): 19198c2ecf20Sopenharmony_ci if (size != sizeof(__u64)) 19208c2ecf20Sopenharmony_ci return false; 19218c2ecf20Sopenharmony_ci info->reg_type = PTR_TO_SOCKET; 19228c2ecf20Sopenharmony_ci break; 19238c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optval): 19248c2ecf20Sopenharmony_ci if (size != sizeof(__u64)) 19258c2ecf20Sopenharmony_ci return false; 19268c2ecf20Sopenharmony_ci info->reg_type = PTR_TO_PACKET; 19278c2ecf20Sopenharmony_ci break; 19288c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optval_end): 19298c2ecf20Sopenharmony_ci if (size != sizeof(__u64)) 19308c2ecf20Sopenharmony_ci return false; 19318c2ecf20Sopenharmony_ci info->reg_type = PTR_TO_PACKET_END; 19328c2ecf20Sopenharmony_ci break; 19338c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, retval): 19348c2ecf20Sopenharmony_ci if (size != size_default) 19358c2ecf20Sopenharmony_ci return false; 19368c2ecf20Sopenharmony_ci return prog->expected_attach_type == BPF_CGROUP_GETSOCKOPT; 19378c2ecf20Sopenharmony_ci default: 19388c2ecf20Sopenharmony_ci if (size != size_default) 19398c2ecf20Sopenharmony_ci return false; 19408c2ecf20Sopenharmony_ci break; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci return true; 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci#define CG_SOCKOPT_ACCESS_FIELD(T, F) \ 19468c2ecf20Sopenharmony_ci T(BPF_FIELD_SIZEOF(struct bpf_sockopt_kern, F), \ 19478c2ecf20Sopenharmony_ci si->dst_reg, si->src_reg, \ 19488c2ecf20Sopenharmony_ci offsetof(struct bpf_sockopt_kern, F)) 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_cistatic u32 cg_sockopt_convert_ctx_access(enum bpf_access_type type, 19518c2ecf20Sopenharmony_ci const struct bpf_insn *si, 19528c2ecf20Sopenharmony_ci struct bpf_insn *insn_buf, 19538c2ecf20Sopenharmony_ci struct bpf_prog *prog, 19548c2ecf20Sopenharmony_ci u32 *target_size) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci struct bpf_insn *insn = insn_buf; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci switch (si->off) { 19598c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, sk): 19608c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, sk); 19618c2ecf20Sopenharmony_ci break; 19628c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, level): 19638c2ecf20Sopenharmony_ci if (type == BPF_WRITE) 19648c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, level); 19658c2ecf20Sopenharmony_ci else 19668c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, level); 19678c2ecf20Sopenharmony_ci break; 19688c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optname): 19698c2ecf20Sopenharmony_ci if (type == BPF_WRITE) 19708c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, optname); 19718c2ecf20Sopenharmony_ci else 19728c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optname); 19738c2ecf20Sopenharmony_ci break; 19748c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optlen): 19758c2ecf20Sopenharmony_ci if (type == BPF_WRITE) 19768c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, optlen); 19778c2ecf20Sopenharmony_ci else 19788c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optlen); 19798c2ecf20Sopenharmony_ci break; 19808c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, retval): 19818c2ecf20Sopenharmony_ci if (type == BPF_WRITE) 19828c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_STX_MEM, retval); 19838c2ecf20Sopenharmony_ci else 19848c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, retval); 19858c2ecf20Sopenharmony_ci break; 19868c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optval): 19878c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optval); 19888c2ecf20Sopenharmony_ci break; 19898c2ecf20Sopenharmony_ci case offsetof(struct bpf_sockopt, optval_end): 19908c2ecf20Sopenharmony_ci *insn++ = CG_SOCKOPT_ACCESS_FIELD(BPF_LDX_MEM, optval_end); 19918c2ecf20Sopenharmony_ci break; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci return insn - insn_buf; 19958c2ecf20Sopenharmony_ci} 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_cistatic int cg_sockopt_get_prologue(struct bpf_insn *insn_buf, 19988c2ecf20Sopenharmony_ci bool direct_write, 19998c2ecf20Sopenharmony_ci const struct bpf_prog *prog) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci /* Nothing to do for sockopt argument. The data is kzalloc'ated. 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_ci return 0; 20048c2ecf20Sopenharmony_ci} 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ciconst struct bpf_verifier_ops cg_sockopt_verifier_ops = { 20078c2ecf20Sopenharmony_ci .get_func_proto = cg_sockopt_func_proto, 20088c2ecf20Sopenharmony_ci .is_valid_access = cg_sockopt_is_valid_access, 20098c2ecf20Sopenharmony_ci .convert_ctx_access = cg_sockopt_convert_ctx_access, 20108c2ecf20Sopenharmony_ci .gen_prologue = cg_sockopt_get_prologue, 20118c2ecf20Sopenharmony_ci}; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ciconst struct bpf_prog_ops cg_sockopt_prog_ops = { 20148c2ecf20Sopenharmony_ci}; 2015