mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 17:57:38 -04:00
Merge tag 'net-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from Paolo Abeni:
"The most interesting thing is probably the networking structs
reorganization and a significant amount of changes is around
self-tests.
Core & protocols:
- Analyze and reorganize core networking structs (socks, netdev,
netns, mibs) to optimize cacheline consumption and set up build
time warnings to safeguard against future header changes
This improves TCP performances with many concurrent connections up
to 40%
- Add page-pool netlink-based introspection, exposing the memory
usage and recycling stats. This helps indentify bad PP users and
possible leaks
- Refine TCP/DCCP source port selection to no longer favor even
source port at connect() time when IP_LOCAL_PORT_RANGE is set. This
lowers the time taken by connect() for hosts having many active
connections to the same destination
- Refactor the TCP bind conflict code, shrinking related socket
structs
- Refactor TCP SYN-Cookie handling, as a preparation step to allow
arbitrary SYN-Cookie processing via eBPF
- Tune optmem_max for 0-copy usage, increasing the default value to
128KB and namespecifying it
- Allow coalescing for cloned skbs coming from page pools, improving
RX performances with some common configurations
- Reduce extension header parsing overhead at GRO time
- Add bridge MDB bulk deletion support, allowing user-space to
request the deletion of matching entries
- Reorder nftables struct members, to keep data accessed by the
datapath first
- Introduce TC block ports tracking and use. This allows supporting
multicast-like behavior at the TC layer
- Remove UAPI support for retired TC qdiscs (dsmark, CBQ and ATM) and
classifiers (RSVP and tcindex)
- More data-race annotations
- Extend the diag interface to dump TCP bound-only sockets
- Conditional notification of events for TC qdisc class and actions
- Support for WPAN dynamic associations with nearby devices, to form
a sub-network using a specific PAN ID
- Implement SMCv2.1 virtual ISM device support
- Add support for Batman-avd mulicast packet type
BPF:
- Tons of verifier improvements:
- BPF register bounds logic and range support along with a large
test suite
- log improvements
- complete precision tracking support for register spills
- track aligned STACK_ZERO cases as imprecise spilled registers.
This improves the verifier "instructions processed" metric from
single digit to 50-60% for some programs
- support for user's global BPF subprogram arguments with few
commonly requested annotations for a better developer
experience
- support tracking of BPF_JNE which helps cases when the compiler
transforms (unsigned) "a > 0" into "if a == 0 goto xxx" and the
like
- several fixes
- Add initial TX metadata implementation for AF_XDP with support in
mlx5 and stmmac drivers. Two types of offloads are supported right
now, that is, TX timestamp and TX checksum offload
- Fix kCFI bugs in BPF all forms of indirect calls from BPF into
kernel and from kernel into BPF work with CFI enabled. This allows
BPF to work with CONFIG_FINEIBT=y
- Change BPF verifier logic to validate global subprograms lazily
instead of unconditionally before the main program, so they can be
guarded using BPF CO-RE techniques
- Support uid/gid options when mounting bpffs
- Add a new kfunc which acquires the associated cgroup of a task
within a specific cgroup v1 hierarchy where the latter is
identified by its id
- Extend verifier to allow bpf_refcount_acquire() of a map value
field obtained via direct load which is a use-case needed in
sched_ext
- Add BPF link_info support for uprobe multi link along with bpftool
integration for the latter
- Support for VLAN tag in XDP hints
- Remove deprecated bpfilter kernel leftovers given the project is
developed in user-space (https://github.com/facebook/bpfilter)
Misc:
- Support for parellel TC self-tests execution
- Increase MPTCP self-tests coverage
- Updated the bridge documentation, including several so-far
undocumented features
- Convert all the net self-tests to run in unique netns, to avoid
random failures due to conflict and allow concurrent runs
- Add TCP-AO self-tests
- Add kunit tests for both cfg80211 and mac80211
- Autogenerate Netlink families documentation from YAML spec
- Add yml-gen support for fixed headers and recursive nests, the tool
can now generate user-space code for all genetlink families for
which we have specs
- A bunch of additional module descriptions fixes
- Catch incorrect freeing of pages belonging to a page pool
Driver API:
- Rust abstractions for network PHY drivers; do not cover yet the
full C API, but already allow implementing functional PHY drivers
in rust
- Introduce queue and NAPI support in the netdev Netlink interface,
allowing complete access to the device <> NAPIs <> queues
relationship
- Introduce notifications filtering for devlink to allow control
application scale to thousands of instances
- Improve PHY validation, requesting rate matching information for
each ethtool link mode supported by both the PHY and host
- Add support for ethtool symmetric-xor RSS hash
- ACPI based Wifi band RFI (WBRF) mitigation feature for the AMD
platform
- Expose pin fractional frequency offset value over new DPLL generic
netlink attribute
- Convert older drivers to platform remove callback returning void
- Add support for PHY package MMD read/write
New hardware / drivers:
- Ethernet:
- Octeon CN10K devices
- Broadcom 5760X P7
- Qualcomm SM8550 SoC
- Texas Instrument DP83TG720S PHY
- Bluetooth:
- IMC Networks Bluetooth radio
Removed:
- WiFi:
- libertas 16-bit PCMCIA support
- Atmel at76c50x drivers
- HostAP ISA/PCMCIA style 802.11b driver
- zd1201 802.11b USB dongles
- Orinoco ISA/PCMCIA 802.11b driver
- Aviator/Raytheon driver
- Planet WL3501 driver
- RNDIS USB 802.11b driver
Driver updates:
- Ethernet high-speed NICs:
- Intel (100G, ice, idpf):
- allow one by one port representors creation and removal
- add temperature and clock information reporting
- add get/set for ethtool's header split ringparam
- add again FW logging
- adds support switchdev hardware packet mirroring
- iavf: implement symmetric-xor RSS hash
- igc: add support for concurrent physical and free-running
timers
- i40e: increase the allowable descriptors
- nVidia/Mellanox:
- Preparation for Socket-Direct multi-dev netdev. That will
allow in future releases combining multiple PFs devices
attached to different NUMA nodes under the same netdev
- Broadcom (bnxt):
- TX completion handling improvements
- add basic ntuple filter support
- reduce MSIX vectors usage for MQPRIO offload
- add VXLAN support, USO offload and TX coalesce completion
for P7
- Marvell Octeon EP:
- xmit-more support
- add PF-VF mailbox support and use it for FW notifications
for VFs
- Wangxun (ngbe/txgbe):
- implement ethtool functions to operate pause param, ring
param, coalesce channel number and msglevel
- Netronome/Corigine (nfp):
- add flow-steering support
- support UDP segmentation offload
- Ethernet NICs embedded, slower, virtual:
- Xilinx AXI: remove duplicate DMA code adopting the dma engine
driver
- stmmac: add support for HW-accelerated VLAN stripping
- TI AM654x sw: add mqprio, frame preemption & coalescing
- gve: add support for non-4k page sizes.
- virtio-net: support dynamic coalescing moderation
- nVidia/Mellanox Ethernet datacenter switches:
- allow firmware upgrade without a reboot
- more flexible support for bridge flooding via the compressed
FID flooding mode
- Ethernet embedded switches:
- Microchip:
- fine-tune flow control and speed configurations in KSZ8xxx
- KSZ88X3: enable setting rmii reference
- Renesas:
- add jumbo frames support
- Marvell:
- 88E6xxx: add "eth-mac" and "rmon" stats support
- Ethernet PHYs:
- aquantia: add firmware load support
- at803x: refactor the driver to simplify adding support for more
chip variants
- NXP C45 TJA11xx: Add MACsec offload support
- Wifi:
- MediaTek (mt76):
- NVMEM EEPROM improvements
- mt7996 Extremely High Throughput (EHT) improvements
- mt7996 Wireless Ethernet Dispatcher (WED) support
- mt7996 36-bit DMA support
- Qualcomm (ath12k):
- support for a single MSI vector
- WCN7850: support AP mode
- Intel (iwlwifi):
- new debugfs file fw_dbg_clear
- allow concurrent P2P operation on DFS channels
- Bluetooth:
- QCA2066: support HFP offload
- ISO: more broadcast-related improvements
- NXP: better recovery in case receiver/transmitter get out of sync"
* tag 'net-next-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1714 commits)
lan78xx: remove redundant statement in lan78xx_get_eee
lan743x: remove redundant statement in lan743x_ethtool_get_eee
bnxt_en: Fix RCU locking for ntuple filters in bnxt_rx_flow_steer()
bnxt_en: Fix RCU locking for ntuple filters in bnxt_srxclsrldel()
bnxt_en: Remove unneeded variable in bnxt_hwrm_clear_vnic_filter()
tcp: Revert no longer abort SYN_SENT when receiving some ICMP
Revert "mlx5 updates 2023-12-20"
Revert "net: stmmac: Enable Per DMA Channel interrupt"
ipvlan: Remove usage of the deprecated ida_simple_xx() API
ipvlan: Fix a typo in a comment
net/sched: Remove ipt action tests
net: stmmac: Use interrupt mode INTM=1 for per channel irq
net: stmmac: Add support for TX/RX channel interrupt
net: stmmac: Make MSI interrupt routine generic
dt-bindings: net: snps,dwmac: per channel irq
net: phy: at803x: make read_status more generic
net: phy: at803x: add support for cdt cross short test for qca808x
net: phy: at803x: refactor qca808x cable test get status function
net: phy: at803x: generalize cdt fault length function
net: ethernet: cortina: Drop TSO support
...
This commit is contained in:
@@ -867,11 +867,11 @@ int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,
|
||||
}
|
||||
|
||||
if (old_ptr)
|
||||
map->ops->map_fd_put_ptr(old_ptr);
|
||||
map->ops->map_fd_put_ptr(map, old_ptr, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long fd_array_map_delete_elem(struct bpf_map *map, void *key)
|
||||
static long __fd_array_map_delete_elem(struct bpf_map *map, void *key, bool need_defer)
|
||||
{
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
void *old_ptr;
|
||||
@@ -890,13 +890,18 @@ static long fd_array_map_delete_elem(struct bpf_map *map, void *key)
|
||||
}
|
||||
|
||||
if (old_ptr) {
|
||||
map->ops->map_fd_put_ptr(old_ptr);
|
||||
map->ops->map_fd_put_ptr(map, old_ptr, need_defer);
|
||||
return 0;
|
||||
} else {
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
static long fd_array_map_delete_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
return __fd_array_map_delete_elem(map, key, true);
|
||||
}
|
||||
|
||||
static void *prog_fd_array_get_ptr(struct bpf_map *map,
|
||||
struct file *map_file, int fd)
|
||||
{
|
||||
@@ -913,8 +918,9 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map,
|
||||
return prog;
|
||||
}
|
||||
|
||||
static void prog_fd_array_put_ptr(void *ptr)
|
||||
static void prog_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
|
||||
{
|
||||
/* bpf_prog is freed after one RCU or tasks trace grace period */
|
||||
bpf_prog_put(ptr);
|
||||
}
|
||||
|
||||
@@ -924,13 +930,13 @@ static u32 prog_fd_array_sys_lookup_elem(void *ptr)
|
||||
}
|
||||
|
||||
/* decrement refcnt of all bpf_progs that are stored in this map */
|
||||
static void bpf_fd_array_map_clear(struct bpf_map *map)
|
||||
static void bpf_fd_array_map_clear(struct bpf_map *map, bool need_defer)
|
||||
{
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < array->map.max_entries; i++)
|
||||
fd_array_map_delete_elem(map, &i);
|
||||
__fd_array_map_delete_elem(map, &i, need_defer);
|
||||
}
|
||||
|
||||
static void prog_array_map_seq_show_elem(struct bpf_map *map, void *key,
|
||||
@@ -1071,7 +1077,7 @@ static void prog_array_map_clear_deferred(struct work_struct *work)
|
||||
{
|
||||
struct bpf_map *map = container_of(work, struct bpf_array_aux,
|
||||
work)->map;
|
||||
bpf_fd_array_map_clear(map);
|
||||
bpf_fd_array_map_clear(map, true);
|
||||
bpf_map_put(map);
|
||||
}
|
||||
|
||||
@@ -1151,7 +1157,7 @@ static struct bpf_event_entry *bpf_event_entry_gen(struct file *perf_file,
|
||||
{
|
||||
struct bpf_event_entry *ee;
|
||||
|
||||
ee = kzalloc(sizeof(*ee), GFP_ATOMIC);
|
||||
ee = kzalloc(sizeof(*ee), GFP_KERNEL);
|
||||
if (ee) {
|
||||
ee->event = perf_file->private_data;
|
||||
ee->perf_file = perf_file;
|
||||
@@ -1201,8 +1207,9 @@ static void *perf_event_fd_array_get_ptr(struct bpf_map *map,
|
||||
return ee;
|
||||
}
|
||||
|
||||
static void perf_event_fd_array_put_ptr(void *ptr)
|
||||
static void perf_event_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
|
||||
{
|
||||
/* bpf_perf_event is freed after one RCU grace period */
|
||||
bpf_event_entry_free_rcu(ptr);
|
||||
}
|
||||
|
||||
@@ -1220,7 +1227,7 @@ static void perf_event_fd_array_release(struct bpf_map *map,
|
||||
for (i = 0; i < array->map.max_entries; i++) {
|
||||
ee = READ_ONCE(array->ptrs[i]);
|
||||
if (ee && ee->map_file == map_file)
|
||||
fd_array_map_delete_elem(map, &i);
|
||||
__fd_array_map_delete_elem(map, &i, true);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@@ -1228,7 +1235,7 @@ static void perf_event_fd_array_release(struct bpf_map *map,
|
||||
static void perf_event_fd_array_map_free(struct bpf_map *map)
|
||||
{
|
||||
if (map->map_flags & BPF_F_PRESERVE_ELEMS)
|
||||
bpf_fd_array_map_clear(map);
|
||||
bpf_fd_array_map_clear(map, false);
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
@@ -1256,7 +1263,7 @@ static void *cgroup_fd_array_get_ptr(struct bpf_map *map,
|
||||
return cgroup_get_from_fd(fd);
|
||||
}
|
||||
|
||||
static void cgroup_fd_array_put_ptr(void *ptr)
|
||||
static void cgroup_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
|
||||
{
|
||||
/* cgroup_put free cgrp after a rcu grace period */
|
||||
cgroup_put(ptr);
|
||||
@@ -1264,7 +1271,7 @@ static void cgroup_fd_array_put_ptr(void *ptr)
|
||||
|
||||
static void cgroup_fd_array_free(struct bpf_map *map)
|
||||
{
|
||||
bpf_fd_array_map_clear(map);
|
||||
bpf_fd_array_map_clear(map, false);
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
@@ -1309,7 +1316,7 @@ static void array_of_map_free(struct bpf_map *map)
|
||||
* is protected by fdget/fdput.
|
||||
*/
|
||||
bpf_map_meta_free(map->inner_map_meta);
|
||||
bpf_fd_array_map_clear(map);
|
||||
bpf_fd_array_map_clear(map, false);
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ static void *bpf_cgrp_storage_lookup_elem(struct bpf_map *map, void *key)
|
||||
int fd;
|
||||
|
||||
fd = *(int *)key;
|
||||
cgroup = cgroup_get_from_fd(fd);
|
||||
cgroup = cgroup_v1v2_get_from_fd(fd);
|
||||
if (IS_ERR(cgroup))
|
||||
return ERR_CAST(cgroup);
|
||||
|
||||
@@ -101,7 +101,7 @@ static long bpf_cgrp_storage_update_elem(struct bpf_map *map, void *key,
|
||||
int fd;
|
||||
|
||||
fd = *(int *)key;
|
||||
cgroup = cgroup_get_from_fd(fd);
|
||||
cgroup = cgroup_v1v2_get_from_fd(fd);
|
||||
if (IS_ERR(cgroup))
|
||||
return PTR_ERR(cgroup);
|
||||
|
||||
@@ -131,7 +131,7 @@ static long bpf_cgrp_storage_delete_elem(struct bpf_map *map, void *key)
|
||||
int err, fd;
|
||||
|
||||
fd = *(int *)key;
|
||||
cgroup = cgroup_get_from_fd(fd);
|
||||
cgroup = cgroup_v1v2_get_from_fd(fd);
|
||||
if (IS_ERR(cgroup))
|
||||
return PTR_ERR(cgroup);
|
||||
|
||||
|
||||
@@ -298,6 +298,18 @@ BTF_ID(func, bpf_lsm_kernel_module_request)
|
||||
BTF_ID(func, bpf_lsm_kernel_read_file)
|
||||
BTF_ID(func, bpf_lsm_kernfs_init_security)
|
||||
|
||||
#ifdef CONFIG_SECURITY_PATH
|
||||
BTF_ID(func, bpf_lsm_path_unlink)
|
||||
BTF_ID(func, bpf_lsm_path_mkdir)
|
||||
BTF_ID(func, bpf_lsm_path_rmdir)
|
||||
BTF_ID(func, bpf_lsm_path_truncate)
|
||||
BTF_ID(func, bpf_lsm_path_symlink)
|
||||
BTF_ID(func, bpf_lsm_path_link)
|
||||
BTF_ID(func, bpf_lsm_path_rename)
|
||||
BTF_ID(func, bpf_lsm_path_chmod)
|
||||
BTF_ID(func, bpf_lsm_path_chown)
|
||||
#endif /* CONFIG_SECURITY_PATH */
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
BTF_ID(func, bpf_lsm_key_free)
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
@@ -352,18 +352,24 @@ const struct bpf_link_ops bpf_struct_ops_link_lops = {
|
||||
int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks,
|
||||
struct bpf_tramp_link *link,
|
||||
const struct btf_func_model *model,
|
||||
void *image, void *image_end)
|
||||
void *stub_func, void *image, void *image_end)
|
||||
{
|
||||
u32 flags;
|
||||
u32 flags = BPF_TRAMP_F_INDIRECT;
|
||||
int size;
|
||||
|
||||
tlinks[BPF_TRAMP_FENTRY].links[0] = link;
|
||||
tlinks[BPF_TRAMP_FENTRY].nr_links = 1;
|
||||
/* BPF_TRAMP_F_RET_FENTRY_RET is only used by bpf_struct_ops,
|
||||
* and it must be used alone.
|
||||
*/
|
||||
flags = model->ret_size > 0 ? BPF_TRAMP_F_RET_FENTRY_RET : 0;
|
||||
|
||||
if (model->ret_size > 0)
|
||||
flags |= BPF_TRAMP_F_RET_FENTRY_RET;
|
||||
|
||||
size = arch_bpf_trampoline_size(model, flags, tlinks, NULL);
|
||||
if (size < 0)
|
||||
return size;
|
||||
if (size > (unsigned long)image_end - (unsigned long)image)
|
||||
return -E2BIG;
|
||||
return arch_prepare_bpf_trampoline(NULL, image, image_end,
|
||||
model, flags, tlinks, NULL);
|
||||
model, flags, tlinks, stub_func);
|
||||
}
|
||||
|
||||
static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
@@ -497,11 +503,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
|
||||
err = bpf_struct_ops_prepare_trampoline(tlinks, link,
|
||||
&st_ops->func_models[i],
|
||||
*(void **)(st_ops->cfi_stubs + moff),
|
||||
image, image_end);
|
||||
if (err < 0)
|
||||
goto reset_unlock;
|
||||
|
||||
*(void **)(kdata + moff) = image;
|
||||
*(void **)(kdata + moff) = image + cfi_get_offset();
|
||||
image += err;
|
||||
|
||||
/* put prog_id to udata */
|
||||
@@ -515,7 +522,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
if (err)
|
||||
goto reset_unlock;
|
||||
}
|
||||
set_memory_rox((long)st_map->image, 1);
|
||||
arch_protect_bpf_trampoline(st_map->image, PAGE_SIZE);
|
||||
/* Let bpf_link handle registration & unregistration.
|
||||
*
|
||||
* Pair with smp_load_acquire() during lookup_elem().
|
||||
@@ -524,7 +531,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
set_memory_rox((long)st_map->image, 1);
|
||||
arch_protect_bpf_trampoline(st_map->image, PAGE_SIZE);
|
||||
err = st_ops->reg(kdata);
|
||||
if (likely(!err)) {
|
||||
/* This refcnt increment on the map here after
|
||||
@@ -547,8 +554,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
* there was a race in registering the struct_ops (under the same name) to
|
||||
* a sub-system through different struct_ops's maps.
|
||||
*/
|
||||
set_memory_nx((long)st_map->image, 1);
|
||||
set_memory_rw((long)st_map->image, 1);
|
||||
arch_unprotect_bpf_trampoline(st_map->image, PAGE_SIZE);
|
||||
|
||||
reset_unlock:
|
||||
bpf_struct_ops_map_put_progs(st_map);
|
||||
@@ -616,7 +622,7 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map)
|
||||
bpf_struct_ops_map_put_progs(st_map);
|
||||
bpf_map_area_free(st_map->links);
|
||||
if (st_map->image) {
|
||||
bpf_jit_free_exec(st_map->image);
|
||||
arch_free_bpf_trampoline(st_map->image, PAGE_SIZE);
|
||||
bpf_jit_uncharge_modmem(PAGE_SIZE);
|
||||
}
|
||||
bpf_map_area_free(st_map->uvalue);
|
||||
@@ -691,7 +697,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
st_map->image = bpf_jit_alloc_exec(PAGE_SIZE);
|
||||
st_map->image = arch_alloc_bpf_trampoline(PAGE_SIZE);
|
||||
if (!st_map->image) {
|
||||
/* __bpf_struct_ops_map_free() uses st_map->image as flag
|
||||
* for "charged or not". In this case, we need to unchange
|
||||
@@ -711,7 +717,6 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
|
||||
}
|
||||
|
||||
mutex_init(&st_map->lock);
|
||||
set_vm_flush_reset_perms(st_map->image);
|
||||
bpf_map_init_from_attr(map, attr);
|
||||
|
||||
return map;
|
||||
|
||||
302
kernel/bpf/btf.c
302
kernel/bpf/btf.c
@@ -3840,9 +3840,6 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
#define GRAPH_ROOT_MASK (BPF_LIST_HEAD | BPF_RB_ROOT)
|
||||
#define GRAPH_NODE_MASK (BPF_LIST_NODE | BPF_RB_NODE)
|
||||
|
||||
int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
|
||||
{
|
||||
int i;
|
||||
@@ -3855,13 +3852,13 @@ int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
|
||||
* Hence we only need to ensure that bpf_{list_head,rb_root} ownership
|
||||
* does not form cycles.
|
||||
*/
|
||||
if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & GRAPH_ROOT_MASK))
|
||||
if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & BPF_GRAPH_ROOT))
|
||||
return 0;
|
||||
for (i = 0; i < rec->cnt; i++) {
|
||||
struct btf_struct_meta *meta;
|
||||
u32 btf_id;
|
||||
|
||||
if (!(rec->fields[i].type & GRAPH_ROOT_MASK))
|
||||
if (!(rec->fields[i].type & BPF_GRAPH_ROOT))
|
||||
continue;
|
||||
btf_id = rec->fields[i].graph_root.value_btf_id;
|
||||
meta = btf_find_struct_meta(btf, btf_id);
|
||||
@@ -3873,7 +3870,7 @@ int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
|
||||
* to check ownership cycle for a type unless it's also a
|
||||
* node type.
|
||||
*/
|
||||
if (!(rec->field_mask & GRAPH_NODE_MASK))
|
||||
if (!(rec->field_mask & BPF_GRAPH_NODE))
|
||||
continue;
|
||||
|
||||
/* We need to ensure ownership acyclicity among all types. The
|
||||
@@ -3909,7 +3906,7 @@ int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec)
|
||||
* - A is both an root and node.
|
||||
* - B is only an node.
|
||||
*/
|
||||
if (meta->record->field_mask & GRAPH_ROOT_MASK)
|
||||
if (meta->record->field_mask & BPF_GRAPH_ROOT)
|
||||
return -ELOOP;
|
||||
}
|
||||
return 0;
|
||||
@@ -6768,222 +6765,64 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr
|
||||
return btf_check_func_type_match(log, btf1, t1, btf2, t2);
|
||||
}
|
||||
|
||||
static int btf_check_func_arg_match(struct bpf_verifier_env *env,
|
||||
const struct btf *btf, u32 func_id,
|
||||
struct bpf_reg_state *regs,
|
||||
bool ptr_to_mem_ok,
|
||||
bool processing_call)
|
||||
static bool btf_is_dynptr_ptr(const struct btf *btf, const struct btf_type *t)
|
||||
{
|
||||
enum bpf_prog_type prog_type = resolve_prog_type(env->prog);
|
||||
struct bpf_verifier_log *log = &env->log;
|
||||
const char *func_name, *ref_tname;
|
||||
const struct btf_type *t, *ref_t;
|
||||
const struct btf_param *args;
|
||||
u32 i, nargs, ref_id;
|
||||
int ret;
|
||||
const char *name;
|
||||
|
||||
t = btf_type_by_id(btf, func_id);
|
||||
if (!t || !btf_type_is_func(t)) {
|
||||
/* These checks were already done by the verifier while loading
|
||||
* struct bpf_func_info or in add_kfunc_call().
|
||||
*/
|
||||
bpf_log(log, "BTF of func_id %u doesn't point to KIND_FUNC\n",
|
||||
func_id);
|
||||
return -EFAULT;
|
||||
}
|
||||
func_name = btf_name_by_offset(btf, t->name_off);
|
||||
t = btf_type_by_id(btf, t->type); /* skip PTR */
|
||||
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
if (!t || !btf_type_is_func_proto(t)) {
|
||||
bpf_log(log, "Invalid BTF of func %s\n", func_name);
|
||||
return -EFAULT;
|
||||
}
|
||||
args = (const struct btf_param *)(t + 1);
|
||||
nargs = btf_type_vlen(t);
|
||||
if (nargs > MAX_BPF_FUNC_REG_ARGS) {
|
||||
bpf_log(log, "Function %s has %d > %d args\n", func_name, nargs,
|
||||
MAX_BPF_FUNC_REG_ARGS);
|
||||
return -EINVAL;
|
||||
while (btf_type_is_modifier(t))
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
|
||||
/* allow either struct or struct forward declaration */
|
||||
if (btf_type_is_struct(t) ||
|
||||
(btf_type_is_fwd(t) && btf_type_kflag(t) == 0)) {
|
||||
name = btf_str_by_offset(btf, t->name_off);
|
||||
return name && strcmp(name, "bpf_dynptr") == 0;
|
||||
}
|
||||
|
||||
/* check that BTF function arguments match actual types that the
|
||||
* verifier sees.
|
||||
*/
|
||||
for (i = 0; i < nargs; i++) {
|
||||
enum bpf_arg_type arg_type = ARG_DONTCARE;
|
||||
u32 regno = i + 1;
|
||||
struct bpf_reg_state *reg = ®s[regno];
|
||||
|
||||
t = btf_type_skip_modifiers(btf, args[i].type, NULL);
|
||||
if (btf_type_is_scalar(t)) {
|
||||
if (reg->type == SCALAR_VALUE)
|
||||
continue;
|
||||
bpf_log(log, "R%d is not a scalar\n", regno);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!btf_type_is_ptr(t)) {
|
||||
bpf_log(log, "Unrecognized arg#%d type %s\n",
|
||||
i, btf_type_str(t));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
|
||||
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
|
||||
|
||||
ret = check_func_arg_reg_off(env, reg, regno, arg_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
|
||||
/* If function expects ctx type in BTF check that caller
|
||||
* is passing PTR_TO_CTX.
|
||||
*/
|
||||
if (reg->type != PTR_TO_CTX) {
|
||||
bpf_log(log,
|
||||
"arg#%d expected pointer to ctx, but got %s\n",
|
||||
i, btf_type_str(t));
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (ptr_to_mem_ok && processing_call) {
|
||||
const struct btf_type *resolve_ret;
|
||||
u32 type_size;
|
||||
|
||||
resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
|
||||
if (IS_ERR(resolve_ret)) {
|
||||
bpf_log(log,
|
||||
"arg#%d reference type('%s %s') size cannot be determined: %ld\n",
|
||||
i, btf_type_str(ref_t), ref_tname,
|
||||
PTR_ERR(resolve_ret));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (check_mem_reg(env, reg, regno, type_size))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
bpf_log(log, "reg type unsupported for arg#%d function %s#%d\n", i,
|
||||
func_name, func_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Compare BTF of a function declaration with given bpf_reg_state.
|
||||
* Returns:
|
||||
* EFAULT - there is a verifier bug. Abort verification.
|
||||
* EINVAL - there is a type mismatch or BTF is not available.
|
||||
* 0 - BTF matches with what bpf_reg_state expects.
|
||||
* Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
|
||||
*/
|
||||
int btf_check_subprog_arg_match(struct bpf_verifier_env *env, int subprog,
|
||||
struct bpf_reg_state *regs)
|
||||
{
|
||||
struct bpf_prog *prog = env->prog;
|
||||
struct btf *btf = prog->aux->btf;
|
||||
bool is_global;
|
||||
u32 btf_id;
|
||||
int err;
|
||||
|
||||
if (!prog->aux->func_info)
|
||||
return -EINVAL;
|
||||
|
||||
btf_id = prog->aux->func_info[subprog].type_id;
|
||||
if (!btf_id)
|
||||
return -EFAULT;
|
||||
|
||||
if (prog->aux->func_info_aux[subprog].unreliable)
|
||||
return -EINVAL;
|
||||
|
||||
is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
|
||||
err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, false);
|
||||
|
||||
/* Compiler optimizations can remove arguments from static functions
|
||||
* or mismatched type can be passed into a global function.
|
||||
* In such cases mark the function as unreliable from BTF point of view.
|
||||
*/
|
||||
if (err)
|
||||
prog->aux->func_info_aux[subprog].unreliable = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Compare BTF of a function call with given bpf_reg_state.
|
||||
* Returns:
|
||||
* EFAULT - there is a verifier bug. Abort verification.
|
||||
* EINVAL - there is a type mismatch or BTF is not available.
|
||||
* 0 - BTF matches with what bpf_reg_state expects.
|
||||
* Only PTR_TO_CTX and SCALAR_VALUE states are recognized.
|
||||
*
|
||||
* NOTE: the code is duplicated from btf_check_subprog_arg_match()
|
||||
* because btf_check_func_arg_match() is still doing both. Once that
|
||||
* function is split in 2, we can call from here btf_check_subprog_arg_match()
|
||||
* first, and then treat the calling part in a new code path.
|
||||
*/
|
||||
int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
|
||||
struct bpf_reg_state *regs)
|
||||
{
|
||||
struct bpf_prog *prog = env->prog;
|
||||
struct btf *btf = prog->aux->btf;
|
||||
bool is_global;
|
||||
u32 btf_id;
|
||||
int err;
|
||||
|
||||
if (!prog->aux->func_info)
|
||||
return -EINVAL;
|
||||
|
||||
btf_id = prog->aux->func_info[subprog].type_id;
|
||||
if (!btf_id)
|
||||
return -EFAULT;
|
||||
|
||||
if (prog->aux->func_info_aux[subprog].unreliable)
|
||||
return -EINVAL;
|
||||
|
||||
is_global = prog->aux->func_info_aux[subprog].linkage == BTF_FUNC_GLOBAL;
|
||||
err = btf_check_func_arg_match(env, btf, btf_id, regs, is_global, true);
|
||||
|
||||
/* Compiler optimizations can remove arguments from static functions
|
||||
* or mismatched type can be passed into a global function.
|
||||
* In such cases mark the function as unreliable from BTF point of view.
|
||||
*/
|
||||
if (err)
|
||||
prog->aux->func_info_aux[subprog].unreliable = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Convert BTF of a function into bpf_reg_state if possible
|
||||
/* Process BTF of a function to produce high-level expectation of function
|
||||
* arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information
|
||||
* is cached in subprog info for reuse.
|
||||
* Returns:
|
||||
* EFAULT - there is a verifier bug. Abort verification.
|
||||
* EINVAL - cannot convert BTF.
|
||||
* 0 - Successfully converted BTF into bpf_reg_state
|
||||
* (either PTR_TO_CTX or SCALAR_VALUE).
|
||||
* 0 - Successfully processed BTF and constructed argument expectations.
|
||||
*/
|
||||
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
|
||||
struct bpf_reg_state *regs, bool is_ex_cb)
|
||||
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
|
||||
{
|
||||
bool is_global = subprog_aux(env, subprog)->linkage == BTF_FUNC_GLOBAL;
|
||||
struct bpf_subprog_info *sub = subprog_info(env, subprog);
|
||||
struct bpf_verifier_log *log = &env->log;
|
||||
struct bpf_prog *prog = env->prog;
|
||||
enum bpf_prog_type prog_type = prog->type;
|
||||
struct btf *btf = prog->aux->btf;
|
||||
const struct btf_param *args;
|
||||
const struct btf_type *t, *ref_t;
|
||||
const struct btf_type *t, *ref_t, *fn_t;
|
||||
u32 i, nargs, btf_id;
|
||||
const char *tname;
|
||||
|
||||
if (!prog->aux->func_info ||
|
||||
prog->aux->func_info_aux[subprog].linkage != BTF_FUNC_GLOBAL) {
|
||||
if (sub->args_cached)
|
||||
return 0;
|
||||
|
||||
if (!prog->aux->func_info) {
|
||||
bpf_log(log, "Verifier bug\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
btf_id = prog->aux->func_info[subprog].type_id;
|
||||
if (!btf_id) {
|
||||
if (!is_global) /* not fatal for static funcs */
|
||||
return -EINVAL;
|
||||
bpf_log(log, "Global functions need valid BTF\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
t = btf_type_by_id(btf, btf_id);
|
||||
if (!t || !btf_type_is_func(t)) {
|
||||
fn_t = btf_type_by_id(btf, btf_id);
|
||||
if (!fn_t || !btf_type_is_func(fn_t)) {
|
||||
/* These checks were already done by the verifier while loading
|
||||
* struct bpf_func_info
|
||||
*/
|
||||
@@ -6991,11 +6830,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
|
||||
subprog);
|
||||
return -EFAULT;
|
||||
}
|
||||
tname = btf_name_by_offset(btf, t->name_off);
|
||||
|
||||
if (log->level & BPF_LOG_LEVEL)
|
||||
bpf_log(log, "Validating %s() func#%d...\n",
|
||||
tname, subprog);
|
||||
tname = btf_name_by_offset(btf, fn_t->name_off);
|
||||
|
||||
if (prog->aux->func_info_aux[subprog].unreliable) {
|
||||
bpf_log(log, "Verifier bug in function %s()\n", tname);
|
||||
@@ -7004,7 +6839,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
|
||||
if (prog_type == BPF_PROG_TYPE_EXT)
|
||||
prog_type = prog->aux->dst_prog->type;
|
||||
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
t = btf_type_by_id(btf, fn_t->type);
|
||||
if (!t || !btf_type_is_func_proto(t)) {
|
||||
bpf_log(log, "Invalid type of function %s()\n", tname);
|
||||
return -EFAULT;
|
||||
@@ -7030,24 +6865,54 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
|
||||
* Only PTR_TO_CTX and SCALAR are supported atm.
|
||||
*/
|
||||
for (i = 0; i < nargs; i++) {
|
||||
struct bpf_reg_state *reg = ®s[i + 1];
|
||||
bool is_nonnull = false;
|
||||
const char *tag;
|
||||
|
||||
t = btf_type_by_id(btf, args[i].type);
|
||||
|
||||
tag = btf_find_decl_tag_value(btf, fn_t, i, "arg:");
|
||||
if (IS_ERR(tag) && PTR_ERR(tag) == -ENOENT) {
|
||||
tag = NULL;
|
||||
} else if (IS_ERR(tag)) {
|
||||
bpf_log(log, "arg#%d type's tag fetching failure: %ld\n", i, PTR_ERR(tag));
|
||||
return PTR_ERR(tag);
|
||||
}
|
||||
/* 'arg:<tag>' decl_tag takes precedence over derivation of
|
||||
* register type from BTF type itself
|
||||
*/
|
||||
if (tag) {
|
||||
/* disallow arg tags in static subprogs */
|
||||
if (!is_global) {
|
||||
bpf_log(log, "arg#%d type tag is not supported in static functions\n", i);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (strcmp(tag, "ctx") == 0) {
|
||||
sub->args[i].arg_type = ARG_PTR_TO_CTX;
|
||||
continue;
|
||||
}
|
||||
if (strcmp(tag, "nonnull") == 0)
|
||||
is_nonnull = true;
|
||||
}
|
||||
|
||||
while (btf_type_is_modifier(t))
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
if (btf_type_is_int(t) || btf_is_any_enum(t)) {
|
||||
reg->type = SCALAR_VALUE;
|
||||
sub->args[i].arg_type = ARG_ANYTHING;
|
||||
continue;
|
||||
}
|
||||
if (btf_type_is_ptr(t)) {
|
||||
if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
|
||||
reg->type = PTR_TO_CTX;
|
||||
continue;
|
||||
}
|
||||
if (btf_type_is_ptr(t) && btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
|
||||
sub->args[i].arg_type = ARG_PTR_TO_CTX;
|
||||
continue;
|
||||
}
|
||||
if (btf_type_is_ptr(t) && btf_is_dynptr_ptr(btf, t)) {
|
||||
sub->args[i].arg_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY;
|
||||
continue;
|
||||
}
|
||||
if (is_global && btf_type_is_ptr(t)) {
|
||||
u32 mem_size;
|
||||
|
||||
t = btf_type_skip_modifiers(btf, t->type, NULL);
|
||||
|
||||
ref_t = btf_resolve_size(btf, t, ®->mem_size);
|
||||
ref_t = btf_resolve_size(btf, t, &mem_size);
|
||||
if (IS_ERR(ref_t)) {
|
||||
bpf_log(log,
|
||||
"arg#%d reference type('%s %s') size cannot be determined: %ld\n",
|
||||
@@ -7056,23 +6921,22 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg->type = PTR_TO_MEM | PTR_MAYBE_NULL;
|
||||
reg->id = ++env->id_gen;
|
||||
|
||||
sub->args[i].arg_type = is_nonnull ? ARG_PTR_TO_MEM : ARG_PTR_TO_MEM_OR_NULL;
|
||||
sub->args[i].mem_size = mem_size;
|
||||
continue;
|
||||
}
|
||||
if (is_nonnull) {
|
||||
bpf_log(log, "arg#%d marked as non-null, but is not a pointer type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
bpf_log(log, "Arg#%d type %s in %s() is not supported yet.\n",
|
||||
i, btf_type_str(t), tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* We have already ensured that the callback returns an integer, just
|
||||
* like all global subprogs. We need to determine it only has a single
|
||||
* scalar argument.
|
||||
*/
|
||||
if (is_ex_cb && (nargs != 1 || regs[BPF_REG_1].type != SCALAR_VALUE)) {
|
||||
bpf_log(log, "exception cb only supports single integer argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sub->arg_cnt = nargs;
|
||||
sub->args_cached = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,9 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
|
||||
#endif
|
||||
|
||||
INIT_LIST_HEAD_RCU(&fp->aux->ksym.lnode);
|
||||
#ifdef CONFIG_FINEIBT
|
||||
INIT_LIST_HEAD_RCU(&fp->aux->ksym_prefix.lnode);
|
||||
#endif
|
||||
mutex_init(&fp->aux->used_maps_mutex);
|
||||
mutex_init(&fp->aux->dst_mutex);
|
||||
|
||||
@@ -687,6 +690,23 @@ void bpf_prog_kallsyms_add(struct bpf_prog *fp)
|
||||
fp->aux->ksym.prog = true;
|
||||
|
||||
bpf_ksym_add(&fp->aux->ksym);
|
||||
|
||||
#ifdef CONFIG_FINEIBT
|
||||
/*
|
||||
* When FineIBT, code in the __cfi_foo() symbols can get executed
|
||||
* and hence unwinder needs help.
|
||||
*/
|
||||
if (cfi_mode != CFI_FINEIBT)
|
||||
return;
|
||||
|
||||
snprintf(fp->aux->ksym_prefix.name, KSYM_NAME_LEN,
|
||||
"__cfi_%s", fp->aux->ksym.name);
|
||||
|
||||
fp->aux->ksym_prefix.start = (unsigned long) fp->bpf_func - 16;
|
||||
fp->aux->ksym_prefix.end = (unsigned long) fp->bpf_func;
|
||||
|
||||
bpf_ksym_add(&fp->aux->ksym_prefix);
|
||||
#endif
|
||||
}
|
||||
|
||||
void bpf_prog_kallsyms_del(struct bpf_prog *fp)
|
||||
@@ -695,6 +715,11 @@ void bpf_prog_kallsyms_del(struct bpf_prog *fp)
|
||||
return;
|
||||
|
||||
bpf_ksym_del(&fp->aux->ksym);
|
||||
#ifdef CONFIG_FINEIBT
|
||||
if (cfi_mode != CFI_FINEIBT)
|
||||
return;
|
||||
bpf_ksym_del(&fp->aux->ksym_prefix);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct bpf_ksym *bpf_ksym_find(unsigned long addr)
|
||||
@@ -932,20 +957,20 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void bpf_prog_pack_free(struct bpf_binary_header *hdr)
|
||||
void bpf_prog_pack_free(void *ptr, u32 size)
|
||||
{
|
||||
struct bpf_prog_pack *pack = NULL, *tmp;
|
||||
unsigned int nbits;
|
||||
unsigned long pos;
|
||||
|
||||
mutex_lock(&pack_mutex);
|
||||
if (hdr->size > BPF_PROG_PACK_SIZE) {
|
||||
bpf_jit_free_exec(hdr);
|
||||
if (size > BPF_PROG_PACK_SIZE) {
|
||||
bpf_jit_free_exec(ptr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(tmp, &pack_list, list) {
|
||||
if ((void *)hdr >= tmp->ptr && (tmp->ptr + BPF_PROG_PACK_SIZE) > (void *)hdr) {
|
||||
if (ptr >= tmp->ptr && (tmp->ptr + BPF_PROG_PACK_SIZE) > ptr) {
|
||||
pack = tmp;
|
||||
break;
|
||||
}
|
||||
@@ -954,10 +979,10 @@ void bpf_prog_pack_free(struct bpf_binary_header *hdr)
|
||||
if (WARN_ONCE(!pack, "bpf_prog_pack bug\n"))
|
||||
goto out;
|
||||
|
||||
nbits = BPF_PROG_SIZE_TO_NBITS(hdr->size);
|
||||
pos = ((unsigned long)hdr - (unsigned long)pack->ptr) >> BPF_PROG_CHUNK_SHIFT;
|
||||
nbits = BPF_PROG_SIZE_TO_NBITS(size);
|
||||
pos = ((unsigned long)ptr - (unsigned long)pack->ptr) >> BPF_PROG_CHUNK_SHIFT;
|
||||
|
||||
WARN_ONCE(bpf_arch_text_invalidate(hdr, hdr->size),
|
||||
WARN_ONCE(bpf_arch_text_invalidate(ptr, size),
|
||||
"bpf_prog_pack bug: missing bpf_arch_text_invalidate?\n");
|
||||
|
||||
bitmap_clear(pack->bitmap, pos, nbits);
|
||||
@@ -1104,8 +1129,7 @@ bpf_jit_binary_pack_alloc(unsigned int proglen, u8 **image_ptr,
|
||||
|
||||
*rw_header = kvmalloc(size, GFP_KERNEL);
|
||||
if (!*rw_header) {
|
||||
bpf_arch_text_copy(&ro_header->size, &size, sizeof(size));
|
||||
bpf_prog_pack_free(ro_header);
|
||||
bpf_prog_pack_free(ro_header, size);
|
||||
bpf_jit_uncharge_modmem(size);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1136,7 +1160,7 @@ int bpf_jit_binary_pack_finalize(struct bpf_prog *prog,
|
||||
kvfree(rw_header);
|
||||
|
||||
if (IS_ERR(ptr)) {
|
||||
bpf_prog_pack_free(ro_header);
|
||||
bpf_prog_pack_free(ro_header, ro_header->size);
|
||||
return PTR_ERR(ptr);
|
||||
}
|
||||
return 0;
|
||||
@@ -1157,7 +1181,7 @@ void bpf_jit_binary_pack_free(struct bpf_binary_header *ro_header,
|
||||
{
|
||||
u32 size = ro_header->size;
|
||||
|
||||
bpf_prog_pack_free(ro_header);
|
||||
bpf_prog_pack_free(ro_header, size);
|
||||
kvfree(rw_header);
|
||||
bpf_jit_uncharge_modmem(size);
|
||||
}
|
||||
@@ -2668,12 +2692,16 @@ void __bpf_free_used_maps(struct bpf_prog_aux *aux,
|
||||
struct bpf_map **used_maps, u32 len)
|
||||
{
|
||||
struct bpf_map *map;
|
||||
bool sleepable;
|
||||
u32 i;
|
||||
|
||||
sleepable = aux->sleepable;
|
||||
for (i = 0; i < len; i++) {
|
||||
map = used_maps[i];
|
||||
if (map->ops->map_poke_untrack)
|
||||
map->ops->map_poke_untrack(map, aux);
|
||||
if (sleepable)
|
||||
atomic64_dec(&map->sleepable_refcnt);
|
||||
bpf_map_put(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,12 @@ __bpf_kfunc void bpf_cpumask_release(struct bpf_cpumask *cpumask)
|
||||
migrate_enable();
|
||||
}
|
||||
|
||||
__bpf_kfunc void bpf_cpumask_release_dtor(void *cpumask)
|
||||
{
|
||||
bpf_cpumask_release(cpumask);
|
||||
}
|
||||
CFI_NOSEAL(bpf_cpumask_release_dtor);
|
||||
|
||||
/**
|
||||
* bpf_cpumask_first() - Get the index of the first nonzero bit in the cpumask.
|
||||
* @cpumask: The cpumask being queried.
|
||||
@@ -405,6 +411,17 @@ __bpf_kfunc u32 bpf_cpumask_any_and_distribute(const struct cpumask *src1,
|
||||
return cpumask_any_and_distribute(src1, src2);
|
||||
}
|
||||
|
||||
/**
|
||||
* bpf_cpumask_weight() - Return the number of bits in @cpumask.
|
||||
* @cpumask: The cpumask being queried.
|
||||
*
|
||||
* Count the number of set bits in the given cpumask.
|
||||
*/
|
||||
__bpf_kfunc u32 bpf_cpumask_weight(const struct cpumask *cpumask)
|
||||
{
|
||||
return cpumask_weight(cpumask);
|
||||
}
|
||||
|
||||
__bpf_kfunc_end_defs();
|
||||
|
||||
BTF_SET8_START(cpumask_kfunc_btf_ids)
|
||||
@@ -432,6 +449,7 @@ BTF_ID_FLAGS(func, bpf_cpumask_full, KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_cpumask_copy, KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_cpumask_any_distribute, KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_cpumask_any_and_distribute, KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_cpumask_weight, KF_RCU)
|
||||
BTF_SET8_END(cpumask_kfunc_btf_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set cpumask_kfunc_set = {
|
||||
@@ -441,7 +459,7 @@ static const struct btf_kfunc_id_set cpumask_kfunc_set = {
|
||||
|
||||
BTF_ID_LIST(cpumask_dtor_ids)
|
||||
BTF_ID(struct, bpf_cpumask)
|
||||
BTF_ID(func, bpf_cpumask_release)
|
||||
BTF_ID(func, bpf_cpumask_release_dtor)
|
||||
|
||||
static int __init cpumask_kfunc_init(void)
|
||||
{
|
||||
|
||||
@@ -150,14 +150,11 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from,
|
||||
goto out;
|
||||
d->rw_image = bpf_jit_alloc_exec(PAGE_SIZE);
|
||||
if (!d->rw_image) {
|
||||
u32 size = PAGE_SIZE;
|
||||
|
||||
bpf_arch_text_copy(d->image, &size, sizeof(size));
|
||||
bpf_prog_pack_free((struct bpf_binary_header *)d->image);
|
||||
bpf_prog_pack_free(d->image, PAGE_SIZE);
|
||||
d->image = NULL;
|
||||
goto out;
|
||||
}
|
||||
bpf_image_ksym_add(d->image, &d->ksym);
|
||||
bpf_image_ksym_add(d->image, PAGE_SIZE, &d->ksym);
|
||||
}
|
||||
|
||||
prev_num_progs = d->num_progs;
|
||||
|
||||
@@ -898,7 +898,7 @@ static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l)
|
||||
|
||||
if (map->ops->map_fd_put_ptr) {
|
||||
ptr = fd_htab_map_get_ptr(map, l);
|
||||
map->ops->map_fd_put_ptr(ptr);
|
||||
map->ops->map_fd_put_ptr(map, ptr, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2485,7 +2485,7 @@ static void fd_htab_map_free(struct bpf_map *map)
|
||||
hlist_nulls_for_each_entry_safe(l, n, head, hash_node) {
|
||||
void *ptr = fd_htab_map_get_ptr(map, l);
|
||||
|
||||
map->ops->map_fd_put_ptr(ptr);
|
||||
map->ops->map_fd_put_ptr(map, ptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2524,9 +2524,15 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,
|
||||
if (IS_ERR(ptr))
|
||||
return PTR_ERR(ptr);
|
||||
|
||||
/* The htab bucket lock is always held during update operations in fd
|
||||
* htab map, and the following rcu_read_lock() is only used to avoid
|
||||
* the WARN_ON_ONCE in htab_map_update_elem().
|
||||
*/
|
||||
rcu_read_lock();
|
||||
ret = htab_map_update_elem(map, key, &ptr, map_flags);
|
||||
rcu_read_unlock();
|
||||
if (ret)
|
||||
map->ops->map_fd_put_ptr(ptr);
|
||||
map->ops->map_fd_put_ptr(map, ptr, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -32,12 +32,13 @@
|
||||
*
|
||||
* Different map implementations will rely on rcu in map methods
|
||||
* lookup/update/delete, therefore eBPF programs must run under rcu lock
|
||||
* if program is allowed to access maps, so check rcu_read_lock_held in
|
||||
* all three functions.
|
||||
* if program is allowed to access maps, so check rcu_read_lock_held() or
|
||||
* rcu_read_lock_trace_held() in all three functions.
|
||||
*/
|
||||
BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key)
|
||||
{
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
|
||||
!rcu_read_lock_bh_held());
|
||||
return (unsigned long) map->ops->map_lookup_elem(map, key);
|
||||
}
|
||||
|
||||
@@ -53,7 +54,8 @@ const struct bpf_func_proto bpf_map_lookup_elem_proto = {
|
||||
BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key,
|
||||
void *, value, u64, flags)
|
||||
{
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
|
||||
!rcu_read_lock_bh_held());
|
||||
return map->ops->map_update_elem(map, key, value, flags);
|
||||
}
|
||||
|
||||
@@ -70,7 +72,8 @@ const struct bpf_func_proto bpf_map_update_elem_proto = {
|
||||
|
||||
BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key)
|
||||
{
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
|
||||
!rcu_read_lock_bh_held());
|
||||
return map->ops->map_delete_elem(map, key);
|
||||
}
|
||||
|
||||
@@ -1937,10 +1940,7 @@ void __bpf_obj_drop_impl(void *p, const struct btf_record *rec, bool percpu)
|
||||
ma = &bpf_global_percpu_ma;
|
||||
else
|
||||
ma = &bpf_global_ma;
|
||||
if (rec && rec->refcount_off >= 0)
|
||||
bpf_mem_free_rcu(ma, p);
|
||||
else
|
||||
bpf_mem_free(ma, p);
|
||||
bpf_mem_free_rcu(ma, p);
|
||||
}
|
||||
|
||||
__bpf_kfunc void bpf_obj_drop_impl(void *p__alloc, void *meta__ign)
|
||||
@@ -2150,6 +2150,12 @@ __bpf_kfunc void bpf_task_release(struct task_struct *p)
|
||||
put_task_struct_rcu_user(p);
|
||||
}
|
||||
|
||||
__bpf_kfunc void bpf_task_release_dtor(void *p)
|
||||
{
|
||||
put_task_struct_rcu_user(p);
|
||||
}
|
||||
CFI_NOSEAL(bpf_task_release_dtor);
|
||||
|
||||
#ifdef CONFIG_CGROUPS
|
||||
/**
|
||||
* bpf_cgroup_acquire - Acquire a reference to a cgroup. A cgroup acquired by
|
||||
@@ -2174,6 +2180,12 @@ __bpf_kfunc void bpf_cgroup_release(struct cgroup *cgrp)
|
||||
cgroup_put(cgrp);
|
||||
}
|
||||
|
||||
__bpf_kfunc void bpf_cgroup_release_dtor(void *cgrp)
|
||||
{
|
||||
cgroup_put(cgrp);
|
||||
}
|
||||
CFI_NOSEAL(bpf_cgroup_release_dtor);
|
||||
|
||||
/**
|
||||
* bpf_cgroup_ancestor - Perform a lookup on an entry in a cgroup's ancestor
|
||||
* array. A cgroup returned by this kfunc which is not subsequently stored in a
|
||||
@@ -2231,6 +2243,25 @@ __bpf_kfunc long bpf_task_under_cgroup(struct task_struct *task,
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* bpf_task_get_cgroup1 - Acquires the associated cgroup of a task within a
|
||||
* specific cgroup1 hierarchy. The cgroup1 hierarchy is identified by its
|
||||
* hierarchy ID.
|
||||
* @task: The target task
|
||||
* @hierarchy_id: The ID of a cgroup1 hierarchy
|
||||
*
|
||||
* On success, the cgroup is returen. On failure, NULL is returned.
|
||||
*/
|
||||
__bpf_kfunc struct cgroup *
|
||||
bpf_task_get_cgroup1(struct task_struct *task, int hierarchy_id)
|
||||
{
|
||||
struct cgroup *cgrp = task_get_cgroup1(task, hierarchy_id);
|
||||
|
||||
if (IS_ERR(cgrp))
|
||||
return NULL;
|
||||
return cgrp;
|
||||
}
|
||||
#endif /* CONFIG_CGROUPS */
|
||||
|
||||
/**
|
||||
@@ -2506,7 +2537,7 @@ __bpf_kfunc void bpf_throw(u64 cookie)
|
||||
* which skips compiler generated instrumentation to do the same.
|
||||
*/
|
||||
kasan_unpoison_task_stack_below((void *)(long)ctx.sp);
|
||||
ctx.aux->bpf_exception_cb(cookie, ctx.sp, ctx.bp);
|
||||
ctx.aux->bpf_exception_cb(cookie, ctx.sp, ctx.bp, 0, 0);
|
||||
WARN(1, "A call to BPF exception callback should never return\n");
|
||||
}
|
||||
|
||||
@@ -2520,7 +2551,7 @@ BTF_ID_FLAGS(func, bpf_obj_new_impl, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_percpu_obj_new_impl, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_obj_drop_impl, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, bpf_percpu_obj_drop_impl, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, bpf_refcount_acquire_impl, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_refcount_acquire_impl, KF_ACQUIRE | KF_RET_NULL | KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_front_impl)
|
||||
BTF_ID_FLAGS(func, bpf_list_push_back_impl)
|
||||
BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL)
|
||||
@@ -2537,6 +2568,7 @@ BTF_ID_FLAGS(func, bpf_cgroup_release, KF_RELEASE)
|
||||
BTF_ID_FLAGS(func, bpf_cgroup_ancestor, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_cgroup_from_id, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_task_under_cgroup, KF_RCU)
|
||||
BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
|
||||
#endif
|
||||
BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL)
|
||||
BTF_ID_FLAGS(func, bpf_throw)
|
||||
@@ -2550,10 +2582,10 @@ static const struct btf_kfunc_id_set generic_kfunc_set = {
|
||||
|
||||
BTF_ID_LIST(generic_dtor_ids)
|
||||
BTF_ID(struct, task_struct)
|
||||
BTF_ID(func, bpf_task_release)
|
||||
BTF_ID(func, bpf_task_release_dtor)
|
||||
#ifdef CONFIG_CGROUPS
|
||||
BTF_ID(struct, cgroup)
|
||||
BTF_ID(func, bpf_cgroup_release)
|
||||
BTF_ID(func, bpf_cgroup_release_dtor)
|
||||
#endif
|
||||
|
||||
BTF_SET8_START(common_btf_ids)
|
||||
@@ -2610,6 +2642,7 @@ static int __init kfunc_init(void)
|
||||
|
||||
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &generic_kfunc_set);
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &generic_kfunc_set);
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &generic_kfunc_set);
|
||||
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &generic_kfunc_set);
|
||||
ret = ret ?: register_btf_id_dtor_kfuncs(generic_dtors,
|
||||
ARRAY_SIZE(generic_dtors),
|
||||
@@ -2618,3 +2651,22 @@ static int __init kfunc_init(void)
|
||||
}
|
||||
|
||||
late_initcall(kfunc_init);
|
||||
|
||||
/* Get a pointer to dynptr data up to len bytes for read only access. If
|
||||
* the dynptr doesn't have continuous data up to len bytes, return NULL.
|
||||
*/
|
||||
const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len)
|
||||
{
|
||||
return bpf_dynptr_slice(ptr, 0, NULL, len);
|
||||
}
|
||||
|
||||
/* Get a pointer to dynptr data up to len bytes for read write access. If
|
||||
* the dynptr doesn't have continuous data up to len bytes, or the dynptr
|
||||
* is read only, return NULL.
|
||||
*/
|
||||
void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len)
|
||||
{
|
||||
if (__bpf_dynptr_is_rdonly(ptr))
|
||||
return NULL;
|
||||
return (void *)__bpf_dynptr_data(ptr, len);
|
||||
}
|
||||
|
||||
@@ -599,8 +599,15 @@ EXPORT_SYMBOL(bpf_prog_get_type_path);
|
||||
*/
|
||||
static int bpf_show_options(struct seq_file *m, struct dentry *root)
|
||||
{
|
||||
umode_t mode = d_inode(root)->i_mode & S_IALLUGO & ~S_ISVTX;
|
||||
struct inode *inode = d_inode(root);
|
||||
umode_t mode = inode->i_mode & S_IALLUGO & ~S_ISVTX;
|
||||
|
||||
if (!uid_eq(inode->i_uid, GLOBAL_ROOT_UID))
|
||||
seq_printf(m, ",uid=%u",
|
||||
from_kuid_munged(&init_user_ns, inode->i_uid));
|
||||
if (!gid_eq(inode->i_gid, GLOBAL_ROOT_GID))
|
||||
seq_printf(m, ",gid=%u",
|
||||
from_kgid_munged(&init_user_ns, inode->i_gid));
|
||||
if (mode != S_IRWXUGO)
|
||||
seq_printf(m, ",mode=%o", mode);
|
||||
return 0;
|
||||
@@ -625,15 +632,21 @@ static const struct super_operations bpf_super_ops = {
|
||||
};
|
||||
|
||||
enum {
|
||||
OPT_UID,
|
||||
OPT_GID,
|
||||
OPT_MODE,
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec bpf_fs_parameters[] = {
|
||||
fsparam_u32 ("uid", OPT_UID),
|
||||
fsparam_u32 ("gid", OPT_GID),
|
||||
fsparam_u32oct ("mode", OPT_MODE),
|
||||
{}
|
||||
};
|
||||
|
||||
struct bpf_mount_opts {
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
umode_t mode;
|
||||
};
|
||||
|
||||
@@ -641,6 +654,8 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
struct bpf_mount_opts *opts = fc->fs_private;
|
||||
struct fs_parse_result result;
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
int opt;
|
||||
|
||||
opt = fs_parse(fc, bpf_fs_parameters, param, &result);
|
||||
@@ -662,12 +677,42 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
}
|
||||
|
||||
switch (opt) {
|
||||
case OPT_UID:
|
||||
uid = make_kuid(current_user_ns(), result.uint_32);
|
||||
if (!uid_valid(uid))
|
||||
goto bad_value;
|
||||
|
||||
/*
|
||||
* The requested uid must be representable in the
|
||||
* filesystem's idmapping.
|
||||
*/
|
||||
if (!kuid_has_mapping(fc->user_ns, uid))
|
||||
goto bad_value;
|
||||
|
||||
opts->uid = uid;
|
||||
break;
|
||||
case OPT_GID:
|
||||
gid = make_kgid(current_user_ns(), result.uint_32);
|
||||
if (!gid_valid(gid))
|
||||
goto bad_value;
|
||||
|
||||
/*
|
||||
* The requested gid must be representable in the
|
||||
* filesystem's idmapping.
|
||||
*/
|
||||
if (!kgid_has_mapping(fc->user_ns, gid))
|
||||
goto bad_value;
|
||||
|
||||
opts->gid = gid;
|
||||
break;
|
||||
case OPT_MODE:
|
||||
opts->mode = result.uint_32 & S_IALLUGO;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
bad_value:
|
||||
return invalfc(fc, "Bad value for '%s'", param->key);
|
||||
}
|
||||
|
||||
struct bpf_preload_ops *bpf_preload_ops;
|
||||
@@ -750,6 +795,8 @@ static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sb->s_op = &bpf_super_ops;
|
||||
|
||||
inode = sb->s_root->d_inode;
|
||||
inode->i_uid = opts->uid;
|
||||
inode->i_gid = opts->gid;
|
||||
inode->i_op = &bpf_dir_iops;
|
||||
inode->i_mode &= ~S_IALLUGO;
|
||||
populate_bpffs(sb->s_root);
|
||||
@@ -785,6 +832,8 @@ static int bpf_init_fs_context(struct fs_context *fc)
|
||||
return -ENOMEM;
|
||||
|
||||
opts->mode = S_IRWXUGO;
|
||||
opts->uid = current_fsuid();
|
||||
opts->gid = current_fsgid();
|
||||
|
||||
fc->fs_private = opts;
|
||||
fc->ops = &bpf_context_ops;
|
||||
|
||||
504
kernel/bpf/log.c
504
kernel/bpf/log.c
@@ -10,6 +10,8 @@
|
||||
#include <linux/bpf_verifier.h>
|
||||
#include <linux/math64.h>
|
||||
|
||||
#define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
|
||||
|
||||
static bool bpf_verifier_log_attr_valid(const struct bpf_verifier_log *log)
|
||||
{
|
||||
/* ubuf and len_total should both be specified (or not) together */
|
||||
@@ -325,3 +327,505 @@ __printf(2, 3) void bpf_log(struct bpf_verifier_log *log,
|
||||
va_end(args);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_log);
|
||||
|
||||
static const struct bpf_line_info *
|
||||
find_linfo(const struct bpf_verifier_env *env, u32 insn_off)
|
||||
{
|
||||
const struct bpf_line_info *linfo;
|
||||
const struct bpf_prog *prog;
|
||||
u32 i, nr_linfo;
|
||||
|
||||
prog = env->prog;
|
||||
nr_linfo = prog->aux->nr_linfo;
|
||||
|
||||
if (!nr_linfo || insn_off >= prog->len)
|
||||
return NULL;
|
||||
|
||||
linfo = prog->aux->linfo;
|
||||
for (i = 1; i < nr_linfo; i++)
|
||||
if (insn_off < linfo[i].insn_off)
|
||||
break;
|
||||
|
||||
return &linfo[i - 1];
|
||||
}
|
||||
|
||||
static const char *ltrim(const char *s)
|
||||
{
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
__printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env,
|
||||
u32 insn_off,
|
||||
const char *prefix_fmt, ...)
|
||||
{
|
||||
const struct bpf_line_info *linfo;
|
||||
|
||||
if (!bpf_verifier_log_needed(&env->log))
|
||||
return;
|
||||
|
||||
linfo = find_linfo(env, insn_off);
|
||||
if (!linfo || linfo == env->prev_linfo)
|
||||
return;
|
||||
|
||||
if (prefix_fmt) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, prefix_fmt);
|
||||
bpf_verifier_vlog(&env->log, prefix_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
verbose(env, "%s\n",
|
||||
ltrim(btf_name_by_offset(env->prog->aux->btf,
|
||||
linfo->line_off)));
|
||||
|
||||
env->prev_linfo = linfo;
|
||||
}
|
||||
|
||||
static const char *btf_type_name(const struct btf *btf, u32 id)
|
||||
{
|
||||
return btf_name_by_offset(btf, btf_type_by_id(btf, id)->name_off);
|
||||
}
|
||||
|
||||
/* string representation of 'enum bpf_reg_type'
|
||||
*
|
||||
* Note that reg_type_str() can not appear more than once in a single verbose()
|
||||
* statement.
|
||||
*/
|
||||
const char *reg_type_str(struct bpf_verifier_env *env, enum bpf_reg_type type)
|
||||
{
|
||||
char postfix[16] = {0}, prefix[64] = {0};
|
||||
static const char * const str[] = {
|
||||
[NOT_INIT] = "?",
|
||||
[SCALAR_VALUE] = "scalar",
|
||||
[PTR_TO_CTX] = "ctx",
|
||||
[CONST_PTR_TO_MAP] = "map_ptr",
|
||||
[PTR_TO_MAP_VALUE] = "map_value",
|
||||
[PTR_TO_STACK] = "fp",
|
||||
[PTR_TO_PACKET] = "pkt",
|
||||
[PTR_TO_PACKET_META] = "pkt_meta",
|
||||
[PTR_TO_PACKET_END] = "pkt_end",
|
||||
[PTR_TO_FLOW_KEYS] = "flow_keys",
|
||||
[PTR_TO_SOCKET] = "sock",
|
||||
[PTR_TO_SOCK_COMMON] = "sock_common",
|
||||
[PTR_TO_TCP_SOCK] = "tcp_sock",
|
||||
[PTR_TO_TP_BUFFER] = "tp_buffer",
|
||||
[PTR_TO_XDP_SOCK] = "xdp_sock",
|
||||
[PTR_TO_BTF_ID] = "ptr_",
|
||||
[PTR_TO_MEM] = "mem",
|
||||
[PTR_TO_BUF] = "buf",
|
||||
[PTR_TO_FUNC] = "func",
|
||||
[PTR_TO_MAP_KEY] = "map_key",
|
||||
[CONST_PTR_TO_DYNPTR] = "dynptr_ptr",
|
||||
};
|
||||
|
||||
if (type & PTR_MAYBE_NULL) {
|
||||
if (base_type(type) == PTR_TO_BTF_ID)
|
||||
strncpy(postfix, "or_null_", 16);
|
||||
else
|
||||
strncpy(postfix, "_or_null", 16);
|
||||
}
|
||||
|
||||
snprintf(prefix, sizeof(prefix), "%s%s%s%s%s%s%s",
|
||||
type & MEM_RDONLY ? "rdonly_" : "",
|
||||
type & MEM_RINGBUF ? "ringbuf_" : "",
|
||||
type & MEM_USER ? "user_" : "",
|
||||
type & MEM_PERCPU ? "percpu_" : "",
|
||||
type & MEM_RCU ? "rcu_" : "",
|
||||
type & PTR_UNTRUSTED ? "untrusted_" : "",
|
||||
type & PTR_TRUSTED ? "trusted_" : ""
|
||||
);
|
||||
|
||||
snprintf(env->tmp_str_buf, TMP_STR_BUF_LEN, "%s%s%s",
|
||||
prefix, str[base_type(type)], postfix);
|
||||
return env->tmp_str_buf;
|
||||
}
|
||||
|
||||
const char *dynptr_type_str(enum bpf_dynptr_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case BPF_DYNPTR_TYPE_LOCAL:
|
||||
return "local";
|
||||
case BPF_DYNPTR_TYPE_RINGBUF:
|
||||
return "ringbuf";
|
||||
case BPF_DYNPTR_TYPE_SKB:
|
||||
return "skb";
|
||||
case BPF_DYNPTR_TYPE_XDP:
|
||||
return "xdp";
|
||||
case BPF_DYNPTR_TYPE_INVALID:
|
||||
return "<invalid>";
|
||||
default:
|
||||
WARN_ONCE(1, "unknown dynptr type %d\n", type);
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
const char *iter_type_str(const struct btf *btf, u32 btf_id)
|
||||
{
|
||||
if (!btf || btf_id == 0)
|
||||
return "<invalid>";
|
||||
|
||||
/* we already validated that type is valid and has conforming name */
|
||||
return btf_type_name(btf, btf_id) + sizeof(ITER_PREFIX) - 1;
|
||||
}
|
||||
|
||||
const char *iter_state_str(enum bpf_iter_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case BPF_ITER_STATE_ACTIVE:
|
||||
return "active";
|
||||
case BPF_ITER_STATE_DRAINED:
|
||||
return "drained";
|
||||
case BPF_ITER_STATE_INVALID:
|
||||
return "<invalid>";
|
||||
default:
|
||||
WARN_ONCE(1, "unknown iter state %d\n", state);
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static char slot_type_char[] = {
|
||||
[STACK_INVALID] = '?',
|
||||
[STACK_SPILL] = 'r',
|
||||
[STACK_MISC] = 'm',
|
||||
[STACK_ZERO] = '0',
|
||||
[STACK_DYNPTR] = 'd',
|
||||
[STACK_ITER] = 'i',
|
||||
};
|
||||
|
||||
static void print_liveness(struct bpf_verifier_env *env,
|
||||
enum bpf_reg_liveness live)
|
||||
{
|
||||
if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN | REG_LIVE_DONE))
|
||||
verbose(env, "_");
|
||||
if (live & REG_LIVE_READ)
|
||||
verbose(env, "r");
|
||||
if (live & REG_LIVE_WRITTEN)
|
||||
verbose(env, "w");
|
||||
if (live & REG_LIVE_DONE)
|
||||
verbose(env, "D");
|
||||
}
|
||||
|
||||
#define UNUM_MAX_DECIMAL U16_MAX
|
||||
#define SNUM_MAX_DECIMAL S16_MAX
|
||||
#define SNUM_MIN_DECIMAL S16_MIN
|
||||
|
||||
static bool is_unum_decimal(u64 num)
|
||||
{
|
||||
return num <= UNUM_MAX_DECIMAL;
|
||||
}
|
||||
|
||||
static bool is_snum_decimal(s64 num)
|
||||
{
|
||||
return num >= SNUM_MIN_DECIMAL && num <= SNUM_MAX_DECIMAL;
|
||||
}
|
||||
|
||||
static void verbose_unum(struct bpf_verifier_env *env, u64 num)
|
||||
{
|
||||
if (is_unum_decimal(num))
|
||||
verbose(env, "%llu", num);
|
||||
else
|
||||
verbose(env, "%#llx", num);
|
||||
}
|
||||
|
||||
static void verbose_snum(struct bpf_verifier_env *env, s64 num)
|
||||
{
|
||||
if (is_snum_decimal(num))
|
||||
verbose(env, "%lld", num);
|
||||
else
|
||||
verbose(env, "%#llx", num);
|
||||
}
|
||||
|
||||
int tnum_strn(char *str, size_t size, struct tnum a)
|
||||
{
|
||||
/* print as a constant, if tnum is fully known */
|
||||
if (a.mask == 0) {
|
||||
if (is_unum_decimal(a.value))
|
||||
return snprintf(str, size, "%llu", a.value);
|
||||
else
|
||||
return snprintf(str, size, "%#llx", a.value);
|
||||
}
|
||||
return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tnum_strn);
|
||||
|
||||
static void print_scalar_ranges(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg,
|
||||
const char **sep)
|
||||
{
|
||||
/* For signed ranges, we want to unify 64-bit and 32-bit values in the
|
||||
* output as much as possible, but there is a bit of a complication.
|
||||
* If we choose to print values as decimals, this is natural to do,
|
||||
* because negative 64-bit and 32-bit values >= -S32_MIN have the same
|
||||
* representation due to sign extension. But if we choose to print
|
||||
* them in hex format (see is_snum_decimal()), then sign extension is
|
||||
* misleading.
|
||||
* E.g., smin=-2 and smin32=-2 are exactly the same in decimal, but in
|
||||
* hex they will be smin=0xfffffffffffffffe and smin32=0xfffffffe, two
|
||||
* very different numbers.
|
||||
* So we avoid sign extension if we choose to print values in hex.
|
||||
*/
|
||||
struct {
|
||||
const char *name;
|
||||
u64 val;
|
||||
bool omit;
|
||||
} minmaxs[] = {
|
||||
{"smin", reg->smin_value, reg->smin_value == S64_MIN},
|
||||
{"smax", reg->smax_value, reg->smax_value == S64_MAX},
|
||||
{"umin", reg->umin_value, reg->umin_value == 0},
|
||||
{"umax", reg->umax_value, reg->umax_value == U64_MAX},
|
||||
{"smin32",
|
||||
is_snum_decimal((s64)reg->s32_min_value)
|
||||
? (s64)reg->s32_min_value
|
||||
: (u32)reg->s32_min_value, reg->s32_min_value == S32_MIN},
|
||||
{"smax32",
|
||||
is_snum_decimal((s64)reg->s32_max_value)
|
||||
? (s64)reg->s32_max_value
|
||||
: (u32)reg->s32_max_value, reg->s32_max_value == S32_MAX},
|
||||
{"umin32", reg->u32_min_value, reg->u32_min_value == 0},
|
||||
{"umax32", reg->u32_max_value, reg->u32_max_value == U32_MAX},
|
||||
}, *m1, *m2, *mend = &minmaxs[ARRAY_SIZE(minmaxs)];
|
||||
bool neg1, neg2;
|
||||
|
||||
for (m1 = &minmaxs[0]; m1 < mend; m1++) {
|
||||
if (m1->omit)
|
||||
continue;
|
||||
|
||||
neg1 = m1->name[0] == 's' && (s64)m1->val < 0;
|
||||
|
||||
verbose(env, "%s%s=", *sep, m1->name);
|
||||
*sep = ",";
|
||||
|
||||
for (m2 = m1 + 2; m2 < mend; m2 += 2) {
|
||||
if (m2->omit || m2->val != m1->val)
|
||||
continue;
|
||||
/* don't mix negatives with positives */
|
||||
neg2 = m2->name[0] == 's' && (s64)m2->val < 0;
|
||||
if (neg2 != neg1)
|
||||
continue;
|
||||
m2->omit = true;
|
||||
verbose(env, "%s=", m2->name);
|
||||
}
|
||||
|
||||
if (m1->name[0] == 's')
|
||||
verbose_snum(env, m1->val);
|
||||
else
|
||||
verbose_unum(env, m1->val);
|
||||
}
|
||||
}
|
||||
|
||||
static bool type_is_map_ptr(enum bpf_reg_type t) {
|
||||
switch (base_type(t)) {
|
||||
case CONST_PTR_TO_MAP:
|
||||
case PTR_TO_MAP_KEY:
|
||||
case PTR_TO_MAP_VALUE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* _a stands for append, was shortened to avoid multiline statements below.
|
||||
* This macro is used to output a comma separated list of attributes.
|
||||
*/
|
||||
#define verbose_a(fmt, ...) ({ verbose(env, "%s" fmt, sep, ##__VA_ARGS__); sep = ","; })
|
||||
|
||||
static void print_reg_state(struct bpf_verifier_env *env,
|
||||
const struct bpf_func_state *state,
|
||||
const struct bpf_reg_state *reg)
|
||||
{
|
||||
enum bpf_reg_type t;
|
||||
const char *sep = "";
|
||||
|
||||
t = reg->type;
|
||||
if (t == SCALAR_VALUE && reg->precise)
|
||||
verbose(env, "P");
|
||||
if (t == SCALAR_VALUE && tnum_is_const(reg->var_off)) {
|
||||
/* reg->off should be 0 for SCALAR_VALUE */
|
||||
verbose_snum(env, reg->var_off.value + reg->off);
|
||||
return;
|
||||
}
|
||||
|
||||
verbose(env, "%s", reg_type_str(env, t));
|
||||
if (t == PTR_TO_STACK) {
|
||||
if (state->frameno != reg->frameno)
|
||||
verbose(env, "[%d]", reg->frameno);
|
||||
if (tnum_is_const(reg->var_off)) {
|
||||
verbose_snum(env, reg->var_off.value + reg->off);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (base_type(t) == PTR_TO_BTF_ID)
|
||||
verbose(env, "%s", btf_type_name(reg->btf, reg->btf_id));
|
||||
verbose(env, "(");
|
||||
if (reg->id)
|
||||
verbose_a("id=%d", reg->id);
|
||||
if (reg->ref_obj_id)
|
||||
verbose_a("ref_obj_id=%d", reg->ref_obj_id);
|
||||
if (type_is_non_owning_ref(reg->type))
|
||||
verbose_a("%s", "non_own_ref");
|
||||
if (type_is_map_ptr(t)) {
|
||||
if (reg->map_ptr->name[0])
|
||||
verbose_a("map=%s", reg->map_ptr->name);
|
||||
verbose_a("ks=%d,vs=%d",
|
||||
reg->map_ptr->key_size,
|
||||
reg->map_ptr->value_size);
|
||||
}
|
||||
if (t != SCALAR_VALUE && reg->off) {
|
||||
verbose_a("off=");
|
||||
verbose_snum(env, reg->off);
|
||||
}
|
||||
if (type_is_pkt_pointer(t)) {
|
||||
verbose_a("r=");
|
||||
verbose_unum(env, reg->range);
|
||||
}
|
||||
if (base_type(t) == PTR_TO_MEM) {
|
||||
verbose_a("sz=");
|
||||
verbose_unum(env, reg->mem_size);
|
||||
}
|
||||
if (t == CONST_PTR_TO_DYNPTR)
|
||||
verbose_a("type=%s", dynptr_type_str(reg->dynptr.type));
|
||||
if (tnum_is_const(reg->var_off)) {
|
||||
/* a pointer register with fixed offset */
|
||||
if (reg->var_off.value) {
|
||||
verbose_a("imm=");
|
||||
verbose_snum(env, reg->var_off.value);
|
||||
}
|
||||
} else {
|
||||
print_scalar_ranges(env, reg, &sep);
|
||||
if (!tnum_is_unknown(reg->var_off)) {
|
||||
char tn_buf[48];
|
||||
|
||||
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
|
||||
verbose_a("var_off=%s", tn_buf);
|
||||
}
|
||||
}
|
||||
verbose(env, ")");
|
||||
}
|
||||
|
||||
void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_state *state,
|
||||
bool print_all)
|
||||
{
|
||||
const struct bpf_reg_state *reg;
|
||||
int i;
|
||||
|
||||
if (state->frameno)
|
||||
verbose(env, " frame%d:", state->frameno);
|
||||
for (i = 0; i < MAX_BPF_REG; i++) {
|
||||
reg = &state->regs[i];
|
||||
if (reg->type == NOT_INIT)
|
||||
continue;
|
||||
if (!print_all && !reg_scratched(env, i))
|
||||
continue;
|
||||
verbose(env, " R%d", i);
|
||||
print_liveness(env, reg->live);
|
||||
verbose(env, "=");
|
||||
print_reg_state(env, state, reg);
|
||||
}
|
||||
for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
|
||||
char types_buf[BPF_REG_SIZE + 1];
|
||||
const char *sep = "";
|
||||
bool valid = false;
|
||||
u8 slot_type;
|
||||
int j;
|
||||
|
||||
if (!print_all && !stack_slot_scratched(env, i))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < BPF_REG_SIZE; j++) {
|
||||
slot_type = state->stack[i].slot_type[j];
|
||||
if (slot_type != STACK_INVALID)
|
||||
valid = true;
|
||||
types_buf[j] = slot_type_char[slot_type];
|
||||
}
|
||||
types_buf[BPF_REG_SIZE] = 0;
|
||||
if (!valid)
|
||||
continue;
|
||||
|
||||
reg = &state->stack[i].spilled_ptr;
|
||||
switch (state->stack[i].slot_type[BPF_REG_SIZE - 1]) {
|
||||
case STACK_SPILL:
|
||||
/* print MISC/ZERO/INVALID slots above subreg spill */
|
||||
for (j = 0; j < BPF_REG_SIZE; j++)
|
||||
if (state->stack[i].slot_type[j] == STACK_SPILL)
|
||||
break;
|
||||
types_buf[j] = '\0';
|
||||
|
||||
verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
|
||||
print_liveness(env, reg->live);
|
||||
verbose(env, "=%s", types_buf);
|
||||
print_reg_state(env, state, reg);
|
||||
break;
|
||||
case STACK_DYNPTR:
|
||||
/* skip to main dynptr slot */
|
||||
i += BPF_DYNPTR_NR_SLOTS - 1;
|
||||
reg = &state->stack[i].spilled_ptr;
|
||||
|
||||
verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
|
||||
print_liveness(env, reg->live);
|
||||
verbose(env, "=dynptr_%s(", dynptr_type_str(reg->dynptr.type));
|
||||
if (reg->id)
|
||||
verbose_a("id=%d", reg->id);
|
||||
if (reg->ref_obj_id)
|
||||
verbose_a("ref_id=%d", reg->ref_obj_id);
|
||||
if (reg->dynptr_id)
|
||||
verbose_a("dynptr_id=%d", reg->dynptr_id);
|
||||
verbose(env, ")");
|
||||
break;
|
||||
case STACK_ITER:
|
||||
/* only main slot has ref_obj_id set; skip others */
|
||||
if (!reg->ref_obj_id)
|
||||
continue;
|
||||
|
||||
verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
|
||||
print_liveness(env, reg->live);
|
||||
verbose(env, "=iter_%s(ref_id=%d,state=%s,depth=%u)",
|
||||
iter_type_str(reg->iter.btf, reg->iter.btf_id),
|
||||
reg->ref_obj_id, iter_state_str(reg->iter.state),
|
||||
reg->iter.depth);
|
||||
break;
|
||||
case STACK_MISC:
|
||||
case STACK_ZERO:
|
||||
default:
|
||||
verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
|
||||
print_liveness(env, reg->live);
|
||||
verbose(env, "=%s", types_buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state->acquired_refs && state->refs[0].id) {
|
||||
verbose(env, " refs=%d", state->refs[0].id);
|
||||
for (i = 1; i < state->acquired_refs; i++)
|
||||
if (state->refs[i].id)
|
||||
verbose(env, ",%d", state->refs[i].id);
|
||||
}
|
||||
if (state->in_callback_fn)
|
||||
verbose(env, " cb");
|
||||
if (state->in_async_callback_fn)
|
||||
verbose(env, " async_cb");
|
||||
verbose(env, "\n");
|
||||
if (!print_all)
|
||||
mark_verifier_state_clean(env);
|
||||
}
|
||||
|
||||
static inline u32 vlog_alignment(u32 pos)
|
||||
{
|
||||
return round_up(max(pos + BPF_LOG_MIN_ALIGNMENT / 2, BPF_LOG_ALIGNMENT),
|
||||
BPF_LOG_MIN_ALIGNMENT) - pos - 1;
|
||||
}
|
||||
|
||||
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_func_state *state)
|
||||
{
|
||||
if (env->prev_log_pos && env->prev_log_pos == env->log.end_pos) {
|
||||
/* remove new line character */
|
||||
bpf_vlog_reset(&env->log, env->prev_log_pos - 1);
|
||||
verbose(env, "%*c;", vlog_alignment(env->prev_insn_print_pos), ' ');
|
||||
} else {
|
||||
verbose(env, "%d:", env->insn_idx);
|
||||
}
|
||||
print_verifier_state(env, state, false);
|
||||
}
|
||||
|
||||
@@ -231,6 +231,9 @@ static void *trie_lookup_elem(struct bpf_map *map, void *_key)
|
||||
struct lpm_trie_node *node, *found = NULL;
|
||||
struct bpf_lpm_trie_key *key = _key;
|
||||
|
||||
if (key->prefixlen > trie->max_prefixlen)
|
||||
return NULL;
|
||||
|
||||
/* Start walking the trie from the root node ... */
|
||||
|
||||
for (node = rcu_dereference_check(trie->root, rcu_read_lock_bh_held());
|
||||
|
||||
@@ -127,12 +127,21 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
||||
return inner_map;
|
||||
}
|
||||
|
||||
void bpf_map_fd_put_ptr(void *ptr)
|
||||
void bpf_map_fd_put_ptr(struct bpf_map *map, void *ptr, bool need_defer)
|
||||
{
|
||||
/* ptr->ops->map_free() has to go through one
|
||||
* rcu grace period by itself.
|
||||
struct bpf_map *inner_map = ptr;
|
||||
|
||||
/* Defer the freeing of inner map according to the sleepable attribute
|
||||
* of bpf program which owns the outer map, so unnecessary waiting for
|
||||
* RCU tasks trace grace period can be avoided.
|
||||
*/
|
||||
bpf_map_put(ptr);
|
||||
if (need_defer) {
|
||||
if (atomic64_read(&map->sleepable_refcnt))
|
||||
WRITE_ONCE(inner_map->free_after_mult_rcu_gp, true);
|
||||
else
|
||||
WRITE_ONCE(inner_map->free_after_rcu_gp, true);
|
||||
}
|
||||
bpf_map_put(inner_map);
|
||||
}
|
||||
|
||||
u32 bpf_map_fd_sys_lookup_elem(void *ptr)
|
||||
|
||||
@@ -13,7 +13,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd);
|
||||
void bpf_map_meta_free(struct bpf_map *map_meta);
|
||||
void *bpf_map_fd_get_ptr(struct bpf_map *map, struct file *map_file,
|
||||
int ufd);
|
||||
void bpf_map_fd_put_ptr(void *ptr);
|
||||
void bpf_map_fd_put_ptr(struct bpf_map *map, void *ptr, bool need_defer);
|
||||
u32 bpf_map_fd_sys_lookup_elem(void *ptr);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -121,6 +121,8 @@ struct bpf_mem_caches {
|
||||
struct bpf_mem_cache cache[NUM_CACHES];
|
||||
};
|
||||
|
||||
static const u16 sizes[NUM_CACHES] = {96, 192, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096};
|
||||
|
||||
static struct llist_node notrace *__llist_del_first(struct llist_head *head)
|
||||
{
|
||||
struct llist_node *entry, *next;
|
||||
@@ -462,11 +464,17 @@ static void notrace irq_work_raise(struct bpf_mem_cache *c)
|
||||
* consume ~ 11 Kbyte per cpu.
|
||||
* Typical case will be between 11K and 116K closer to 11K.
|
||||
* bpf progs can and should share bpf_mem_cache when possible.
|
||||
*
|
||||
* Percpu allocation is typically rare. To avoid potential unnecessary large
|
||||
* memory consumption, set low_mark = 1 and high_mark = 3, resulting in c->batch = 1.
|
||||
*/
|
||||
static void init_refill_work(struct bpf_mem_cache *c)
|
||||
{
|
||||
init_irq_work(&c->refill_work, bpf_mem_refill);
|
||||
if (c->unit_size <= 256) {
|
||||
if (c->percpu_size) {
|
||||
c->low_watermark = 1;
|
||||
c->high_watermark = 3;
|
||||
} else if (c->unit_size <= 256) {
|
||||
c->low_watermark = 32;
|
||||
c->high_watermark = 96;
|
||||
} else {
|
||||
@@ -483,32 +491,16 @@ static void init_refill_work(struct bpf_mem_cache *c)
|
||||
|
||||
static void prefill_mem_cache(struct bpf_mem_cache *c, int cpu)
|
||||
{
|
||||
/* To avoid consuming memory assume that 1st run of bpf
|
||||
* prog won't be doing more than 4 map_update_elem from
|
||||
* irq disabled region
|
||||
int cnt = 1;
|
||||
|
||||
/* To avoid consuming memory, for non-percpu allocation, assume that
|
||||
* 1st run of bpf prog won't be doing more than 4 map_update_elem from
|
||||
* irq disabled region if unit size is less than or equal to 256.
|
||||
* For all other cases, let us just do one allocation.
|
||||
*/
|
||||
alloc_bulk(c, c->unit_size <= 256 ? 4 : 1, cpu_to_node(cpu), false);
|
||||
}
|
||||
|
||||
static int check_obj_size(struct bpf_mem_cache *c, unsigned int idx)
|
||||
{
|
||||
struct llist_node *first;
|
||||
unsigned int obj_size;
|
||||
|
||||
first = c->free_llist.first;
|
||||
if (!first)
|
||||
return 0;
|
||||
|
||||
if (c->percpu_size)
|
||||
obj_size = pcpu_alloc_size(((void **)first)[1]);
|
||||
else
|
||||
obj_size = ksize(first);
|
||||
if (obj_size != c->unit_size) {
|
||||
WARN_ONCE(1, "bpf_mem_cache[%u]: percpu %d, unexpected object size %u, expect %u\n",
|
||||
idx, c->percpu_size, obj_size, c->unit_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
if (!c->percpu_size && c->unit_size <= 256)
|
||||
cnt = 4;
|
||||
alloc_bulk(c, cnt, cpu_to_node(cpu), false);
|
||||
}
|
||||
|
||||
/* When size != 0 bpf_mem_cache for each cpu.
|
||||
@@ -520,11 +512,13 @@ static int check_obj_size(struct bpf_mem_cache *c, unsigned int idx)
|
||||
*/
|
||||
int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)
|
||||
{
|
||||
static u16 sizes[NUM_CACHES] = {96, 192, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096};
|
||||
int cpu, i, err, unit_size, percpu_size = 0;
|
||||
struct bpf_mem_caches *cc, __percpu *pcc;
|
||||
struct bpf_mem_cache *c, __percpu *pc;
|
||||
struct obj_cgroup *objcg = NULL;
|
||||
int cpu, i, unit_size, percpu_size = 0;
|
||||
|
||||
if (percpu && size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* room for llist_node and per-cpu pointer */
|
||||
if (percpu)
|
||||
@@ -544,6 +538,8 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)
|
||||
if (memcg_bpf_enabled())
|
||||
objcg = get_obj_cgroup_from_current();
|
||||
#endif
|
||||
ma->objcg = objcg;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
c = per_cpu_ptr(pc, cpu);
|
||||
c->unit_size = unit_size;
|
||||
@@ -560,10 +556,10 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)
|
||||
pcc = __alloc_percpu_gfp(sizeof(*cc), 8, GFP_KERNEL);
|
||||
if (!pcc)
|
||||
return -ENOMEM;
|
||||
err = 0;
|
||||
#ifdef CONFIG_MEMCG_KMEM
|
||||
objcg = get_obj_cgroup_from_current();
|
||||
#endif
|
||||
ma->objcg = objcg;
|
||||
for_each_possible_cpu(cpu) {
|
||||
cc = per_cpu_ptr(pcc, cpu);
|
||||
for (i = 0; i < NUM_CACHES; i++) {
|
||||
@@ -574,28 +570,62 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu)
|
||||
c->tgt = c;
|
||||
|
||||
init_refill_work(c);
|
||||
/* Another bpf_mem_cache will be used when allocating
|
||||
* c->unit_size in bpf_mem_alloc(), so doesn't prefill
|
||||
* for the bpf_mem_cache because these free objects will
|
||||
* never be used.
|
||||
*/
|
||||
if (i != bpf_mem_cache_idx(c->unit_size))
|
||||
continue;
|
||||
prefill_mem_cache(c, cpu);
|
||||
err = check_obj_size(c, i);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
ma->caches = pcc;
|
||||
/* refill_work is either zeroed or initialized, so it is safe to
|
||||
* call irq_work_sync().
|
||||
*/
|
||||
if (err)
|
||||
bpf_mem_alloc_destroy(ma);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_mem_alloc_percpu_init(struct bpf_mem_alloc *ma, struct obj_cgroup *objcg)
|
||||
{
|
||||
struct bpf_mem_caches __percpu *pcc;
|
||||
|
||||
pcc = __alloc_percpu_gfp(sizeof(struct bpf_mem_caches), 8, GFP_KERNEL);
|
||||
if (!pcc)
|
||||
return -ENOMEM;
|
||||
|
||||
ma->caches = pcc;
|
||||
ma->objcg = objcg;
|
||||
ma->percpu = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_mem_alloc_percpu_unit_init(struct bpf_mem_alloc *ma, int size)
|
||||
{
|
||||
struct bpf_mem_caches *cc, __percpu *pcc;
|
||||
int cpu, i, unit_size, percpu_size;
|
||||
struct obj_cgroup *objcg;
|
||||
struct bpf_mem_cache *c;
|
||||
|
||||
i = bpf_mem_cache_idx(size);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* room for llist_node and per-cpu pointer */
|
||||
percpu_size = LLIST_NODE_SZ + sizeof(void *);
|
||||
|
||||
unit_size = sizes[i];
|
||||
objcg = ma->objcg;
|
||||
pcc = ma->caches;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
cc = per_cpu_ptr(pcc, cpu);
|
||||
c = &cc->cache[i];
|
||||
if (c->unit_size)
|
||||
break;
|
||||
|
||||
c->unit_size = unit_size;
|
||||
c->objcg = objcg;
|
||||
c->percpu_size = percpu_size;
|
||||
c->tgt = c;
|
||||
|
||||
init_refill_work(c);
|
||||
prefill_mem_cache(c, cpu);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drain_mem_cache(struct bpf_mem_cache *c)
|
||||
@@ -729,9 +759,8 @@ void bpf_mem_alloc_destroy(struct bpf_mem_alloc *ma)
|
||||
rcu_in_progress += atomic_read(&c->call_rcu_ttrace_in_progress);
|
||||
rcu_in_progress += atomic_read(&c->call_rcu_in_progress);
|
||||
}
|
||||
/* objcg is the same across cpus */
|
||||
if (c->objcg)
|
||||
obj_cgroup_put(c->objcg);
|
||||
if (ma->objcg)
|
||||
obj_cgroup_put(ma->objcg);
|
||||
destroy_mem_alloc(ma, rcu_in_progress);
|
||||
}
|
||||
if (ma->caches) {
|
||||
@@ -747,8 +776,8 @@ void bpf_mem_alloc_destroy(struct bpf_mem_alloc *ma)
|
||||
rcu_in_progress += atomic_read(&c->call_rcu_in_progress);
|
||||
}
|
||||
}
|
||||
if (c->objcg)
|
||||
obj_cgroup_put(c->objcg);
|
||||
if (ma->objcg)
|
||||
obj_cgroup_put(ma->objcg);
|
||||
destroy_mem_alloc(ma, rcu_in_progress);
|
||||
}
|
||||
}
|
||||
@@ -869,9 +898,11 @@ void notrace *bpf_mem_alloc(struct bpf_mem_alloc *ma, size_t size)
|
||||
void *ret;
|
||||
|
||||
if (!size)
|
||||
return ZERO_SIZE_PTR;
|
||||
return NULL;
|
||||
|
||||
idx = bpf_mem_cache_idx(size + LLIST_NODE_SZ);
|
||||
if (!ma->percpu)
|
||||
size += LLIST_NODE_SZ;
|
||||
idx = bpf_mem_cache_idx(size);
|
||||
if (idx < 0)
|
||||
return NULL;
|
||||
|
||||
@@ -879,26 +910,17 @@ void notrace *bpf_mem_alloc(struct bpf_mem_alloc *ma, size_t size)
|
||||
return !ret ? NULL : ret + LLIST_NODE_SZ;
|
||||
}
|
||||
|
||||
static notrace int bpf_mem_free_idx(void *ptr, bool percpu)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
if (percpu)
|
||||
size = pcpu_alloc_size(*((void **)ptr));
|
||||
else
|
||||
size = ksize(ptr - LLIST_NODE_SZ);
|
||||
return bpf_mem_cache_idx(size);
|
||||
}
|
||||
|
||||
void notrace bpf_mem_free(struct bpf_mem_alloc *ma, void *ptr)
|
||||
{
|
||||
struct bpf_mem_cache *c;
|
||||
int idx;
|
||||
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
idx = bpf_mem_free_idx(ptr, ma->percpu);
|
||||
if (idx < 0)
|
||||
c = *(void **)(ptr - LLIST_NODE_SZ);
|
||||
idx = bpf_mem_cache_idx(c->unit_size);
|
||||
if (WARN_ON_ONCE(idx < 0))
|
||||
return;
|
||||
|
||||
unit_free(this_cpu_ptr(ma->caches)->cache + idx, ptr);
|
||||
@@ -906,13 +928,15 @@ void notrace bpf_mem_free(struct bpf_mem_alloc *ma, void *ptr)
|
||||
|
||||
void notrace bpf_mem_free_rcu(struct bpf_mem_alloc *ma, void *ptr)
|
||||
{
|
||||
struct bpf_mem_cache *c;
|
||||
int idx;
|
||||
|
||||
if (!ptr)
|
||||
return;
|
||||
|
||||
idx = bpf_mem_free_idx(ptr, ma->percpu);
|
||||
if (idx < 0)
|
||||
c = *(void **)(ptr - LLIST_NODE_SZ);
|
||||
idx = bpf_mem_cache_idx(c->unit_size);
|
||||
if (WARN_ON_ONCE(idx < 0))
|
||||
return;
|
||||
|
||||
unit_free_rcu(this_cpu_ptr(ma->caches)->cache + idx, ptr);
|
||||
@@ -986,41 +1010,3 @@ void notrace *bpf_mem_cache_alloc_flags(struct bpf_mem_alloc *ma, gfp_t flags)
|
||||
|
||||
return !ret ? NULL : ret + LLIST_NODE_SZ;
|
||||
}
|
||||
|
||||
/* The alignment of dynamic per-cpu area is 8, so c->unit_size and the
|
||||
* actual size of dynamic per-cpu area will always be matched and there is
|
||||
* no need to adjust size_index for per-cpu allocation. However for the
|
||||
* simplicity of the implementation, use an unified size_index for both
|
||||
* kmalloc and per-cpu allocation.
|
||||
*/
|
||||
static __init int bpf_mem_cache_adjust_size(void)
|
||||
{
|
||||
unsigned int size;
|
||||
|
||||
/* Adjusting the indexes in size_index() according to the object_size
|
||||
* of underlying slab cache, so bpf_mem_alloc() will select a
|
||||
* bpf_mem_cache with unit_size equal to the object_size of
|
||||
* the underlying slab cache.
|
||||
*
|
||||
* The maximal value of KMALLOC_MIN_SIZE and __kmalloc_minalign() is
|
||||
* 256-bytes, so only do adjustment for [8-bytes, 192-bytes].
|
||||
*/
|
||||
for (size = 192; size >= 8; size -= 8) {
|
||||
unsigned int kmalloc_size, index;
|
||||
|
||||
kmalloc_size = kmalloc_size_roundup(size);
|
||||
if (kmalloc_size == size)
|
||||
continue;
|
||||
|
||||
if (kmalloc_size <= 192)
|
||||
index = size_index[(kmalloc_size - 1) / 8];
|
||||
else
|
||||
index = fls(kmalloc_size - 1) - 1;
|
||||
/* Only overwrite if necessary */
|
||||
if (size_index[(size - 1) / 8] != index)
|
||||
size_index[(size - 1) / 8] = index;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(bpf_mem_cache_adjust_size);
|
||||
|
||||
@@ -388,6 +388,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
|
||||
{
|
||||
u32 trace_nr, copy_len, elem_size, num_elem, max_depth;
|
||||
bool user_build_id = flags & BPF_F_USER_BUILD_ID;
|
||||
bool crosstask = task && task != current;
|
||||
u32 skip = flags & BPF_F_SKIP_FIELD_MASK;
|
||||
bool user = flags & BPF_F_USER_STACK;
|
||||
struct perf_callchain_entry *trace;
|
||||
@@ -410,6 +411,14 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
|
||||
if (task && user && !user_mode(regs))
|
||||
goto err_fault;
|
||||
|
||||
/* get_perf_callchain does not support crosstask user stack walking
|
||||
* but returns an empty stack instead of NULL.
|
||||
*/
|
||||
if (crosstask && user) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
num_elem = size / elem_size;
|
||||
max_depth = num_elem + skip;
|
||||
if (sysctl_perf_event_max_stack < max_depth)
|
||||
@@ -421,7 +430,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
|
||||
trace = get_callchain_entry_for_task(task, max_depth);
|
||||
else
|
||||
trace = get_perf_callchain(regs, 0, kernel, user, max_depth,
|
||||
false, false);
|
||||
crosstask, false);
|
||||
if (unlikely(!trace))
|
||||
goto err_fault;
|
||||
|
||||
|
||||
@@ -142,9 +142,13 @@ static u32 bpf_map_value_size(const struct bpf_map *map)
|
||||
|
||||
static void maybe_wait_bpf_programs(struct bpf_map *map)
|
||||
{
|
||||
/* Wait for any running BPF programs to complete so that
|
||||
* userspace, when we return to it, knows that all programs
|
||||
* that could be running use the new map value.
|
||||
/* Wait for any running non-sleepable BPF programs to complete so that
|
||||
* userspace, when we return to it, knows that all non-sleepable
|
||||
* programs that could be running use the new map value. For sleepable
|
||||
* BPF programs, synchronize_rcu_tasks_trace() should be used to wait
|
||||
* for the completions of these programs, but considering the waiting
|
||||
* time can be very long and userspace may think it will hang forever,
|
||||
* so don't handle sleepable BPF programs now.
|
||||
*/
|
||||
if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS ||
|
||||
map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS)
|
||||
@@ -180,15 +184,11 @@ static int bpf_map_update_value(struct bpf_map *map, struct file *map_file,
|
||||
err = bpf_percpu_cgroup_storage_update(map, key, value,
|
||||
flags);
|
||||
} else if (IS_FD_ARRAY(map)) {
|
||||
rcu_read_lock();
|
||||
err = bpf_fd_array_map_update_elem(map, map_file, key, value,
|
||||
flags);
|
||||
rcu_read_unlock();
|
||||
} else if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) {
|
||||
rcu_read_lock();
|
||||
err = bpf_fd_htab_map_update_elem(map, map_file, key, value,
|
||||
flags);
|
||||
rcu_read_unlock();
|
||||
} else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {
|
||||
/* rcu_read_lock() is not needed */
|
||||
err = bpf_fd_reuseport_array_update_elem(map, key, value,
|
||||
@@ -203,7 +203,6 @@ static int bpf_map_update_value(struct bpf_map *map, struct file *map_file,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
bpf_enable_instrumentation();
|
||||
maybe_wait_bpf_programs(map);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -264,7 +263,6 @@ static int bpf_map_copy_value(struct bpf_map *map, void *key, void *value,
|
||||
}
|
||||
|
||||
bpf_enable_instrumentation();
|
||||
maybe_wait_bpf_programs(map);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -694,6 +692,7 @@ static void bpf_map_free_deferred(struct work_struct *work)
|
||||
{
|
||||
struct bpf_map *map = container_of(work, struct bpf_map, work);
|
||||
struct btf_record *rec = map->record;
|
||||
struct btf *btf = map->btf;
|
||||
|
||||
security_bpf_map_free(map);
|
||||
bpf_map_release_memcg(map);
|
||||
@@ -709,6 +708,10 @@ static void bpf_map_free_deferred(struct work_struct *work)
|
||||
* template bpf_map struct used during verification.
|
||||
*/
|
||||
btf_record_free(rec);
|
||||
/* Delay freeing of btf for maps, as map_free callback may need
|
||||
* struct_meta info which will be freed with btf_put().
|
||||
*/
|
||||
btf_put(btf);
|
||||
}
|
||||
|
||||
static void bpf_map_put_uref(struct bpf_map *map)
|
||||
@@ -719,6 +722,28 @@ static void bpf_map_put_uref(struct bpf_map *map)
|
||||
}
|
||||
}
|
||||
|
||||
static void bpf_map_free_in_work(struct bpf_map *map)
|
||||
{
|
||||
INIT_WORK(&map->work, bpf_map_free_deferred);
|
||||
/* Avoid spawning kworkers, since they all might contend
|
||||
* for the same mutex like slab_mutex.
|
||||
*/
|
||||
queue_work(system_unbound_wq, &map->work);
|
||||
}
|
||||
|
||||
static void bpf_map_free_rcu_gp(struct rcu_head *rcu)
|
||||
{
|
||||
bpf_map_free_in_work(container_of(rcu, struct bpf_map, rcu));
|
||||
}
|
||||
|
||||
static void bpf_map_free_mult_rcu_gp(struct rcu_head *rcu)
|
||||
{
|
||||
if (rcu_trace_implies_rcu_gp())
|
||||
bpf_map_free_rcu_gp(rcu);
|
||||
else
|
||||
call_rcu(rcu, bpf_map_free_rcu_gp);
|
||||
}
|
||||
|
||||
/* decrement map refcnt and schedule it for freeing via workqueue
|
||||
* (underlying map implementation ops->map_free() might sleep)
|
||||
*/
|
||||
@@ -727,12 +752,14 @@ void bpf_map_put(struct bpf_map *map)
|
||||
if (atomic64_dec_and_test(&map->refcnt)) {
|
||||
/* bpf_map_free_id() must be called first */
|
||||
bpf_map_free_id(map);
|
||||
btf_put(map->btf);
|
||||
INIT_WORK(&map->work, bpf_map_free_deferred);
|
||||
/* Avoid spawning kworkers, since they all might contend
|
||||
* for the same mutex like slab_mutex.
|
||||
*/
|
||||
queue_work(system_unbound_wq, &map->work);
|
||||
|
||||
WARN_ON_ONCE(atomic64_read(&map->sleepable_refcnt));
|
||||
if (READ_ONCE(map->free_after_mult_rcu_gp))
|
||||
call_rcu_tasks_trace(&map->rcu, bpf_map_free_mult_rcu_gp);
|
||||
else if (READ_ONCE(map->free_after_rcu_gp))
|
||||
call_rcu(&map->rcu, bpf_map_free_rcu_gp);
|
||||
else
|
||||
bpf_map_free_in_work(map);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_map_put);
|
||||
@@ -1524,6 +1551,8 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
|
||||
}
|
||||
|
||||
err = bpf_map_update_value(map, f.file, key, value, attr->flags);
|
||||
if (!err)
|
||||
maybe_wait_bpf_programs(map);
|
||||
|
||||
kvfree(value);
|
||||
free_key:
|
||||
@@ -1579,7 +1608,8 @@ static int map_delete_elem(union bpf_attr *attr, bpfptr_t uattr)
|
||||
err = map->ops->map_delete_elem(map, key);
|
||||
rcu_read_unlock();
|
||||
bpf_enable_instrumentation();
|
||||
maybe_wait_bpf_programs(map);
|
||||
if (!err)
|
||||
maybe_wait_bpf_programs(map);
|
||||
out:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
@@ -1676,6 +1706,9 @@ int generic_map_delete_batch(struct bpf_map *map,
|
||||
if (!max_count)
|
||||
return 0;
|
||||
|
||||
if (put_user(0, &uattr->batch.count))
|
||||
return -EFAULT;
|
||||
|
||||
key = kvmalloc(map->key_size, GFP_USER | __GFP_NOWARN);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
@@ -1705,7 +1738,6 @@ int generic_map_delete_batch(struct bpf_map *map,
|
||||
|
||||
kvfree(key);
|
||||
|
||||
maybe_wait_bpf_programs(map);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1733,6 +1765,9 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
|
||||
if (!max_count)
|
||||
return 0;
|
||||
|
||||
if (put_user(0, &uattr->batch.count))
|
||||
return -EFAULT;
|
||||
|
||||
key = kvmalloc(map->key_size, GFP_USER | __GFP_NOWARN);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
@@ -1763,6 +1798,7 @@ int generic_map_update_batch(struct bpf_map *map, struct file *map_file,
|
||||
|
||||
kvfree(value);
|
||||
kvfree(key);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2573,7 +2609,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
|
||||
BPF_F_SLEEPABLE |
|
||||
BPF_F_TEST_RND_HI32 |
|
||||
BPF_F_XDP_HAS_FRAGS |
|
||||
BPF_F_XDP_DEV_BOUND_ONLY))
|
||||
BPF_F_XDP_DEV_BOUND_ONLY |
|
||||
BPF_F_TEST_REG_INVARIANTS))
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
|
||||
@@ -2701,6 +2738,22 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
|
||||
goto free_prog_sec;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bookkeeping for managing the program attachment chain.
|
||||
*
|
||||
* It might be tempting to set attach_tracing_prog flag at the attachment
|
||||
* time, but this will not prevent from loading bunch of tracing prog
|
||||
* first, then attach them one to another.
|
||||
*
|
||||
* The flag attach_tracing_prog is set for the whole program lifecycle, and
|
||||
* doesn't have to be cleared in bpf_tracing_link_release, since tracing
|
||||
* programs cannot change attachment target.
|
||||
*/
|
||||
if (type == BPF_PROG_TYPE_TRACING && dst_prog &&
|
||||
dst_prog->type == BPF_PROG_TYPE_TRACING) {
|
||||
prog->aux->attach_tracing_prog = true;
|
||||
}
|
||||
|
||||
/* find program type: socket_filter vs tracing_filter */
|
||||
err = find_prog_type(type, prog);
|
||||
if (err < 0)
|
||||
@@ -3134,7 +3187,12 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
|
||||
}
|
||||
|
||||
if (tgt_prog_fd) {
|
||||
/* For now we only allow new targets for BPF_PROG_TYPE_EXT */
|
||||
/*
|
||||
* For now we only allow new targets for BPF_PROG_TYPE_EXT. If this
|
||||
* part would be changed to implement the same for
|
||||
* BPF_PROG_TYPE_TRACING, do not forget to update the way how
|
||||
* attach_tracing_prog flag is set.
|
||||
*/
|
||||
if (prog->type != BPF_PROG_TYPE_EXT) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
@@ -3179,6 +3237,10 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
|
||||
*
|
||||
* - if prog->aux->dst_trampoline and tgt_prog is NULL, the program
|
||||
* was detached and is going for re-attachment.
|
||||
*
|
||||
* - if prog->aux->dst_trampoline is NULL and tgt_prog and prog->aux->attach_btf
|
||||
* are NULL, then program was already attached and user did not provide
|
||||
* tgt_prog_fd so we have no way to find out or create trampoline
|
||||
*/
|
||||
if (!prog->aux->dst_trampoline && !tgt_prog) {
|
||||
/*
|
||||
@@ -3192,6 +3254,11 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
/* We can allow re-attach only if we have valid attach_btf. */
|
||||
if (!prog->aux->attach_btf) {
|
||||
err = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
btf_id = prog->aux->attach_btf_id;
|
||||
key = bpf_trampoline_compute_key(NULL, prog->aux->attach_btf, btf_id);
|
||||
}
|
||||
@@ -4919,8 +4986,10 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
|
||||
else
|
||||
BPF_DO_BATCH(map->ops->map_delete_batch, map, attr, uattr);
|
||||
err_put:
|
||||
if (has_write)
|
||||
if (has_write) {
|
||||
maybe_wait_bpf_programs(map);
|
||||
bpf_map_write_active_dec(map);
|
||||
}
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
@@ -5322,6 +5391,11 @@ static int bpf_prog_bind_map(union bpf_attr *attr)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* The bpf program will not access the bpf map, but for the sake of
|
||||
* simplicity, increase sleepable_refcnt for sleepable program as well.
|
||||
*/
|
||||
if (prog->aux->sleepable)
|
||||
atomic64_inc(&map->sleepable_refcnt);
|
||||
memcpy(used_maps_new, used_maps_old,
|
||||
sizeof(used_maps_old[0]) * prog->aux->used_map_cnt);
|
||||
used_maps_new[prog->aux->used_map_cnt] = map;
|
||||
|
||||
@@ -70,15 +70,13 @@ static struct task_struct *task_group_seq_get_next(struct bpf_iter_seq_task_comm
|
||||
return NULL;
|
||||
|
||||
retry:
|
||||
task = next_thread(task);
|
||||
task = __next_thread(task);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
next_tid = __task_pid_nr_ns(task, PIDTYPE_PID, common->ns);
|
||||
if (!next_tid || next_tid == common->pid) {
|
||||
/* Run out of tasks of a process. The tasks of a
|
||||
* thread_group are linked as circular linked list.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
if (!next_tid)
|
||||
goto retry;
|
||||
|
||||
if (skip_if_dup_files && task->files == task->group_leader->files)
|
||||
goto retry;
|
||||
@@ -980,7 +978,6 @@ __bpf_kfunc int bpf_iter_task_new(struct bpf_iter_task *it,
|
||||
BUILD_BUG_ON(__alignof__(struct bpf_iter_task_kern) !=
|
||||
__alignof__(struct bpf_iter_task));
|
||||
|
||||
kit->task = kit->pos = NULL;
|
||||
switch (flags) {
|
||||
case BPF_TASK_ITER_ALL_THREADS:
|
||||
case BPF_TASK_ITER_ALL_PROCS:
|
||||
@@ -1017,20 +1014,16 @@ __bpf_kfunc struct task_struct *bpf_iter_task_next(struct bpf_iter_task *it)
|
||||
if (flags == BPF_TASK_ITER_ALL_PROCS)
|
||||
goto get_next_task;
|
||||
|
||||
kit->pos = next_thread(kit->pos);
|
||||
if (kit->pos == kit->task) {
|
||||
if (flags == BPF_TASK_ITER_PROC_THREADS) {
|
||||
kit->pos = NULL;
|
||||
return pos;
|
||||
}
|
||||
} else
|
||||
kit->pos = __next_thread(kit->pos);
|
||||
if (kit->pos || flags == BPF_TASK_ITER_PROC_THREADS)
|
||||
return pos;
|
||||
|
||||
get_next_task:
|
||||
kit->pos = next_task(kit->pos);
|
||||
kit->task = kit->pos;
|
||||
if (kit->pos == &init_task)
|
||||
kit->task = next_task(kit->task);
|
||||
if (kit->task == &init_task)
|
||||
kit->pos = NULL;
|
||||
else
|
||||
kit->pos = kit->task;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -172,12 +172,6 @@ bool tnum_in(struct tnum a, struct tnum b)
|
||||
return a.value == b.value;
|
||||
}
|
||||
|
||||
int tnum_strn(char *str, size_t size, struct tnum a)
|
||||
{
|
||||
return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tnum_strn);
|
||||
|
||||
int tnum_sbin(char *str, size_t size, struct tnum a)
|
||||
{
|
||||
size_t n;
|
||||
@@ -208,7 +202,12 @@ struct tnum tnum_clear_subreg(struct tnum a)
|
||||
return tnum_lshift(tnum_rshift(a, 32), 32);
|
||||
}
|
||||
|
||||
struct tnum tnum_with_subreg(struct tnum reg, struct tnum subreg)
|
||||
{
|
||||
return tnum_or(tnum_clear_subreg(reg), tnum_subreg(subreg));
|
||||
}
|
||||
|
||||
struct tnum tnum_const_subreg(struct tnum a, u32 value)
|
||||
{
|
||||
return tnum_or(tnum_clear_subreg(a), tnum_const(value));
|
||||
return tnum_with_subreg(a, tnum_const(value));
|
||||
}
|
||||
|
||||
@@ -115,10 +115,10 @@ bool bpf_prog_has_trampoline(const struct bpf_prog *prog)
|
||||
(ptype == BPF_PROG_TYPE_LSM && eatype == BPF_LSM_MAC);
|
||||
}
|
||||
|
||||
void bpf_image_ksym_add(void *data, struct bpf_ksym *ksym)
|
||||
void bpf_image_ksym_add(void *data, unsigned int size, struct bpf_ksym *ksym)
|
||||
{
|
||||
ksym->start = (unsigned long) data;
|
||||
ksym->end = ksym->start + PAGE_SIZE;
|
||||
ksym->end = ksym->start + size;
|
||||
bpf_ksym_add(ksym);
|
||||
perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start,
|
||||
PAGE_SIZE, false, ksym->name);
|
||||
@@ -254,8 +254,8 @@ bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total, bool *ip_a
|
||||
static void bpf_tramp_image_free(struct bpf_tramp_image *im)
|
||||
{
|
||||
bpf_image_ksym_del(&im->ksym);
|
||||
bpf_jit_free_exec(im->image);
|
||||
bpf_jit_uncharge_modmem(PAGE_SIZE);
|
||||
arch_free_bpf_trampoline(im->image, im->size);
|
||||
bpf_jit_uncharge_modmem(im->size);
|
||||
percpu_ref_exit(&im->pcref);
|
||||
kfree_rcu(im, rcu);
|
||||
}
|
||||
@@ -349,7 +349,7 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im)
|
||||
call_rcu_tasks_trace(&im->rcu, __bpf_tramp_image_put_rcu_tasks);
|
||||
}
|
||||
|
||||
static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key)
|
||||
static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size)
|
||||
{
|
||||
struct bpf_tramp_image *im;
|
||||
struct bpf_ksym *ksym;
|
||||
@@ -360,15 +360,15 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key)
|
||||
if (!im)
|
||||
goto out;
|
||||
|
||||
err = bpf_jit_charge_modmem(PAGE_SIZE);
|
||||
err = bpf_jit_charge_modmem(size);
|
||||
if (err)
|
||||
goto out_free_im;
|
||||
im->size = size;
|
||||
|
||||
err = -ENOMEM;
|
||||
im->image = image = bpf_jit_alloc_exec(PAGE_SIZE);
|
||||
im->image = image = arch_alloc_bpf_trampoline(size);
|
||||
if (!image)
|
||||
goto out_uncharge;
|
||||
set_vm_flush_reset_perms(image);
|
||||
|
||||
err = percpu_ref_init(&im->pcref, __bpf_tramp_image_release, 0, GFP_KERNEL);
|
||||
if (err)
|
||||
@@ -377,13 +377,13 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key)
|
||||
ksym = &im->ksym;
|
||||
INIT_LIST_HEAD_RCU(&ksym->lnode);
|
||||
snprintf(ksym->name, KSYM_NAME_LEN, "bpf_trampoline_%llu", key);
|
||||
bpf_image_ksym_add(image, ksym);
|
||||
bpf_image_ksym_add(image, size, ksym);
|
||||
return im;
|
||||
|
||||
out_free_image:
|
||||
bpf_jit_free_exec(im->image);
|
||||
arch_free_bpf_trampoline(im->image, im->size);
|
||||
out_uncharge:
|
||||
bpf_jit_uncharge_modmem(PAGE_SIZE);
|
||||
bpf_jit_uncharge_modmem(size);
|
||||
out_free_im:
|
||||
kfree(im);
|
||||
out:
|
||||
@@ -396,7 +396,7 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
|
||||
struct bpf_tramp_links *tlinks;
|
||||
u32 orig_flags = tr->flags;
|
||||
bool ip_arg = false;
|
||||
int err, total;
|
||||
int err, total, size;
|
||||
|
||||
tlinks = bpf_trampoline_get_progs(tr, &total, &ip_arg);
|
||||
if (IS_ERR(tlinks))
|
||||
@@ -409,12 +409,6 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
|
||||
goto out;
|
||||
}
|
||||
|
||||
im = bpf_tramp_image_alloc(tr->key);
|
||||
if (IS_ERR(im)) {
|
||||
err = PTR_ERR(im);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* clear all bits except SHARE_IPMODIFY and TAIL_CALL_CTX */
|
||||
tr->flags &= (BPF_TRAMP_F_SHARE_IPMODIFY | BPF_TRAMP_F_TAIL_CALL_CTX);
|
||||
|
||||
@@ -438,13 +432,31 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
|
||||
tr->flags |= BPF_TRAMP_F_ORIG_STACK;
|
||||
#endif
|
||||
|
||||
err = arch_prepare_bpf_trampoline(im, im->image, im->image + PAGE_SIZE,
|
||||
size = arch_bpf_trampoline_size(&tr->func.model, tr->flags,
|
||||
tlinks, tr->func.addr);
|
||||
if (size < 0) {
|
||||
err = size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (size > PAGE_SIZE) {
|
||||
err = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
im = bpf_tramp_image_alloc(tr->key, size);
|
||||
if (IS_ERR(im)) {
|
||||
err = PTR_ERR(im);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = arch_prepare_bpf_trampoline(im, im->image, im->image + size,
|
||||
&tr->func.model, tr->flags, tlinks,
|
||||
tr->func.addr);
|
||||
if (err < 0)
|
||||
goto out_free;
|
||||
|
||||
set_memory_rox((long)im->image, 1);
|
||||
arch_protect_bpf_trampoline(im->image, im->size);
|
||||
|
||||
WARN_ON(tr->cur_image && total == 0);
|
||||
if (tr->cur_image)
|
||||
@@ -464,9 +476,8 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
|
||||
tr->fops->func = NULL;
|
||||
tr->fops->trampoline = 0;
|
||||
|
||||
/* reset im->image memory attr for arch_prepare_bpf_trampoline */
|
||||
set_memory_nx((long)im->image, 1);
|
||||
set_memory_rw((long)im->image, 1);
|
||||
/* free im memory and reallocate later */
|
||||
bpf_tramp_image_free(im);
|
||||
goto again;
|
||||
}
|
||||
#endif
|
||||
@@ -1032,10 +1043,50 @@ bpf_trampoline_exit_t bpf_trampoline_exit(const struct bpf_prog *prog)
|
||||
}
|
||||
|
||||
int __weak
|
||||
arch_prepare_bpf_trampoline(struct bpf_tramp_image *tr, void *image, void *image_end,
|
||||
arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *image_end,
|
||||
const struct btf_func_model *m, u32 flags,
|
||||
struct bpf_tramp_links *tlinks,
|
||||
void *orig_call)
|
||||
void *func_addr)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
void * __weak arch_alloc_bpf_trampoline(unsigned int size)
|
||||
{
|
||||
void *image;
|
||||
|
||||
if (WARN_ON_ONCE(size > PAGE_SIZE))
|
||||
return NULL;
|
||||
image = bpf_jit_alloc_exec(PAGE_SIZE);
|
||||
if (image)
|
||||
set_vm_flush_reset_perms(image);
|
||||
return image;
|
||||
}
|
||||
|
||||
void __weak arch_free_bpf_trampoline(void *image, unsigned int size)
|
||||
{
|
||||
WARN_ON_ONCE(size > PAGE_SIZE);
|
||||
/* bpf_jit_free_exec doesn't need "size", but
|
||||
* bpf_prog_pack_free() needs it.
|
||||
*/
|
||||
bpf_jit_free_exec(image);
|
||||
}
|
||||
|
||||
void __weak arch_protect_bpf_trampoline(void *image, unsigned int size)
|
||||
{
|
||||
WARN_ON_ONCE(size > PAGE_SIZE);
|
||||
set_memory_rox((long)image, 1);
|
||||
}
|
||||
|
||||
void __weak arch_unprotect_bpf_trampoline(void *image, unsigned int size)
|
||||
{
|
||||
WARN_ON_ONCE(size > PAGE_SIZE);
|
||||
set_memory_nx((long)image, 1);
|
||||
set_memory_rw((long)image, 1);
|
||||
}
|
||||
|
||||
int __weak arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
|
||||
struct bpf_tramp_links *tlinks, void *func_addr)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@
|
||||
#include <linux/key.h>
|
||||
#include <linux/verification.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/fileattr.h>
|
||||
|
||||
#include <net/bpf_sk_storage.h>
|
||||
|
||||
@@ -41,6 +42,9 @@
|
||||
#define bpf_event_rcu_dereference(p) \
|
||||
rcu_dereference_protected(p, lockdep_is_held(&bpf_event_mutex))
|
||||
|
||||
#define MAX_UPROBE_MULTI_CNT (1U << 20)
|
||||
#define MAX_KPROBE_MULTI_CNT (1U << 20)
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
struct bpf_trace_module {
|
||||
struct module *module;
|
||||
@@ -1376,6 +1380,8 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
|
||||
struct bpf_dynptr_kern *sig_ptr,
|
||||
struct bpf_key *trusted_keyring)
|
||||
{
|
||||
const void *data, *sig;
|
||||
u32 data_len, sig_len;
|
||||
int ret;
|
||||
|
||||
if (trusted_keyring->has_ref) {
|
||||
@@ -1392,10 +1398,12 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
return verify_pkcs7_signature(data_ptr->data,
|
||||
__bpf_dynptr_size(data_ptr),
|
||||
sig_ptr->data,
|
||||
__bpf_dynptr_size(sig_ptr),
|
||||
data_len = __bpf_dynptr_size(data_ptr);
|
||||
data = __bpf_dynptr_data(data_ptr, data_len);
|
||||
sig_len = __bpf_dynptr_size(sig_ptr);
|
||||
sig = __bpf_dynptr_data(sig_ptr, sig_len);
|
||||
|
||||
return verify_pkcs7_signature(data, data_len, sig, sig_len,
|
||||
trusted_keyring->key,
|
||||
VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
|
||||
NULL);
|
||||
@@ -1427,6 +1435,72 @@ static int __init bpf_key_sig_kfuncs_init(void)
|
||||
late_initcall(bpf_key_sig_kfuncs_init);
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
/* filesystem kfuncs */
|
||||
__bpf_kfunc_start_defs();
|
||||
|
||||
/**
|
||||
* bpf_get_file_xattr - get xattr of a file
|
||||
* @file: file to get xattr from
|
||||
* @name__str: name of the xattr
|
||||
* @value_ptr: output buffer of the xattr value
|
||||
*
|
||||
* Get xattr *name__str* of *file* and store the output in *value_ptr*.
|
||||
*
|
||||
* For security reasons, only *name__str* with prefix "user." is allowed.
|
||||
*
|
||||
* Return: 0 on success, a negative value on error.
|
||||
*/
|
||||
__bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str,
|
||||
struct bpf_dynptr_kern *value_ptr)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
u32 value_len;
|
||||
void *value;
|
||||
int ret;
|
||||
|
||||
if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
|
||||
return -EPERM;
|
||||
|
||||
value_len = __bpf_dynptr_size(value_ptr);
|
||||
value = __bpf_dynptr_data_rw(value_ptr, value_len);
|
||||
if (!value)
|
||||
return -EINVAL;
|
||||
|
||||
dentry = file_dentry(file);
|
||||
ret = inode_permission(&nop_mnt_idmap, dentry->d_inode, MAY_READ);
|
||||
if (ret)
|
||||
return ret;
|
||||
return __vfs_getxattr(dentry, dentry->d_inode, name__str, value, value_len);
|
||||
}
|
||||
|
||||
__bpf_kfunc_end_defs();
|
||||
|
||||
BTF_SET8_START(fs_kfunc_set_ids)
|
||||
BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
|
||||
BTF_SET8_END(fs_kfunc_set_ids)
|
||||
|
||||
static int bpf_get_file_xattr_filter(const struct bpf_prog *prog, u32 kfunc_id)
|
||||
{
|
||||
if (!btf_id_set8_contains(&fs_kfunc_set_ids, kfunc_id))
|
||||
return 0;
|
||||
|
||||
/* Only allow to attach from LSM hooks, to avoid recursion */
|
||||
return prog->type != BPF_PROG_TYPE_LSM ? -EACCES : 0;
|
||||
}
|
||||
|
||||
static const struct btf_kfunc_id_set bpf_fs_kfunc_set = {
|
||||
.owner = THIS_MODULE,
|
||||
.set = &fs_kfunc_set_ids,
|
||||
.filter = bpf_get_file_xattr_filter,
|
||||
};
|
||||
|
||||
static int __init bpf_fs_kfuncs_init(void)
|
||||
{
|
||||
return register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fs_kfunc_set);
|
||||
}
|
||||
|
||||
late_initcall(bpf_fs_kfuncs_init);
|
||||
|
||||
static const struct bpf_func_proto *
|
||||
bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
{
|
||||
@@ -2899,6 +2973,8 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
cnt = attr->link_create.kprobe_multi.cnt;
|
||||
if (!cnt)
|
||||
return -EINVAL;
|
||||
if (cnt > MAX_KPROBE_MULTI_CNT)
|
||||
return -E2BIG;
|
||||
|
||||
size = cnt * sizeof(*addrs);
|
||||
addrs = kvmalloc_array(cnt, sizeof(*addrs), GFP_KERNEL);
|
||||
@@ -3029,6 +3105,7 @@ struct bpf_uprobe_multi_link;
|
||||
struct bpf_uprobe {
|
||||
struct bpf_uprobe_multi_link *link;
|
||||
loff_t offset;
|
||||
unsigned long ref_ctr_offset;
|
||||
u64 cookie;
|
||||
struct uprobe_consumer consumer;
|
||||
};
|
||||
@@ -3037,6 +3114,7 @@ struct bpf_uprobe_multi_link {
|
||||
struct path path;
|
||||
struct bpf_link link;
|
||||
u32 cnt;
|
||||
u32 flags;
|
||||
struct bpf_uprobe *uprobes;
|
||||
struct task_struct *task;
|
||||
};
|
||||
@@ -3078,9 +3156,79 @@ static void bpf_uprobe_multi_link_dealloc(struct bpf_link *link)
|
||||
kfree(umulti_link);
|
||||
}
|
||||
|
||||
static int bpf_uprobe_multi_link_fill_link_info(const struct bpf_link *link,
|
||||
struct bpf_link_info *info)
|
||||
{
|
||||
u64 __user *uref_ctr_offsets = u64_to_user_ptr(info->uprobe_multi.ref_ctr_offsets);
|
||||
u64 __user *ucookies = u64_to_user_ptr(info->uprobe_multi.cookies);
|
||||
u64 __user *uoffsets = u64_to_user_ptr(info->uprobe_multi.offsets);
|
||||
u64 __user *upath = u64_to_user_ptr(info->uprobe_multi.path);
|
||||
u32 upath_size = info->uprobe_multi.path_size;
|
||||
struct bpf_uprobe_multi_link *umulti_link;
|
||||
u32 ucount = info->uprobe_multi.count;
|
||||
int err = 0, i;
|
||||
long left;
|
||||
|
||||
if (!upath ^ !upath_size)
|
||||
return -EINVAL;
|
||||
|
||||
if ((uoffsets || uref_ctr_offsets || ucookies) && !ucount)
|
||||
return -EINVAL;
|
||||
|
||||
umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
|
||||
info->uprobe_multi.count = umulti_link->cnt;
|
||||
info->uprobe_multi.flags = umulti_link->flags;
|
||||
info->uprobe_multi.pid = umulti_link->task ?
|
||||
task_pid_nr_ns(umulti_link->task, task_active_pid_ns(current)) : 0;
|
||||
|
||||
if (upath) {
|
||||
char *p, *buf;
|
||||
|
||||
upath_size = min_t(u32, upath_size, PATH_MAX);
|
||||
|
||||
buf = kmalloc(upath_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
p = d_path(&umulti_link->path, buf, upath_size);
|
||||
if (IS_ERR(p)) {
|
||||
kfree(buf);
|
||||
return PTR_ERR(p);
|
||||
}
|
||||
upath_size = buf + upath_size - p;
|
||||
left = copy_to_user(upath, p, upath_size);
|
||||
kfree(buf);
|
||||
if (left)
|
||||
return -EFAULT;
|
||||
info->uprobe_multi.path_size = upath_size;
|
||||
}
|
||||
|
||||
if (!uoffsets && !ucookies && !uref_ctr_offsets)
|
||||
return 0;
|
||||
|
||||
if (ucount < umulti_link->cnt)
|
||||
err = -ENOSPC;
|
||||
else
|
||||
ucount = umulti_link->cnt;
|
||||
|
||||
for (i = 0; i < ucount; i++) {
|
||||
if (uoffsets &&
|
||||
put_user(umulti_link->uprobes[i].offset, uoffsets + i))
|
||||
return -EFAULT;
|
||||
if (uref_ctr_offsets &&
|
||||
put_user(umulti_link->uprobes[i].ref_ctr_offset, uref_ctr_offsets + i))
|
||||
return -EFAULT;
|
||||
if (ucookies &&
|
||||
put_user(umulti_link->uprobes[i].cookie, ucookies + i))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_uprobe_multi_link_lops = {
|
||||
.release = bpf_uprobe_multi_link_release,
|
||||
.dealloc = bpf_uprobe_multi_link_dealloc,
|
||||
.fill_link_info = bpf_uprobe_multi_link_fill_link_info,
|
||||
};
|
||||
|
||||
static int uprobe_prog_run(struct bpf_uprobe *uprobe,
|
||||
@@ -3168,7 +3316,6 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
{
|
||||
struct bpf_uprobe_multi_link *link = NULL;
|
||||
unsigned long __user *uref_ctr_offsets;
|
||||
unsigned long *ref_ctr_offsets = NULL;
|
||||
struct bpf_link_primer link_primer;
|
||||
struct bpf_uprobe *uprobes = NULL;
|
||||
struct task_struct *task = NULL;
|
||||
@@ -3202,6 +3349,8 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
|
||||
if (!upath || !uoffsets || !cnt)
|
||||
return -EINVAL;
|
||||
if (cnt > MAX_UPROBE_MULTI_CNT)
|
||||
return -E2BIG;
|
||||
|
||||
uref_ctr_offsets = u64_to_user_ptr(attr->link_create.uprobe_multi.ref_ctr_offsets);
|
||||
ucookies = u64_to_user_ptr(attr->link_create.uprobe_multi.cookies);
|
||||
@@ -3241,25 +3390,23 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
if (!uprobes || !link)
|
||||
goto error_free;
|
||||
|
||||
if (uref_ctr_offsets) {
|
||||
ref_ctr_offsets = kvcalloc(cnt, sizeof(*ref_ctr_offsets), GFP_KERNEL);
|
||||
if (!ref_ctr_offsets)
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
if (ucookies && __get_user(uprobes[i].cookie, ucookies + i)) {
|
||||
err = -EFAULT;
|
||||
goto error_free;
|
||||
}
|
||||
if (uref_ctr_offsets && __get_user(ref_ctr_offsets[i], uref_ctr_offsets + i)) {
|
||||
err = -EFAULT;
|
||||
goto error_free;
|
||||
}
|
||||
if (__get_user(uprobes[i].offset, uoffsets + i)) {
|
||||
err = -EFAULT;
|
||||
goto error_free;
|
||||
}
|
||||
if (uprobes[i].offset < 0) {
|
||||
err = -EINVAL;
|
||||
goto error_free;
|
||||
}
|
||||
if (uref_ctr_offsets && __get_user(uprobes[i].ref_ctr_offset, uref_ctr_offsets + i)) {
|
||||
err = -EFAULT;
|
||||
goto error_free;
|
||||
}
|
||||
if (ucookies && __get_user(uprobes[i].cookie, ucookies + i)) {
|
||||
err = -EFAULT;
|
||||
goto error_free;
|
||||
}
|
||||
|
||||
uprobes[i].link = link;
|
||||
|
||||
@@ -3276,6 +3423,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
link->uprobes = uprobes;
|
||||
link->path = path;
|
||||
link->task = task;
|
||||
link->flags = flags;
|
||||
|
||||
bpf_link_init(&link->link, BPF_LINK_TYPE_UPROBE_MULTI,
|
||||
&bpf_uprobe_multi_link_lops, prog);
|
||||
@@ -3283,7 +3431,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
for (i = 0; i < cnt; i++) {
|
||||
err = uprobe_register_refctr(d_real_inode(link->path.dentry),
|
||||
uprobes[i].offset,
|
||||
ref_ctr_offsets ? ref_ctr_offsets[i] : 0,
|
||||
uprobes[i].ref_ctr_offset,
|
||||
&uprobes[i].consumer);
|
||||
if (err) {
|
||||
bpf_uprobe_unregister(&path, uprobes, i);
|
||||
@@ -3295,11 +3443,9 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
|
||||
if (err)
|
||||
goto error_free;
|
||||
|
||||
kvfree(ref_ctr_offsets);
|
||||
return bpf_link_settle(&link_primer);
|
||||
|
||||
error_free:
|
||||
kvfree(ref_ctr_offsets);
|
||||
kvfree(uprobes);
|
||||
kfree(link);
|
||||
if (task)
|
||||
|
||||
Reference in New Issue
Block a user