mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Alexei Starovoitov says: ==================== pull-request: bpf-next 2019-11-02 The following pull-request contains BPF updates for your *net-next* tree. We've added 30 non-merge commits during the last 7 day(s) which contain a total of 41 files changed, 1864 insertions(+), 474 deletions(-). The main changes are: 1) Fix long standing user vs kernel access issue by introducing bpf_probe_read_user() and bpf_probe_read_kernel() helpers, from Daniel. 2) Accelerated xskmap lookup, from Björn and Maciej. 3) Support for automatic map pinning in libbpf, from Toke. 4) Cleanup of BTF-enabled raw tracepoints, from Alexei. 5) Various fixes to libbpf and selftests. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -47,6 +47,15 @@ Program types
|
||||
prog_flow_dissector
|
||||
|
||||
|
||||
Testing BPF
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
s390
|
||||
|
||||
|
||||
.. Links:
|
||||
.. _Documentation/networking/filter.txt: ../networking/filter.txt
|
||||
.. _man-pages: https://www.kernel.org/doc/man-pages/
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
===================
|
||||
Testing BPF on s390
|
||||
===================
|
||||
|
||||
1. Introduction
|
||||
***************
|
||||
|
||||
IBM Z are mainframe computers, which are descendants of IBM System/360 from
|
||||
year 1964. They are supported by the Linux kernel under the name "s390". This
|
||||
document describes how to test BPF in an s390 QEMU guest.
|
||||
|
||||
2. One-time setup
|
||||
*****************
|
||||
|
||||
The following is required to build and run the test suite:
|
||||
|
||||
* s390 GCC
|
||||
* s390 development headers and libraries
|
||||
* Clang with BPF support
|
||||
* QEMU with s390 support
|
||||
* Disk image with s390 rootfs
|
||||
|
||||
Debian supports installing compiler and libraries for s390 out of the box.
|
||||
Users of other distros may use debootstrap in order to set up a Debian chroot::
|
||||
|
||||
sudo debootstrap \
|
||||
--variant=minbase \
|
||||
--include=sudo \
|
||||
testing \
|
||||
./s390-toolchain
|
||||
sudo mount --rbind /dev ./s390-toolchain/dev
|
||||
sudo mount --rbind /proc ./s390-toolchain/proc
|
||||
sudo mount --rbind /sys ./s390-toolchain/sys
|
||||
sudo chroot ./s390-toolchain
|
||||
|
||||
Once on Debian, the build prerequisites can be installed as follows::
|
||||
|
||||
sudo dpkg --add-architecture s390x
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
bc \
|
||||
bison \
|
||||
cmake \
|
||||
debootstrap \
|
||||
dwarves \
|
||||
flex \
|
||||
g++ \
|
||||
gcc \
|
||||
g++-s390x-linux-gnu \
|
||||
gcc-s390x-linux-gnu \
|
||||
gdb-multiarch \
|
||||
git \
|
||||
make \
|
||||
python3 \
|
||||
qemu-system-misc \
|
||||
qemu-utils \
|
||||
rsync \
|
||||
libcap-dev:s390x \
|
||||
libelf-dev:s390x \
|
||||
libncurses-dev
|
||||
|
||||
Latest Clang targeting BPF can be installed as follows::
|
||||
|
||||
git clone https://github.com/llvm/llvm-project.git
|
||||
ln -s ../../clang llvm-project/llvm/tools/
|
||||
mkdir llvm-project-build
|
||||
cd llvm-project-build
|
||||
cmake \
|
||||
-DLLVM_TARGETS_TO_BUILD=BPF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/opt/clang-bpf \
|
||||
../llvm-project/llvm
|
||||
make
|
||||
sudo make install
|
||||
export PATH=/opt/clang-bpf/bin:$PATH
|
||||
|
||||
The disk image can be prepared using a loopback mount and debootstrap::
|
||||
|
||||
qemu-img create -f raw ./s390.img 1G
|
||||
sudo losetup -f ./s390.img
|
||||
sudo mkfs.ext4 /dev/loopX
|
||||
mkdir ./s390.rootfs
|
||||
sudo mount /dev/loopX ./s390.rootfs
|
||||
sudo debootstrap \
|
||||
--foreign \
|
||||
--arch=s390x \
|
||||
--variant=minbase \
|
||||
--include=" \
|
||||
iproute2, \
|
||||
iputils-ping, \
|
||||
isc-dhcp-client, \
|
||||
kmod, \
|
||||
libcap2, \
|
||||
libelf1, \
|
||||
netcat, \
|
||||
procps" \
|
||||
testing \
|
||||
./s390.rootfs
|
||||
sudo umount ./s390.rootfs
|
||||
sudo losetup -d /dev/loopX
|
||||
|
||||
3. Compilation
|
||||
**************
|
||||
|
||||
In addition to the usual Kconfig options required to run the BPF test suite, it
|
||||
is also helpful to select::
|
||||
|
||||
CONFIG_NET_9P=y
|
||||
CONFIG_9P_FS=y
|
||||
CONFIG_NET_9P_VIRTIO=y
|
||||
CONFIG_VIRTIO_PCI=y
|
||||
|
||||
as that would enable a very easy way to share files with the s390 virtual
|
||||
machine.
|
||||
|
||||
Compiling kernel, modules and testsuite, as well as preparing gdb scripts to
|
||||
simplify debugging, can be done using the following commands::
|
||||
|
||||
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- menuconfig
|
||||
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- bzImage modules scripts_gdb
|
||||
make ARCH=s390 CROSS_COMPILE=s390x-linux-gnu- \
|
||||
-C tools/testing/selftests \
|
||||
TARGETS=bpf \
|
||||
INSTALL_PATH=$PWD/tools/testing/selftests/kselftest_install \
|
||||
install
|
||||
|
||||
4. Running the test suite
|
||||
*************************
|
||||
|
||||
The virtual machine can be started as follows::
|
||||
|
||||
qemu-system-s390x \
|
||||
-cpu max,zpci=on \
|
||||
-smp 2 \
|
||||
-m 4G \
|
||||
-kernel linux/arch/s390/boot/compressed/vmlinux \
|
||||
-drive file=./s390.img,if=virtio,format=raw \
|
||||
-nographic \
|
||||
-append 'root=/dev/vda rw console=ttyS1' \
|
||||
-virtfs local,path=./linux,security_model=none,mount_tag=linux \
|
||||
-object rng-random,filename=/dev/urandom,id=rng0 \
|
||||
-device virtio-rng-ccw,rng=rng0 \
|
||||
-netdev user,id=net0 \
|
||||
-device virtio-net-ccw,netdev=net0
|
||||
|
||||
When using this on a real IBM Z, ``-enable-kvm`` may be added for better
|
||||
performance. When starting the virtual machine for the first time, disk image
|
||||
setup must be finalized using the following command::
|
||||
|
||||
/debootstrap/debootstrap --second-stage
|
||||
|
||||
Directory with the code built on the host as well as ``/proc`` and ``/sys``
|
||||
need to be mounted as follows::
|
||||
|
||||
mkdir -p /linux
|
||||
mount -t 9p linux /linux
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sys /sys
|
||||
|
||||
After that, the test suite can be run using the following commands::
|
||||
|
||||
cd /linux/tools/testing/selftests/kselftest_install
|
||||
./run_kselftest.sh
|
||||
|
||||
As usual, tests can be also run individually::
|
||||
|
||||
cd /linux/tools/testing/selftests/bpf
|
||||
./test_verifier
|
||||
|
||||
5. Debugging
|
||||
************
|
||||
|
||||
It is possible to debug the s390 kernel using QEMU GDB stub, which is activated
|
||||
by passing ``-s`` to QEMU.
|
||||
|
||||
It is preferable to turn KASLR off, so that gdb would know where to find the
|
||||
kernel image in memory, by building the kernel with::
|
||||
|
||||
RANDOMIZE_BASE=n
|
||||
|
||||
GDB can then be attached using the following command::
|
||||
|
||||
gdb-multiarch -ex 'target remote localhost:1234' ./vmlinux
|
||||
|
||||
6. Network
|
||||
**********
|
||||
|
||||
In case one needs to use the network in the virtual machine in order to e.g.
|
||||
install additional packages, it can be configured using::
|
||||
|
||||
dhclient eth0
|
||||
|
||||
7. Links
|
||||
********
|
||||
|
||||
This document is a compilation of techniques, whose more comprehensive
|
||||
descriptions can be found by following these links:
|
||||
|
||||
- `Debootstrap <https://wiki.debian.org/EmDebian/CrossDebootstrap>`_
|
||||
- `Multiarch <https://wiki.debian.org/Multiarch/HOWTO>`_
|
||||
- `Building LLVM <https://llvm.org/docs/CMake.html>`_
|
||||
- `Cross-compiling the kernel <https://wiki.gentoo.org/wiki/Embedded_Handbook/General/Cross-compiling_the_kernel>`_
|
||||
- `QEMU s390x Guest Support <https://wiki.qemu.org/Documentation/Platforms/S390X>`_
|
||||
- `Plan 9 folder sharing over Virtio <https://wiki.qemu.org/Documentation/9psetup>`_
|
||||
- `Using GDB with QEMU <https://wiki.osdev.org/Kernel_Debugging#Use_GDB_with_QEMU>`_
|
||||
@@ -13,7 +13,7 @@ CFLAGS_REMOVE_mem_encrypt_identity.o = -pg
|
||||
endif
|
||||
|
||||
obj-y := init.o init_$(BITS).o fault.o ioremap.o extable.o pageattr.o mmap.o \
|
||||
pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o
|
||||
pat.o pgtable.o physaddr.o setup_nx.o tlb.o cpu_entry_area.o maccess.o
|
||||
|
||||
# Make sure __phys_addr has no stackprotector
|
||||
nostackp := $(call cc-option, -fno-stack-protector)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
static __always_inline u64 canonical_address(u64 vaddr, u8 vaddr_bits)
|
||||
{
|
||||
return ((s64)vaddr << (64 - vaddr_bits)) >> (64 - vaddr_bits);
|
||||
}
|
||||
|
||||
static __always_inline bool invalid_probe_range(u64 vaddr)
|
||||
{
|
||||
/*
|
||||
* Range covering the highest possible canonical userspace address
|
||||
* as well as non-canonical address range. For the canonical range
|
||||
* we also need to include the userspace guard page.
|
||||
*/
|
||||
return vaddr < TASK_SIZE_MAX + PAGE_SIZE ||
|
||||
canonical_address(vaddr, boot_cpu_data.x86_virt_bits) != vaddr;
|
||||
}
|
||||
#else
|
||||
static __always_inline bool invalid_probe_range(u64 vaddr)
|
||||
{
|
||||
return vaddr < TASK_SIZE_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
long probe_kernel_read_strict(void *dst, const void *src, size_t size)
|
||||
{
|
||||
if (unlikely(invalid_probe_range((unsigned long)src)))
|
||||
return -EFAULT;
|
||||
|
||||
return __probe_kernel_read(dst, src, size);
|
||||
}
|
||||
|
||||
long strncpy_from_unsafe_strict(char *dst, const void *unsafe_addr, long count)
|
||||
{
|
||||
if (unlikely(invalid_probe_range((unsigned long)unsafe_addr)))
|
||||
return -EFAULT;
|
||||
|
||||
return __strncpy_from_unsafe(dst, unsafe_addr, count);
|
||||
}
|
||||
+5
-25
@@ -373,6 +373,11 @@ enum bpf_cgroup_storage_type {
|
||||
|
||||
#define MAX_BPF_CGROUP_STORAGE_TYPE __BPF_CGROUP_STORAGE_MAX
|
||||
|
||||
/* The longest tracepoint has 12 args.
|
||||
* See include/trace/bpf_probe.h
|
||||
*/
|
||||
#define MAX_BPF_FUNC_ARGS 12
|
||||
|
||||
struct bpf_prog_stats {
|
||||
u64 cnt;
|
||||
u64 nsecs;
|
||||
@@ -1004,31 +1009,6 @@ static inline int sock_map_get_from_fd(const union bpf_attr *attr,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_XDP_SOCKETS)
|
||||
struct xdp_sock;
|
||||
struct xdp_sock *__xsk_map_lookup_elem(struct bpf_map *map, u32 key);
|
||||
int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp,
|
||||
struct xdp_sock *xs);
|
||||
void __xsk_map_flush(struct bpf_map *map);
|
||||
#else
|
||||
struct xdp_sock;
|
||||
static inline struct xdp_sock *__xsk_map_lookup_elem(struct bpf_map *map,
|
||||
u32 key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp,
|
||||
struct xdp_sock *xs)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void __xsk_map_flush(struct bpf_map *map)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL)
|
||||
void bpf_sk_reuseport_detach(struct sock *sk);
|
||||
int bpf_fd_reuseport_array_lookup_elem(struct bpf_map *map, void *key,
|
||||
|
||||
@@ -26,6 +26,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint)
|
||||
BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event)
|
||||
BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
|
||||
BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, raw_tracepoint_writable)
|
||||
BPF_PROG_TYPE(BPF_PROG_TYPE_TRACING, tracing)
|
||||
#endif
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
|
||||
|
||||
@@ -311,6 +311,7 @@ copy_struct_from_user(void *dst, size_t ksize, const void __user *src,
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
extern long probe_kernel_read(void *dst, const void *src, size_t size);
|
||||
extern long probe_kernel_read_strict(void *dst, const void *src, size_t size);
|
||||
extern long __probe_kernel_read(void *dst, const void *src, size_t size);
|
||||
|
||||
/*
|
||||
@@ -337,7 +338,22 @@ extern long __probe_user_read(void *dst, const void __user *src, size_t size);
|
||||
extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
|
||||
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
|
||||
|
||||
/*
|
||||
* probe_user_write(): safely attempt to write to a location in user space
|
||||
* @dst: address to write to
|
||||
* @src: pointer to the data that shall be written
|
||||
* @size: size of the data chunk
|
||||
*
|
||||
* Safely write to address @dst from the buffer at @src. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
extern long notrace probe_user_write(void __user *dst, const void *src, size_t size);
|
||||
extern long notrace __probe_user_write(void __user *dst, const void *src, size_t size);
|
||||
|
||||
extern long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
|
||||
extern long strncpy_from_unsafe_strict(char *dst, const void *unsafe_addr,
|
||||
long count);
|
||||
extern long __strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count);
|
||||
extern long strncpy_from_unsafe_user(char *dst, const void __user *unsafe_addr,
|
||||
long count);
|
||||
extern long strnlen_unsafe_user(const void __user *unsafe_addr, long count);
|
||||
|
||||
+39
-12
@@ -69,7 +69,14 @@ struct xdp_umem {
|
||||
/* Nodes are linked in the struct xdp_sock map_list field, and used to
|
||||
* track which maps a certain socket reside in.
|
||||
*/
|
||||
struct xsk_map;
|
||||
|
||||
struct xsk_map {
|
||||
struct bpf_map map;
|
||||
struct list_head __percpu *flush_list;
|
||||
spinlock_t lock; /* Synchronize map updates */
|
||||
struct xdp_sock *xsk_map[];
|
||||
};
|
||||
|
||||
struct xsk_map_node {
|
||||
struct list_head node;
|
||||
struct xsk_map *map;
|
||||
@@ -109,8 +116,6 @@ struct xdp_sock {
|
||||
struct xdp_buff;
|
||||
#ifdef CONFIG_XDP_SOCKETS
|
||||
int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp);
|
||||
int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp);
|
||||
void xsk_flush(struct xdp_sock *xs);
|
||||
bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs);
|
||||
/* Used from netdev driver */
|
||||
bool xsk_umem_has_addrs(struct xdp_umem *umem, u32 cnt);
|
||||
@@ -134,6 +139,22 @@ void xsk_map_try_sock_delete(struct xsk_map *map, struct xdp_sock *xs,
|
||||
struct xdp_sock **map_entry);
|
||||
int xsk_map_inc(struct xsk_map *map);
|
||||
void xsk_map_put(struct xsk_map *map);
|
||||
int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp,
|
||||
struct xdp_sock *xs);
|
||||
void __xsk_map_flush(struct bpf_map *map);
|
||||
|
||||
static inline struct xdp_sock *__xsk_map_lookup_elem(struct bpf_map *map,
|
||||
u32 key)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct xdp_sock *xs;
|
||||
|
||||
if (key >= map->max_entries)
|
||||
return NULL;
|
||||
|
||||
xs = READ_ONCE(m->xsk_map[key]);
|
||||
return xs;
|
||||
}
|
||||
|
||||
static inline u64 xsk_umem_extract_addr(u64 addr)
|
||||
{
|
||||
@@ -224,15 +245,6 @@ static inline int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline void xsk_flush(struct xdp_sock *xs)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs)
|
||||
{
|
||||
return false;
|
||||
@@ -357,6 +369,21 @@ static inline u64 xsk_umem_adjust_offset(struct xdp_umem *umem, u64 handle,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp,
|
||||
struct xdp_sock *xs)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void __xsk_map_flush(struct bpf_map *map)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct xdp_sock *__xsk_map_lookup_elem(struct bpf_map *map,
|
||||
u32 key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_XDP_SOCKETS */
|
||||
|
||||
#endif /* _LINUX_XDP_SOCK_H */
|
||||
|
||||
+84
-40
@@ -173,6 +173,7 @@ enum bpf_prog_type {
|
||||
BPF_PROG_TYPE_CGROUP_SYSCTL,
|
||||
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
|
||||
BPF_PROG_TYPE_CGROUP_SOCKOPT,
|
||||
BPF_PROG_TYPE_TRACING,
|
||||
};
|
||||
|
||||
enum bpf_attach_type {
|
||||
@@ -199,6 +200,7 @@ enum bpf_attach_type {
|
||||
BPF_CGROUP_UDP6_RECVMSG,
|
||||
BPF_CGROUP_GETSOCKOPT,
|
||||
BPF_CGROUP_SETSOCKOPT,
|
||||
BPF_TRACE_RAW_TP,
|
||||
__MAX_BPF_ATTACH_TYPE
|
||||
};
|
||||
|
||||
@@ -561,10 +563,13 @@ union bpf_attr {
|
||||
* Return
|
||||
* 0 on success, or a negative error in case of failure.
|
||||
*
|
||||
* int bpf_probe_read(void *dst, u32 size, const void *src)
|
||||
* int bpf_probe_read(void *dst, u32 size, const void *unsafe_ptr)
|
||||
* Description
|
||||
* For tracing programs, safely attempt to read *size* bytes from
|
||||
* address *src* and store the data in *dst*.
|
||||
* kernel space address *unsafe_ptr* and store the data in *dst*.
|
||||
*
|
||||
* Generally, use bpf_probe_read_user() or bpf_probe_read_kernel()
|
||||
* instead.
|
||||
* Return
|
||||
* 0 on success, or a negative error in case of failure.
|
||||
*
|
||||
@@ -1426,45 +1431,14 @@ union bpf_attr {
|
||||
* Return
|
||||
* 0 on success, or a negative error in case of failure.
|
||||
*
|
||||
* int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr)
|
||||
* int bpf_probe_read_str(void *dst, u32 size, const void *unsafe_ptr)
|
||||
* Description
|
||||
* Copy a NUL terminated string from an unsafe address
|
||||
* *unsafe_ptr* to *dst*. The *size* should include the
|
||||
* terminating NUL byte. In case the string length is smaller than
|
||||
* *size*, the target is not padded with further NUL bytes. If the
|
||||
* string length is larger than *size*, just *size*-1 bytes are
|
||||
* copied and the last byte is set to NUL.
|
||||
* Copy a NUL terminated string from an unsafe kernel address
|
||||
* *unsafe_ptr* to *dst*. See bpf_probe_read_kernel_str() for
|
||||
* more details.
|
||||
*
|
||||
* On success, the length of the copied string is returned. This
|
||||
* makes this helper useful in tracing programs for reading
|
||||
* strings, and more importantly to get its length at runtime. See
|
||||
* the following snippet:
|
||||
*
|
||||
* ::
|
||||
*
|
||||
* SEC("kprobe/sys_open")
|
||||
* void bpf_sys_open(struct pt_regs *ctx)
|
||||
* {
|
||||
* char buf[PATHLEN]; // PATHLEN is defined to 256
|
||||
* int res = bpf_probe_read_str(buf, sizeof(buf),
|
||||
* ctx->di);
|
||||
*
|
||||
* // Consume buf, for example push it to
|
||||
* // userspace via bpf_perf_event_output(); we
|
||||
* // can use res (the string length) as event
|
||||
* // size, after checking its boundaries.
|
||||
* }
|
||||
*
|
||||
* In comparison, using **bpf_probe_read()** helper here instead
|
||||
* to read the string would require to estimate the length at
|
||||
* compile time, and would often result in copying more memory
|
||||
* than necessary.
|
||||
*
|
||||
* Another useful use case is when parsing individual process
|
||||
* arguments or individual environment variables navigating
|
||||
* *current*\ **->mm->arg_start** and *current*\
|
||||
* **->mm->env_start**: using this helper and the return value,
|
||||
* one can quickly iterate at the right offset of the memory area.
|
||||
* Generally, use bpf_probe_read_user_str() or bpf_probe_read_kernel_str()
|
||||
* instead.
|
||||
* Return
|
||||
* On success, the strictly positive length of the string,
|
||||
* including the trailing NUL character. On error, a negative
|
||||
@@ -2775,6 +2749,72 @@ union bpf_attr {
|
||||
* restricted to raw_tracepoint bpf programs.
|
||||
* Return
|
||||
* 0 on success, or a negative error in case of failure.
|
||||
*
|
||||
* int bpf_probe_read_user(void *dst, u32 size, const void *unsafe_ptr)
|
||||
* Description
|
||||
* Safely attempt to read *size* bytes from user space address
|
||||
* *unsafe_ptr* and store the data in *dst*.
|
||||
* Return
|
||||
* 0 on success, or a negative error in case of failure.
|
||||
*
|
||||
* int bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr)
|
||||
* Description
|
||||
* Safely attempt to read *size* bytes from kernel space address
|
||||
* *unsafe_ptr* and store the data in *dst*.
|
||||
* Return
|
||||
* 0 on success, or a negative error in case of failure.
|
||||
*
|
||||
* int bpf_probe_read_user_str(void *dst, u32 size, const void *unsafe_ptr)
|
||||
* Description
|
||||
* Copy a NUL terminated string from an unsafe user address
|
||||
* *unsafe_ptr* to *dst*. The *size* should include the
|
||||
* terminating NUL byte. In case the string length is smaller than
|
||||
* *size*, the target is not padded with further NUL bytes. If the
|
||||
* string length is larger than *size*, just *size*-1 bytes are
|
||||
* copied and the last byte is set to NUL.
|
||||
*
|
||||
* On success, the length of the copied string is returned. This
|
||||
* makes this helper useful in tracing programs for reading
|
||||
* strings, and more importantly to get its length at runtime. See
|
||||
* the following snippet:
|
||||
*
|
||||
* ::
|
||||
*
|
||||
* SEC("kprobe/sys_open")
|
||||
* void bpf_sys_open(struct pt_regs *ctx)
|
||||
* {
|
||||
* char buf[PATHLEN]; // PATHLEN is defined to 256
|
||||
* int res = bpf_probe_read_user_str(buf, sizeof(buf),
|
||||
* ctx->di);
|
||||
*
|
||||
* // Consume buf, for example push it to
|
||||
* // userspace via bpf_perf_event_output(); we
|
||||
* // can use res (the string length) as event
|
||||
* // size, after checking its boundaries.
|
||||
* }
|
||||
*
|
||||
* In comparison, using **bpf_probe_read_user()** helper here
|
||||
* instead to read the string would require to estimate the length
|
||||
* at compile time, and would often result in copying more memory
|
||||
* than necessary.
|
||||
*
|
||||
* Another useful use case is when parsing individual process
|
||||
* arguments or individual environment variables navigating
|
||||
* *current*\ **->mm->arg_start** and *current*\
|
||||
* **->mm->env_start**: using this helper and the return value,
|
||||
* one can quickly iterate at the right offset of the memory area.
|
||||
* Return
|
||||
* On success, the strictly positive length of the string,
|
||||
* including the trailing NUL character. On error, a negative
|
||||
* value.
|
||||
*
|
||||
* int bpf_probe_read_kernel_str(void *dst, u32 size, const void *unsafe_ptr)
|
||||
* Description
|
||||
* Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr*
|
||||
* to *dst*. Same semantics as with bpf_probe_read_user_str() apply.
|
||||
* Return
|
||||
* On success, the strictly positive length of the string, including
|
||||
* the trailing NUL character. On error, a negative value.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@@ -2888,7 +2928,11 @@ union bpf_attr {
|
||||
FN(sk_storage_delete), \
|
||||
FN(send_signal), \
|
||||
FN(tcp_gen_syncookie), \
|
||||
FN(skb_output),
|
||||
FN(skb_output), \
|
||||
FN(probe_read_user), \
|
||||
FN(probe_read_kernel), \
|
||||
FN(probe_read_user_str), \
|
||||
FN(probe_read_kernel_str),
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
* function eBPF program intends to call
|
||||
|
||||
+5
-7
@@ -668,9 +668,6 @@ static struct bpf_prog *bpf_prog_kallsyms_find(unsigned long addr)
|
||||
{
|
||||
struct latch_tree_node *n;
|
||||
|
||||
if (!bpf_jit_kallsyms_enabled())
|
||||
return NULL;
|
||||
|
||||
n = latch_tree_find((void *)addr, &bpf_tree, &bpf_tree_ops);
|
||||
return n ?
|
||||
container_of(n, struct bpf_prog_aux, ksym_tnode)->prog :
|
||||
@@ -1309,11 +1306,12 @@ bool bpf_opcode_in_insntable(u8 code)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
u64 __weak bpf_probe_read(void * dst, u32 size, const void * unsafe_ptr)
|
||||
u64 __weak bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr)
|
||||
{
|
||||
memset(dst, 0, size);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* __bpf_prog_run - run eBPF program on a given context
|
||||
* @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
|
||||
@@ -1569,9 +1567,9 @@ out:
|
||||
LDST(W, u32)
|
||||
LDST(DW, u64)
|
||||
#undef LDST
|
||||
#define LDX_PROBE(SIZEOP, SIZE) \
|
||||
LDX_PROBE_MEM_##SIZEOP: \
|
||||
bpf_probe_read(&DST, SIZE, (const void *)(long) SRC); \
|
||||
#define LDX_PROBE(SIZEOP, SIZE) \
|
||||
LDX_PROBE_MEM_##SIZEOP: \
|
||||
bpf_probe_read_kernel(&DST, SIZE, (const void *)(long) SRC); \
|
||||
CONT;
|
||||
LDX_PROBE(B, 1)
|
||||
LDX_PROBE(H, 2)
|
||||
|
||||
@@ -1579,7 +1579,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
|
||||
u32 btf_id)
|
||||
{
|
||||
switch (prog_type) {
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT:
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
if (btf_id > BTF_MAX_TYPE)
|
||||
return -EINVAL;
|
||||
break;
|
||||
@@ -1842,13 +1842,13 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
return PTR_ERR(prog);
|
||||
|
||||
if (prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT &&
|
||||
prog->type != BPF_PROG_TYPE_TRACING &&
|
||||
prog->type != BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
if (prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT &&
|
||||
prog->aux->attach_btf_id) {
|
||||
if (prog->type == BPF_PROG_TYPE_TRACING) {
|
||||
if (attr->raw_tracepoint.name) {
|
||||
/* raw_tp name should not be specified in raw_tp
|
||||
* programs that were verified via in-kernel BTF info
|
||||
|
||||
+29
-10
@@ -6279,6 +6279,11 @@ static int check_return_code(struct bpf_verifier_env *env)
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
break;
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT:
|
||||
if (!env->prog->aux->attach_btf_id)
|
||||
return 0;
|
||||
range = tnum_const(0);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -9376,24 +9381,36 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_prog *prog = env->prog;
|
||||
u32 btf_id = prog->aux->attach_btf_id;
|
||||
const char prefix[] = "btf_trace_";
|
||||
const struct btf_type *t;
|
||||
const char *tname;
|
||||
|
||||
if (prog->type == BPF_PROG_TYPE_RAW_TRACEPOINT && btf_id) {
|
||||
const char prefix[] = "btf_trace_";
|
||||
if (prog->type != BPF_PROG_TYPE_TRACING)
|
||||
return 0;
|
||||
|
||||
t = btf_type_by_id(btf_vmlinux, btf_id);
|
||||
if (!t) {
|
||||
verbose(env, "attach_btf_id %u is invalid\n", btf_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!btf_id) {
|
||||
verbose(env, "Tracing programs must provide btf_id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
t = btf_type_by_id(btf_vmlinux, btf_id);
|
||||
if (!t) {
|
||||
verbose(env, "attach_btf_id %u is invalid\n", btf_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
tname = btf_name_by_offset(btf_vmlinux, t->name_off);
|
||||
if (!tname) {
|
||||
verbose(env, "attach_btf_id %u doesn't have a name\n", btf_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (prog->expected_attach_type) {
|
||||
case BPF_TRACE_RAW_TP:
|
||||
if (!btf_type_is_typedef(t)) {
|
||||
verbose(env, "attach_btf_id %u is not a typedef\n",
|
||||
btf_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
tname = btf_name_by_offset(btf_vmlinux, t->name_off);
|
||||
if (!tname || strncmp(prefix, tname, sizeof(prefix) - 1)) {
|
||||
if (strncmp(prefix, tname, sizeof(prefix) - 1)) {
|
||||
verbose(env, "attach_btf_id %u points to wrong type name %s\n",
|
||||
btf_id, tname);
|
||||
return -EINVAL;
|
||||
@@ -9414,8 +9431,10 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
|
||||
prog->aux->attach_func_name = tname;
|
||||
prog->aux->attach_func_proto = t;
|
||||
prog->aux->attach_btf_trace = true;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
|
||||
|
||||
+35
-75
@@ -9,13 +9,6 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
struct xsk_map {
|
||||
struct bpf_map map;
|
||||
struct xdp_sock **xsk_map;
|
||||
struct list_head __percpu *flush_list;
|
||||
spinlock_t lock; /* Synchronize map updates */
|
||||
};
|
||||
|
||||
int xsk_map_inc(struct xsk_map *map)
|
||||
{
|
||||
struct bpf_map *m = &map->map;
|
||||
@@ -80,9 +73,10 @@ static void xsk_map_sock_delete(struct xdp_sock *xs,
|
||||
|
||||
static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_map_memory mem;
|
||||
int cpu, err, numa_node;
|
||||
struct xsk_map *m;
|
||||
int cpu, err;
|
||||
u64 cost;
|
||||
u64 cost, size;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return ERR_PTR(-EPERM);
|
||||
@@ -92,44 +86,35 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
||||
attr->map_flags & ~(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
m = kzalloc(sizeof(*m), GFP_USER);
|
||||
if (!m)
|
||||
numa_node = bpf_map_attr_numa_node(attr);
|
||||
size = struct_size(m, xsk_map, attr->max_entries);
|
||||
cost = size + array_size(sizeof(*m->flush_list), num_possible_cpus());
|
||||
|
||||
err = bpf_map_charge_init(&mem, cost);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
m = bpf_map_area_alloc(size, numa_node);
|
||||
if (!m) {
|
||||
bpf_map_charge_finish(&mem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
bpf_map_init_from_attr(&m->map, attr);
|
||||
bpf_map_charge_move(&m->map.memory, &mem);
|
||||
spin_lock_init(&m->lock);
|
||||
|
||||
cost = (u64)m->map.max_entries * sizeof(struct xdp_sock *);
|
||||
cost += sizeof(struct list_head) * num_possible_cpus();
|
||||
|
||||
/* Notice returns -EPERM on if map size is larger than memlock limit */
|
||||
err = bpf_map_charge_init(&m->map.memory, cost);
|
||||
if (err)
|
||||
goto free_m;
|
||||
|
||||
err = -ENOMEM;
|
||||
|
||||
m->flush_list = alloc_percpu(struct list_head);
|
||||
if (!m->flush_list)
|
||||
goto free_charge;
|
||||
if (!m->flush_list) {
|
||||
bpf_map_charge_finish(&m->map.memory);
|
||||
bpf_map_area_free(m);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
INIT_LIST_HEAD(per_cpu_ptr(m->flush_list, cpu));
|
||||
|
||||
m->xsk_map = bpf_map_area_alloc(m->map.max_entries *
|
||||
sizeof(struct xdp_sock *),
|
||||
m->map.numa_node);
|
||||
if (!m->xsk_map)
|
||||
goto free_percpu;
|
||||
return &m->map;
|
||||
|
||||
free_percpu:
|
||||
free_percpu(m->flush_list);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&m->map.memory);
|
||||
free_m:
|
||||
kfree(m);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void xsk_map_free(struct bpf_map *map)
|
||||
@@ -139,8 +124,7 @@ static void xsk_map_free(struct bpf_map *map)
|
||||
bpf_clear_redirect_map(map);
|
||||
synchronize_net();
|
||||
free_percpu(m->flush_list);
|
||||
bpf_map_area_free(m->xsk_map);
|
||||
kfree(m);
|
||||
bpf_map_area_free(m);
|
||||
}
|
||||
|
||||
static int xsk_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
|
||||
@@ -160,45 +144,20 @@ static int xsk_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct xdp_sock *__xsk_map_lookup_elem(struct bpf_map *map, u32 key)
|
||||
static u32 xsk_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct xdp_sock *xs;
|
||||
const int ret = BPF_REG_0, mp = BPF_REG_1, index = BPF_REG_2;
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
|
||||
if (key >= map->max_entries)
|
||||
return NULL;
|
||||
|
||||
xs = READ_ONCE(m->xsk_map[key]);
|
||||
return xs;
|
||||
}
|
||||
|
||||
int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp,
|
||||
struct xdp_sock *xs)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct list_head *flush_list = this_cpu_ptr(m->flush_list);
|
||||
int err;
|
||||
|
||||
err = xsk_rcv(xs, xdp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!xs->flush_node.prev)
|
||||
list_add(&xs->flush_node, flush_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __xsk_map_flush(struct bpf_map *map)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct list_head *flush_list = this_cpu_ptr(m->flush_list);
|
||||
struct xdp_sock *xs, *tmp;
|
||||
|
||||
list_for_each_entry_safe(xs, tmp, flush_list, flush_node) {
|
||||
xsk_flush(xs);
|
||||
__list_del_clearprev(&xs->flush_node);
|
||||
}
|
||||
*insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0);
|
||||
*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 5);
|
||||
*insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(sizeof(struct xsk_sock *)));
|
||||
*insn++ = BPF_ALU64_IMM(BPF_ADD, mp, offsetof(struct xsk_map, xsk_map));
|
||||
*insn++ = BPF_ALU64_REG(BPF_ADD, ret, mp);
|
||||
*insn++ = BPF_LDX_MEM(BPF_SIZEOF(struct xsk_sock *), ret, ret, 0);
|
||||
*insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
|
||||
*insn++ = BPF_MOV64_IMM(ret, 0);
|
||||
return insn - insn_buf;
|
||||
}
|
||||
|
||||
static void *xsk_map_lookup_elem(struct bpf_map *map, void *key)
|
||||
@@ -312,6 +271,7 @@ const struct bpf_map_ops xsk_map_ops = {
|
||||
.map_free = xsk_map_free,
|
||||
.map_get_next_key = xsk_map_get_next_key,
|
||||
.map_lookup_elem = xsk_map_lookup_elem,
|
||||
.map_gen_lookup = xsk_map_gen_lookup,
|
||||
.map_lookup_elem_sys_only = xsk_map_lookup_elem_sys_only,
|
||||
.map_update_elem = xsk_map_update_elem,
|
||||
.map_delete_elem = xsk_map_delete_elem,
|
||||
|
||||
+175
-60
@@ -138,24 +138,19 @@ static const struct bpf_func_proto bpf_override_return_proto = {
|
||||
};
|
||||
#endif
|
||||
|
||||
BPF_CALL_3(bpf_probe_read, void *, dst, u32, size, const void *, unsafe_ptr)
|
||||
BPF_CALL_3(bpf_probe_read_user, void *, dst, u32, size,
|
||||
const void __user *, unsafe_ptr)
|
||||
{
|
||||
int ret;
|
||||
int ret = probe_user_read(dst, unsafe_ptr, size);
|
||||
|
||||
ret = security_locked_down(LOCKDOWN_BPF_READ);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = probe_kernel_read(dst, unsafe_ptr, size);
|
||||
if (unlikely(ret < 0))
|
||||
out:
|
||||
memset(dst, 0, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_proto = {
|
||||
.func = bpf_probe_read,
|
||||
static const struct bpf_func_proto bpf_probe_read_user_proto = {
|
||||
.func = bpf_probe_read_user,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
@@ -163,7 +158,128 @@ static const struct bpf_func_proto bpf_probe_read_proto = {
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_probe_write_user, void *, unsafe_ptr, const void *, src,
|
||||
BPF_CALL_3(bpf_probe_read_user_str, void *, dst, u32, size,
|
||||
const void __user *, unsafe_ptr)
|
||||
{
|
||||
int ret = strncpy_from_unsafe_user(dst, unsafe_ptr, size);
|
||||
|
||||
if (unlikely(ret < 0))
|
||||
memset(dst, 0, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_user_str_proto = {
|
||||
.func = bpf_probe_read_user_str,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
static __always_inline int
|
||||
bpf_probe_read_kernel_common(void *dst, u32 size, const void *unsafe_ptr,
|
||||
const bool compat)
|
||||
{
|
||||
int ret = security_locked_down(LOCKDOWN_BPF_READ);
|
||||
|
||||
if (unlikely(ret < 0))
|
||||
goto out;
|
||||
ret = compat ? probe_kernel_read(dst, unsafe_ptr, size) :
|
||||
probe_kernel_read_strict(dst, unsafe_ptr, size);
|
||||
if (unlikely(ret < 0))
|
||||
out:
|
||||
memset(dst, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BPF_CALL_3(bpf_probe_read_kernel, void *, dst, u32, size,
|
||||
const void *, unsafe_ptr)
|
||||
{
|
||||
return bpf_probe_read_kernel_common(dst, size, unsafe_ptr, false);
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_kernel_proto = {
|
||||
.func = bpf_probe_read_kernel,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_probe_read_compat, void *, dst, u32, size,
|
||||
const void *, unsafe_ptr)
|
||||
{
|
||||
return bpf_probe_read_kernel_common(dst, size, unsafe_ptr, true);
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_compat_proto = {
|
||||
.func = bpf_probe_read_compat,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
static __always_inline int
|
||||
bpf_probe_read_kernel_str_common(void *dst, u32 size, const void *unsafe_ptr,
|
||||
const bool compat)
|
||||
{
|
||||
int ret = security_locked_down(LOCKDOWN_BPF_READ);
|
||||
|
||||
if (unlikely(ret < 0))
|
||||
goto out;
|
||||
/*
|
||||
* The strncpy_from_unsafe_*() call will likely not fill the entire
|
||||
* buffer, but that's okay in this circumstance as we're probing
|
||||
* arbitrary memory anyway similar to bpf_probe_read_*() and might
|
||||
* as well probe the stack. Thus, memory is explicitly cleared
|
||||
* only in error case, so that improper users ignoring return
|
||||
* code altogether don't copy garbage; otherwise length of string
|
||||
* is returned that can be used for bpf_perf_event_output() et al.
|
||||
*/
|
||||
ret = compat ? strncpy_from_unsafe(dst, unsafe_ptr, size) :
|
||||
strncpy_from_unsafe_strict(dst, unsafe_ptr, size);
|
||||
if (unlikely(ret < 0))
|
||||
out:
|
||||
memset(dst, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
BPF_CALL_3(bpf_probe_read_kernel_str, void *, dst, u32, size,
|
||||
const void *, unsafe_ptr)
|
||||
{
|
||||
return bpf_probe_read_kernel_str_common(dst, size, unsafe_ptr, false);
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_kernel_str_proto = {
|
||||
.func = bpf_probe_read_kernel_str,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_probe_read_compat_str, void *, dst, u32, size,
|
||||
const void *, unsafe_ptr)
|
||||
{
|
||||
return bpf_probe_read_kernel_str_common(dst, size, unsafe_ptr, true);
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_compat_str_proto = {
|
||||
.func = bpf_probe_read_compat_str,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_probe_write_user, void __user *, unsafe_ptr, const void *, src,
|
||||
u32, size)
|
||||
{
|
||||
/*
|
||||
@@ -186,10 +302,8 @@ BPF_CALL_3(bpf_probe_write_user, void *, unsafe_ptr, const void *, src,
|
||||
return -EPERM;
|
||||
if (unlikely(!nmi_uaccess_okay()))
|
||||
return -EPERM;
|
||||
if (!access_ok(unsafe_ptr, size))
|
||||
return -EPERM;
|
||||
|
||||
return probe_kernel_write(unsafe_ptr, src, size);
|
||||
return probe_user_write(unsafe_ptr, src, size);
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_write_user_proto = {
|
||||
@@ -585,41 +699,6 @@ static const struct bpf_func_proto bpf_current_task_under_cgroup_proto = {
|
||||
.arg2_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_probe_read_str, void *, dst, u32, size,
|
||||
const void *, unsafe_ptr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = security_locked_down(LOCKDOWN_BPF_READ);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The strncpy_from_unsafe() call will likely not fill the entire
|
||||
* buffer, but that's okay in this circumstance as we're probing
|
||||
* arbitrary memory anyway similar to bpf_probe_read() and might
|
||||
* as well probe the stack. Thus, memory is explicitly cleared
|
||||
* only in error case, so that improper users ignoring return
|
||||
* code altogether don't copy garbage; otherwise length of string
|
||||
* is returned that can be used for bpf_perf_event_output() et al.
|
||||
*/
|
||||
ret = strncpy_from_unsafe(dst, unsafe_ptr, size);
|
||||
if (unlikely(ret < 0))
|
||||
out:
|
||||
memset(dst, 0, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto bpf_probe_read_str_proto = {
|
||||
.func = bpf_probe_read_str,
|
||||
.gpl_only = true,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
struct send_signal_irq_work {
|
||||
struct irq_work irq_work;
|
||||
struct task_struct *task;
|
||||
@@ -699,8 +778,6 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_map_pop_elem_proto;
|
||||
case BPF_FUNC_map_peek_elem:
|
||||
return &bpf_map_peek_elem_proto;
|
||||
case BPF_FUNC_probe_read:
|
||||
return &bpf_probe_read_proto;
|
||||
case BPF_FUNC_ktime_get_ns:
|
||||
return &bpf_ktime_get_ns_proto;
|
||||
case BPF_FUNC_tail_call:
|
||||
@@ -727,8 +804,18 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_current_task_under_cgroup_proto;
|
||||
case BPF_FUNC_get_prandom_u32:
|
||||
return &bpf_get_prandom_u32_proto;
|
||||
case BPF_FUNC_probe_read_user:
|
||||
return &bpf_probe_read_user_proto;
|
||||
case BPF_FUNC_probe_read_kernel:
|
||||
return &bpf_probe_read_kernel_proto;
|
||||
case BPF_FUNC_probe_read:
|
||||
return &bpf_probe_read_compat_proto;
|
||||
case BPF_FUNC_probe_read_user_str:
|
||||
return &bpf_probe_read_user_str_proto;
|
||||
case BPF_FUNC_probe_read_kernel_str:
|
||||
return &bpf_probe_read_kernel_str_proto;
|
||||
case BPF_FUNC_probe_read_str:
|
||||
return &bpf_probe_read_str_proto;
|
||||
return &bpf_probe_read_compat_str_proto;
|
||||
#ifdef CONFIG_CGROUPS
|
||||
case BPF_FUNC_get_current_cgroup_id:
|
||||
return &bpf_get_current_cgroup_id_proto;
|
||||
@@ -1055,10 +1142,6 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_perf_event_output:
|
||||
return &bpf_perf_event_output_proto_raw_tp;
|
||||
#ifdef CONFIG_NET
|
||||
case BPF_FUNC_skb_output:
|
||||
return &bpf_skb_output_proto;
|
||||
#endif
|
||||
case BPF_FUNC_get_stackid:
|
||||
return &bpf_get_stackid_proto_raw_tp;
|
||||
case BPF_FUNC_get_stack:
|
||||
@@ -1068,20 +1151,44 @@ raw_tp_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
}
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto *
|
||||
tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
{
|
||||
switch (func_id) {
|
||||
#ifdef CONFIG_NET
|
||||
case BPF_FUNC_skb_output:
|
||||
return &bpf_skb_output_proto;
|
||||
#endif
|
||||
default:
|
||||
return raw_tp_prog_func_proto(func_id, prog);
|
||||
}
|
||||
}
|
||||
|
||||
static bool raw_tp_prog_is_valid_access(int off, int size,
|
||||
enum bpf_access_type type,
|
||||
const struct bpf_prog *prog,
|
||||
struct bpf_insn_access_aux *info)
|
||||
{
|
||||
/* largest tracepoint in the kernel has 12 args */
|
||||
if (off < 0 || off >= sizeof(__u64) * 12)
|
||||
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
|
||||
return false;
|
||||
if (type != BPF_READ)
|
||||
return false;
|
||||
if (off % size != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tracing_prog_is_valid_access(int off, int size,
|
||||
enum bpf_access_type type,
|
||||
const struct bpf_prog *prog,
|
||||
struct bpf_insn_access_aux *info)
|
||||
{
|
||||
if (off < 0 || off >= sizeof(__u64) * MAX_BPF_FUNC_ARGS)
|
||||
return false;
|
||||
if (type != BPF_READ)
|
||||
return false;
|
||||
if (off % size != 0)
|
||||
return false;
|
||||
if (!prog->aux->attach_btf_id)
|
||||
return true;
|
||||
return btf_ctx_access(off, size, type, prog, info);
|
||||
}
|
||||
|
||||
@@ -1093,6 +1200,14 @@ const struct bpf_verifier_ops raw_tracepoint_verifier_ops = {
|
||||
const struct bpf_prog_ops raw_tracepoint_prog_ops = {
|
||||
};
|
||||
|
||||
const struct bpf_verifier_ops tracing_verifier_ops = {
|
||||
.get_func_proto = tracing_prog_func_proto,
|
||||
.is_valid_access = tracing_prog_is_valid_access,
|
||||
};
|
||||
|
||||
const struct bpf_prog_ops tracing_prog_ops = {
|
||||
};
|
||||
|
||||
static bool raw_tp_writable_prog_is_valid_access(int off, int size,
|
||||
enum bpf_access_type type,
|
||||
const struct bpf_prog *prog,
|
||||
|
||||
+103
-9
@@ -6859,34 +6859,128 @@ err_page0:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static __init int test_skb_segment(void)
|
||||
static __init struct sk_buff *build_test_skb_linear_no_head_frag(void)
|
||||
{
|
||||
unsigned int alloc_size = 2000;
|
||||
unsigned int headroom = 102, doffset = 72, data_size = 1308;
|
||||
struct sk_buff *skb[2];
|
||||
int i;
|
||||
|
||||
/* skbs linked in a frag_list, both with linear data, with head_frag=0
|
||||
* (data allocated by kmalloc), both have tcp data of 1308 bytes
|
||||
* (total payload is 2616 bytes).
|
||||
* Data offset is 72 bytes (40 ipv6 hdr, 32 tcp hdr). Some headroom.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
skb[i] = alloc_skb(alloc_size, GFP_KERNEL);
|
||||
if (!skb[i]) {
|
||||
if (i == 0)
|
||||
goto err_skb0;
|
||||
else
|
||||
goto err_skb1;
|
||||
}
|
||||
|
||||
skb[i]->protocol = htons(ETH_P_IPV6);
|
||||
skb_reserve(skb[i], headroom);
|
||||
skb_put(skb[i], doffset + data_size);
|
||||
skb_reset_network_header(skb[i]);
|
||||
if (i == 0)
|
||||
skb_reset_mac_header(skb[i]);
|
||||
else
|
||||
skb_set_mac_header(skb[i], -ETH_HLEN);
|
||||
__skb_pull(skb[i], doffset);
|
||||
}
|
||||
|
||||
/* setup shinfo.
|
||||
* mimic bpf_skb_proto_4_to_6, which resets gso_segs and assigns a
|
||||
* reduced gso_size.
|
||||
*/
|
||||
skb_shinfo(skb[0])->gso_size = 1288;
|
||||
skb_shinfo(skb[0])->gso_type = SKB_GSO_TCPV6 | SKB_GSO_DODGY;
|
||||
skb_shinfo(skb[0])->gso_segs = 0;
|
||||
skb_shinfo(skb[0])->frag_list = skb[1];
|
||||
|
||||
/* adjust skb[0]'s len */
|
||||
skb[0]->len += skb[1]->len;
|
||||
skb[0]->data_len += skb[1]->len;
|
||||
skb[0]->truesize += skb[1]->truesize;
|
||||
|
||||
return skb[0];
|
||||
|
||||
err_skb1:
|
||||
kfree_skb(skb[0]);
|
||||
err_skb0:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct skb_segment_test {
|
||||
const char *descr;
|
||||
struct sk_buff *(*build_skb)(void);
|
||||
netdev_features_t features;
|
||||
};
|
||||
|
||||
static struct skb_segment_test skb_segment_tests[] __initconst = {
|
||||
{
|
||||
.descr = "gso_with_rx_frags",
|
||||
.build_skb = build_test_skb,
|
||||
.features = NETIF_F_SG | NETIF_F_GSO_PARTIAL | NETIF_F_IP_CSUM |
|
||||
NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM
|
||||
},
|
||||
{
|
||||
.descr = "gso_linear_no_head_frag",
|
||||
.build_skb = build_test_skb_linear_no_head_frag,
|
||||
.features = NETIF_F_SG | NETIF_F_FRAGLIST |
|
||||
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_GSO |
|
||||
NETIF_F_LLTX_BIT | NETIF_F_GRO |
|
||||
NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |
|
||||
NETIF_F_HW_VLAN_STAG_TX_BIT
|
||||
}
|
||||
};
|
||||
|
||||
static __init int test_skb_segment_single(const struct skb_segment_test *test)
|
||||
{
|
||||
struct sk_buff *skb, *segs;
|
||||
int ret = -1;
|
||||
|
||||
features = NETIF_F_SG | NETIF_F_GSO_PARTIAL | NETIF_F_IP_CSUM |
|
||||
NETIF_F_IPV6_CSUM;
|
||||
features |= NETIF_F_RXCSUM;
|
||||
skb = build_test_skb();
|
||||
skb = test->build_skb();
|
||||
if (!skb) {
|
||||
pr_info("%s: failed to build_test_skb", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
segs = skb_segment(skb, features);
|
||||
segs = skb_segment(skb, test->features);
|
||||
if (!IS_ERR(segs)) {
|
||||
kfree_skb_list(segs);
|
||||
ret = 0;
|
||||
pr_info("%s: success in skb_segment!", __func__);
|
||||
} else {
|
||||
pr_info("%s: failed in skb_segment!", __func__);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __init int test_skb_segment(void)
|
||||
{
|
||||
int i, err_cnt = 0, pass_cnt = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(skb_segment_tests); i++) {
|
||||
const struct skb_segment_test *test = &skb_segment_tests[i];
|
||||
|
||||
pr_info("#%d %s ", i, test->descr);
|
||||
|
||||
if (test_skb_segment_single(test)) {
|
||||
pr_cont("FAIL\n");
|
||||
err_cnt++;
|
||||
} else {
|
||||
pr_cont("PASS\n");
|
||||
pass_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("%s: Summary: %d PASSED, %d FAILED\n", __func__,
|
||||
pass_cnt, err_cnt);
|
||||
return err_cnt ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static __init int test_bpf(void)
|
||||
{
|
||||
int i, err_cnt = 0, pass_cnt = 0;
|
||||
|
||||
+65
-5
@@ -18,6 +18,18 @@ probe_read_common(void *dst, const void __user *src, size_t size)
|
||||
return ret ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static __always_inline long
|
||||
probe_write_common(void __user *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret;
|
||||
|
||||
pagefault_disable();
|
||||
ret = __copy_to_user_inatomic(dst, src, size);
|
||||
pagefault_enable();
|
||||
|
||||
return ret ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* probe_kernel_read(): safely attempt to read from a kernel-space location
|
||||
* @dst: pointer to the buffer that shall take the data
|
||||
@@ -31,11 +43,20 @@ probe_read_common(void *dst, const void __user *src, size_t size)
|
||||
* do_page_fault() doesn't attempt to take mmap_sem. This makes
|
||||
* probe_kernel_read() suitable for use within regions where the caller
|
||||
* already holds mmap_sem, or other locks which nest inside mmap_sem.
|
||||
*
|
||||
* probe_kernel_read_strict() is the same as probe_kernel_read() except for
|
||||
* the case where architectures have non-overlapping user and kernel address
|
||||
* ranges: probe_kernel_read_strict() will additionally return -EFAULT for
|
||||
* probing memory on a user address range where probe_user_read() is supposed
|
||||
* to be used instead.
|
||||
*/
|
||||
|
||||
long __weak probe_kernel_read(void *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_kernel_read")));
|
||||
|
||||
long __weak probe_kernel_read_strict(void *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_kernel_read")));
|
||||
|
||||
long __probe_kernel_read(void *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret;
|
||||
@@ -85,6 +106,7 @@ EXPORT_SYMBOL_GPL(probe_user_read);
|
||||
* Safely write to address @dst from the buffer at @src. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
|
||||
long __weak probe_kernel_write(void *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_kernel_write")));
|
||||
|
||||
@@ -94,15 +116,39 @@ long __probe_kernel_write(void *dst, const void *src, size_t size)
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
pagefault_disable();
|
||||
ret = __copy_to_user_inatomic((__force void __user *)dst, src, size);
|
||||
pagefault_enable();
|
||||
ret = probe_write_common((__force void __user *)dst, src, size);
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret ? -EFAULT : 0;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(probe_kernel_write);
|
||||
|
||||
/**
|
||||
* probe_user_write(): safely attempt to write to a user-space location
|
||||
* @dst: address to write to
|
||||
* @src: pointer to the data that shall be written
|
||||
* @size: size of the data chunk
|
||||
*
|
||||
* Safely write to address @dst from the buffer at @src. If a kernel fault
|
||||
* happens, handle that and return -EFAULT.
|
||||
*/
|
||||
|
||||
long __weak probe_user_write(void __user *dst, const void *src, size_t size)
|
||||
__attribute__((alias("__probe_user_write")));
|
||||
|
||||
long __probe_user_write(void __user *dst, const void *src, size_t size)
|
||||
{
|
||||
long ret = -EFAULT;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
|
||||
set_fs(USER_DS);
|
||||
if (access_ok(dst, size))
|
||||
ret = probe_write_common(dst, src, size);
|
||||
set_fs(old_fs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(probe_user_write);
|
||||
|
||||
/**
|
||||
* strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
|
||||
@@ -120,8 +166,22 @@ EXPORT_SYMBOL_GPL(probe_kernel_write);
|
||||
*
|
||||
* If @count is smaller than the length of the string, copies @count-1 bytes,
|
||||
* sets the last byte of @dst buffer to NUL and returns @count.
|
||||
*
|
||||
* strncpy_from_unsafe_strict() is the same as strncpy_from_unsafe() except
|
||||
* for the case where architectures have non-overlapping user and kernel address
|
||||
* ranges: strncpy_from_unsafe_strict() will additionally return -EFAULT for
|
||||
* probing memory on a user address range where strncpy_from_unsafe_user() is
|
||||
* supposed to be used instead.
|
||||
*/
|
||||
long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
||||
|
||||
long __weak strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
||||
__attribute__((alias("__strncpy_from_unsafe")));
|
||||
|
||||
long __weak strncpy_from_unsafe_strict(char *dst, const void *unsafe_addr,
|
||||
long count)
|
||||
__attribute__((alias("__strncpy_from_unsafe")));
|
||||
|
||||
long __strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
|
||||
{
|
||||
mm_segment_t old_fs = get_fs();
|
||||
const void *src = unsafe_addr;
|
||||
|
||||
+31
-2
@@ -196,7 +196,7 @@ static bool xsk_is_bound(struct xdp_sock *xs)
|
||||
return false;
|
||||
}
|
||||
|
||||
int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
static int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
{
|
||||
u32 len;
|
||||
|
||||
@@ -212,7 +212,7 @@ int xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
__xsk_rcv_zc(xs, xdp, len) : __xsk_rcv(xs, xdp, len);
|
||||
}
|
||||
|
||||
void xsk_flush(struct xdp_sock *xs)
|
||||
static void xsk_flush(struct xdp_sock *xs)
|
||||
{
|
||||
xskq_produce_flush_desc(xs->rx);
|
||||
xs->sk.sk_data_ready(&xs->sk);
|
||||
@@ -264,6 +264,35 @@ out_unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
int __xsk_map_redirect(struct bpf_map *map, struct xdp_buff *xdp,
|
||||
struct xdp_sock *xs)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct list_head *flush_list = this_cpu_ptr(m->flush_list);
|
||||
int err;
|
||||
|
||||
err = xsk_rcv(xs, xdp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!xs->flush_node.prev)
|
||||
list_add(&xs->flush_node, flush_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __xsk_map_flush(struct bpf_map *map)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct list_head *flush_list = this_cpu_ptr(m->flush_list);
|
||||
struct xdp_sock *xs, *tmp;
|
||||
|
||||
list_for_each_entry_safe(xs, tmp, flush_list, flush_node) {
|
||||
xsk_flush(xs);
|
||||
__list_del_clearprev(&xs->flush_node);
|
||||
}
|
||||
}
|
||||
|
||||
void xsk_umem_complete_tx(struct xdp_umem *umem, u32 nb_entries)
|
||||
{
|
||||
xskq_produce_flush_addr_n(umem->cq, nb_entries);
|
||||
|
||||
@@ -181,8 +181,8 @@ int stress_lru_hmap_alloc(struct pt_regs *ctx)
|
||||
if (addrlen != sizeof(*in6))
|
||||
return 0;
|
||||
|
||||
ret = bpf_probe_read(test_params.dst6, sizeof(test_params.dst6),
|
||||
&in6->sin6_addr);
|
||||
ret = bpf_probe_read_user(test_params.dst6, sizeof(test_params.dst6),
|
||||
&in6->sin6_addr);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ int trace_sys_connect(struct pt_regs *ctx)
|
||||
if (addrlen != sizeof(*in6))
|
||||
return 0;
|
||||
|
||||
ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr);
|
||||
ret = bpf_probe_read_user(dst6, sizeof(dst6), &in6->sin6_addr);
|
||||
if (ret) {
|
||||
inline_ret = ret;
|
||||
goto done;
|
||||
@@ -129,7 +129,7 @@ int trace_sys_connect(struct pt_regs *ctx)
|
||||
|
||||
test_case = dst6[7];
|
||||
|
||||
ret = bpf_probe_read(&port, sizeof(port), &in6->sin6_port);
|
||||
ret = bpf_probe_read_user(&port, sizeof(port), &in6->sin6_port);
|
||||
if (ret) {
|
||||
inline_ret = ret;
|
||||
goto done;
|
||||
|
||||
@@ -37,7 +37,7 @@ int bpf_prog1(struct pt_regs *ctx)
|
||||
if (sockaddr_len > sizeof(orig_addr))
|
||||
return 0;
|
||||
|
||||
if (bpf_probe_read(&orig_addr, sizeof(orig_addr), sockaddr_arg) != 0)
|
||||
if (bpf_probe_read_user(&orig_addr, sizeof(orig_addr), sockaddr_arg) != 0)
|
||||
return 0;
|
||||
|
||||
mapped_addr = bpf_map_lookup_elem(&dnat_map, &orig_addr);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user