Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HotBPF: Prevent Kernel Heap-based Exploitation #5373

Closed
wants to merge 1 commit into from

Conversation

kernel-patches-daemon-bpf[bot]
Copy link

Pull request for series with
subject: HotBPF: Prevent Kernel Heap-based Exploitation
version: 1
url: https://patchwork.kernel.org/project/netdevbpf/list/?series=767463

@kernel-patches-daemon-bpf
Copy link
Author

Upstream branch: a3f25d6
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=767463
version: 1

Pull request is NOT updated. Failed to apply https://patchwork.kernel.org/project/netdevbpf/list/?series=767463
error message:

Cmd('git') failed due to: exit code(128)
  cmdline: git am --3way
  stdout: 'Applying: HotBPF: Prevent Kernel Heap-based Exploitation
Using index info to reconstruct a base tree...
M	include/linux/bpf.h
M	include/uapi/linux/bpf.h
M	kernel/trace/bpf_trace.c
M	lib/Kconfig.debug
Falling back to patching base and 3-way merge...
Auto-merging lib/Kconfig.debug
Auto-merging kernel/trace/bpf_trace.c
CONFLICT (content): Merge conflict in kernel/trace/bpf_trace.c
Auto-merging include/uapi/linux/bpf.h
CONFLICT (content): Merge conflict in include/uapi/linux/bpf.h
Auto-merging include/linux/bpf.h
CONFLICT (content): Merge conflict in include/linux/bpf.h
Patch failed at 0001 HotBPF: Prevent Kernel Heap-based Exploitation
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".'
  stderr: 'error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch'

conflict:

diff --cc include/linux/bpf.h
index f58895830ada,1a12af0b7347..000000000000
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@@ -2899,16 -2105,9 +2899,22 @@@ extern const struct bpf_func_proto bpf_
  extern const struct bpf_func_proto bpf_btf_find_by_name_kind_proto;
  extern const struct bpf_func_proto bpf_sk_setsockopt_proto;
  extern const struct bpf_func_proto bpf_sk_getsockopt_proto;
++<<<<<<< HEAD
 +extern const struct bpf_func_proto bpf_unlocked_sk_setsockopt_proto;
 +extern const struct bpf_func_proto bpf_unlocked_sk_getsockopt_proto;
 +extern const struct bpf_func_proto bpf_find_vma_proto;
 +extern const struct bpf_func_proto bpf_loop_proto;
 +extern const struct bpf_func_proto bpf_copy_from_user_task_proto;
 +extern const struct bpf_func_proto bpf_set_retval_proto;
 +extern const struct bpf_func_proto bpf_get_retval_proto;
 +extern const struct bpf_func_proto bpf_user_ringbuf_drain_proto;
 +extern const struct bpf_func_proto bpf_cgrp_storage_get_proto;
 +extern const struct bpf_func_proto bpf_cgrp_storage_delete_proto;
++=======
+ extern const struct bpf_func_proto bpf_create_slub_cache_proto;
+ extern const struct bpf_func_proto bpf_cache_alloc_proto;
+ extern const struct bpf_func_proto bpf_jmp_next_proto;
++>>>>>>> HotBPF: Prevent Kernel Heap-based Exploitation
  
  const struct bpf_func_proto *tracing_prog_func_proto(
    enum bpf_func_id func_id, const struct bpf_prog *prog);
diff --cc include/uapi/linux/bpf.h
index 60a9d59beeab,2a0b03f4f2fc..000000000000
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@@ -5069,732 -4878,207 +5069,934 @@@ union bpf_attr 
   *	Return
   *		A pointer to struct pt_regs.
   *
++<<<<<<< HEAD
 + * long bpf_get_branch_snapshot(void *entries, u32 size, u64 flags)
 + *	Description
 + *		Get branch trace from hardware engines like Intel LBR. The
 + *		hardware engine is stopped shortly after the helper is
 + *		called. Therefore, the user need to filter branch entries
 + *		based on the actual use case. To capture branch trace
 + *		before the trigger point of the BPF program, the helper
 + *		should be called at the beginning of the BPF program.
 + *
 + *		The data is stored as struct perf_branch_entry into output
 + *		buffer *entries*. *size* is the size of *entries* in bytes.
 + *		*flags* is reserved for now and must be zero.
 + *
 + *	Return
 + *		On success, number of bytes written to *buf*. On error, a
 + *		negative value.
 + *
 + *		**-EINVAL** if *flags* is not zero.
 + *
 + *		**-ENOENT** if architecture does not support branch records.
 + *
 + * long bpf_trace_vprintk(const char *fmt, u32 fmt_size, const void *data, u32 data_len)
 + *	Description
 + *		Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64
 + *		to format and can handle more format args as a result.
 + *
 + *		Arguments are to be used as in **bpf_seq_printf**\ () helper.
 + *	Return
 + *		The number of bytes written to the buffer, or a negative error
 + *		in case of failure.
 + *
 + * struct unix_sock *bpf_skc_to_unix_sock(void *sk)
 + * 	Description
 + *		Dynamically cast a *sk* pointer to a *unix_sock* pointer.
 + *	Return
 + *		*sk* if casting is valid, or **NULL** otherwise.
 + *
 + * long bpf_kallsyms_lookup_name(const char *name, int name_sz, int flags, u64 *res)
 + *	Description
 + *		Get the address of a kernel symbol, returned in *res*. *res* is
 + *		set to 0 if the symbol is not found.
 + *	Return
 + *		On success, zero. On error, a negative value.
 + *
 + *		**-EINVAL** if *flags* is not zero.
 + *
 + *		**-EINVAL** if string *name* is not the same size as *name_sz*.
 + *
 + *		**-ENOENT** if symbol is not found.
 + *
 + *		**-EPERM** if caller does not have permission to obtain kernel address.
 + *
 + * long bpf_find_vma(struct task_struct *task, u64 addr, void *callback_fn, void *callback_ctx, u64 flags)
 + *	Description
 + *		Find vma of *task* that contains *addr*, call *callback_fn*
 + *		function with *task*, *vma*, and *callback_ctx*.
 + *		The *callback_fn* should be a static function and
 + *		the *callback_ctx* should be a pointer to the stack.
 + *		The *flags* is used to control certain aspects of the helper.
 + *		Currently, the *flags* must be 0.
 + *
 + *		The expected callback signature is
 + *
 + *		long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx);
 + *
 + *	Return
 + *		0 on success.
 + *		**-ENOENT** if *task->mm* is NULL, or no vma contains *addr*.
 + *		**-EBUSY** if failed to try lock mmap_lock.
 + *		**-EINVAL** for invalid **flags**.
 + *
 + * long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)
 + *	Description
 + *		For **nr_loops**, call **callback_fn** function
 + *		with **callback_ctx** as the context parameter.
 + *		The **callback_fn** should be a static function and
 + *		the **callback_ctx** should be a pointer to the stack.
 + *		The **flags** is used to control certain aspects of the helper.
 + *		Currently, the **flags** must be 0. Currently, nr_loops is
 + *		limited to 1 << 23 (~8 million) loops.
 + *
 + *		long (\*callback_fn)(u32 index, void \*ctx);
 + *
 + *		where **index** is the current index in the loop. The index
 + *		is zero-indexed.
 + *
 + *		If **callback_fn** returns 0, the helper will continue to the next
 + *		loop. If return value is 1, the helper will skip the rest of
 + *		the loops and return. Other return values are not used now,
 + *		and will be rejected by the verifier.
 + *
 + *	Return
 + *		The number of loops performed, **-EINVAL** for invalid **flags**,
 + *		**-E2BIG** if **nr_loops** exceeds the maximum number of loops.
 + *
 + * long bpf_strncmp(const char *s1, u32 s1_sz, const char *s2)
 + *	Description
 + *		Do strncmp() between **s1** and **s2**. **s1** doesn't need
 + *		to be null-terminated and **s1_sz** is the maximum storage
 + *		size of **s1**. **s2** must be a read-only string.
 + *	Return
 + *		An integer less than, equal to, or greater than zero
 + *		if the first **s1_sz** bytes of **s1** is found to be
 + *		less than, to match, or be greater than **s2**.
 + *
 + * long bpf_get_func_arg(void *ctx, u32 n, u64 *value)
 + *	Description
 + *		Get **n**-th argument register (zero based) of the traced function (for tracing programs)
 + *		returned in **value**.
 + *
 + *	Return
 + *		0 on success.
 + *		**-EINVAL** if n >= argument register count of traced function.
 + *
 + * long bpf_get_func_ret(void *ctx, u64 *value)
 + *	Description
 + *		Get return value of the traced function (for tracing programs)
 + *		in **value**.
 + *
 + *	Return
 + *		0 on success.
 + *		**-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN.
 + *
 + * long bpf_get_func_arg_cnt(void *ctx)
 + *	Description
 + *		Get number of registers of the traced function (for tracing programs) where
 + *		function arguments are stored in these registers.
 + *
 + *	Return
 + *		The number of argument registers of the traced function.
 + *
 + * int bpf_get_retval(void)
 + *	Description
 + *		Get the BPF program's return value that will be returned to the upper layers.
 + *
 + *		This helper is currently supported by cgroup programs and only by the hooks
 + *		where BPF program's return value is returned to the userspace via errno.
 + *	Return
 + *		The BPF program's return value.
 + *
 + * int bpf_set_retval(int retval)
 + *	Description
 + *		Set the BPF program's return value that will be returned to the upper layers.
 + *
 + *		This helper is currently supported by cgroup programs and only by the hooks
 + *		where BPF program's return value is returned to the userspace via errno.
 + *
 + *		Note that there is the following corner case where the program exports an error
 + *		via bpf_set_retval but signals success via 'return 1':
 + *
 + *			bpf_set_retval(-EPERM);
 + *			return 1;
 + *
 + *		In this case, the BPF program's return value will use helper's -EPERM. This
 + *		still holds true for cgroup/bind{4,6} which supports extra 'return 3' success case.
 + *
 + *	Return
 + *		0 on success, or a negative error in case of failure.
 + *
 + * u64 bpf_xdp_get_buff_len(struct xdp_buff *xdp_md)
 + *	Description
 + *		Get the total size of a given xdp buff (linear and paged area)
 + *	Return
 + *		The total size of a given xdp buffer.
 + *
 + * long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
 + *	Description
 + *		This helper is provided as an easy way to load data from a
 + *		xdp buffer. It can be used to load *len* bytes from *offset* from
 + *		the frame associated to *xdp_md*, into the buffer pointed by
 + *		*buf*.
 + *	Return
 + *		0 on success, or a negative error in case of failure.
 + *
 + * long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
 + *	Description
 + *		Store *len* bytes from buffer *buf* into the frame
 + *		associated to *xdp_md*, at *offset*.
 + *	Return
 + *		0 on success, or a negative error in case of failure.
 + *
 + * long bpf_copy_from_user_task(void *dst, u32 size, const void *user_ptr, struct task_struct *tsk, u64 flags)
 + *	Description
 + *		Read *size* bytes from user space address *user_ptr* in *tsk*'s
 + *		address space, and stores the data in *dst*. *flags* is not
 + *		used yet and is provided for future extensibility. This helper
 + *		can only be used by sleepable programs.
 + *	Return
 + *		0 on success, or a negative error in case of failure. On error
 + *		*dst* buffer is zeroed out.
 + *
 + * long bpf_skb_set_tstamp(struct sk_buff *skb, u64 tstamp, u32 tstamp_type)
 + *	Description
 + *		Change the __sk_buff->tstamp_type to *tstamp_type*
 + *		and set *tstamp* to the __sk_buff->tstamp together.
 + *
 + *		If there is no need to change the __sk_buff->tstamp_type,
 + *		the tstamp value can be directly written to __sk_buff->tstamp
 + *		instead.
 + *
 + *		BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that
 + *		will be kept during bpf_redirect_*().  A non zero
 + *		*tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO
 + *		*tstamp_type*.
 + *
 + *		A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used
 + *		with a zero *tstamp*.
 + *
 + *		Only IPv4 and IPv6 skb->protocol are supported.
 + *
 + *		This function is most useful when it needs to set a
 + *		mono delivery time to __sk_buff->tstamp and then
 + *		bpf_redirect_*() to the egress of an iface.  For example,
 + *		changing the (rcv) timestamp in __sk_buff->tstamp at
 + *		ingress to a mono delivery time and then bpf_redirect_*()
 + *		to sch_fq@phy-dev.
 + *	Return
 + *		0 on success.
 + *		**-EINVAL** for invalid input
 + *		**-EOPNOTSUPP** for unsupported protocol
 + *
 + * long bpf_ima_file_hash(struct file *file, void *dst, u32 size)
 + *	Description
 + *		Returns a calculated IMA hash of the *file*.
 + *		If the hash is larger than *size*, then only *size*
 + *		bytes will be copied to *dst*
 + *	Return
 + *		The **hash_algo** is returned on success,
 + *		**-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
 + *		invalid arguments are passed.
 + *
 + * void *bpf_kptr_xchg(void *map_value, void *ptr)
 + *	Description
 + *		Exchange kptr at pointer *map_value* with *ptr*, and return the
 + *		old value. *ptr* can be NULL, otherwise it must be a referenced
 + *		pointer which will be released when this helper is called.
 + *	Return
 + *		The old value of kptr (which can be NULL). The returned pointer
 + *		if not NULL, is a reference which must be released using its
 + *		corresponding release function, or moved into a BPF map before
 + *		program exit.
 + *
 + * void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu)
 + * 	Description
 + * 		Perform a lookup in *percpu map* for an entry associated to
 + * 		*key* on *cpu*.
 + * 	Return
 + * 		Map value associated to *key* on *cpu*, or **NULL** if no entry
 + * 		was found or *cpu* is invalid.
 + *
 + * struct mptcp_sock *bpf_skc_to_mptcp_sock(void *sk)
 + *	Description
 + *		Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
 + *	Return
 + *		*sk* if casting is valid, or **NULL** otherwise.
 + *
 + * long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
 + *	Description
 + *		Get a dynptr to local memory *data*.
 + *
 + *		*data* must be a ptr to a map value.
 + *		The maximum *size* supported is DYNPTR_MAX_SIZE.
 + *		*flags* is currently unused.
 + *	Return
 + *		0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
 + *		-EINVAL if flags is not 0.
 + *
 + * long bpf_ringbuf_reserve_dynptr(void *ringbuf, u32 size, u64 flags, struct bpf_dynptr *ptr)
 + *	Description
 + *		Reserve *size* bytes of payload in a ring buffer *ringbuf*
 + *		through the dynptr interface. *flags* must be 0.
 + *
 + *		Please note that a corresponding bpf_ringbuf_submit_dynptr or
 + *		bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the
 + *		reservation fails. This is enforced by the verifier.
 + *	Return
 + *		0 on success, or a negative error in case of failure.
 + *
 + * void bpf_ringbuf_submit_dynptr(struct bpf_dynptr *ptr, u64 flags)
 + *	Description
 + *		Submit reserved ring buffer sample, pointed to by *data*,
 + *		through the dynptr interface. This is a no-op if the dynptr is
 + *		invalid/null.
 + *
 + *		For more information on *flags*, please see
 + *		'bpf_ringbuf_submit'.
 + *	Return
 + *		Nothing. Always succeeds.
 + *
 + * void bpf_ringbuf_discard_dynptr(struct bpf_dynptr *ptr, u64 flags)
 + *	Description
 + *		Discard reserved ring buffer sample through the dynptr
 + *		interface. This is a no-op if the dynptr is invalid/null.
 + *
 + *		For more information on *flags*, please see
 + *		'bpf_ringbuf_discard'.
 + *	Return
 + *		Nothing. Always succeeds.
 + *
 + * long bpf_dynptr_read(void *dst, u32 len, const struct bpf_dynptr *src, u32 offset, u64 flags)
 + *	Description
 + *		Read *len* bytes from *src* into *dst*, starting from *offset*
 + *		into *src*.
 + *		*flags* is currently unused.
 + *	Return
 + *		0 on success, -E2BIG if *offset* + *len* exceeds the length
 + *		of *src*'s data, -EINVAL if *src* is an invalid dynptr or if
 + *		*flags* is not 0.
 + *
 + * long bpf_dynptr_write(const struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags)
 + *	Description
 + *		Write *len* bytes from *src* into *dst*, starting from *offset*
 + *		into *dst*.
 + *
 + *		*flags* must be 0 except for skb-type dynptrs.
 + *
 + *		For skb-type dynptrs:
 + *		    *  All data slices of the dynptr are automatically
 + *		       invalidated after **bpf_dynptr_write**\ (). This is
 + *		       because writing may pull the skb and change the
 + *		       underlying packet buffer.
 + *
 + *		    *  For *flags*, please see the flags accepted by
 + *		       **bpf_skb_store_bytes**\ ().
 + *	Return
 + *		0 on success, -E2BIG if *offset* + *len* exceeds the length
 + *		of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
 + *		is a read-only dynptr or if *flags* is not correct. For skb-type dynptrs,
 + *		other errors correspond to errors returned by **bpf_skb_store_bytes**\ ().
 + *
 + * void *bpf_dynptr_data(const struct bpf_dynptr *ptr, u32 offset, u32 len)
 + *	Description
 + *		Get a pointer to the underlying dynptr data.
 + *
 + *		*len* must be a statically known value. The returned data slice
 + *		is invalidated whenever the dynptr is invalidated.
 + *
 + *		skb and xdp type dynptrs may not use bpf_dynptr_data. They should
 + *		instead use bpf_dynptr_slice and bpf_dynptr_slice_rdwr.
 + *	Return
 + *		Pointer to the underlying dynptr data, NULL if the dynptr is
 + *		read-only, if the dynptr is invalid, or if the offset and length
 + *		is out of bounds.
 + *
 + * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len)
 + *	Description
 + *		Try to issue a SYN cookie for the packet with corresponding
 + *		IPv4/TCP headers, *iph* and *th*, without depending on a
 + *		listening socket.
 + *
 + *		*iph* points to the IPv4 header.
 + *
 + *		*th* points to the start of the TCP header, while *th_len*
 + *		contains the length of the TCP header (at least
 + *		**sizeof**\ (**struct tcphdr**)).
 + *	Return
 + *		On success, lower 32 bits hold the generated SYN cookie in
 + *		followed by 16 bits which hold the MSS value for that cookie,
 + *		and the top 16 bits are unused.
 + *
 + *		On failure, the returned value is one of the following:
 + *
 + *		**-EINVAL** if *th_len* is invalid.
 + *
 + * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len)
 + *	Description
 + *		Try to issue a SYN cookie for the packet with corresponding
 + *		IPv6/TCP headers, *iph* and *th*, without depending on a
 + *		listening socket.
 + *
 + *		*iph* points to the IPv6 header.
 + *
 + *		*th* points to the start of the TCP header, while *th_len*
 + *		contains the length of the TCP header (at least
 + *		**sizeof**\ (**struct tcphdr**)).
 + *	Return
 + *		On success, lower 32 bits hold the generated SYN cookie in
 + *		followed by 16 bits which hold the MSS value for that cookie,
 + *		and the top 16 bits are unused.
 + *
 + *		On failure, the returned value is one of the following:
 + *
 + *		**-EINVAL** if *th_len* is invalid.
 + *
 + *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
 + *
 + * long bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th)
 + *	Description
 + *		Check whether *iph* and *th* contain a valid SYN cookie ACK
 + *		without depending on a listening socket.
 + *
 + *		*iph* points to the IPv4 header.
 + *
 + *		*th* points to the TCP header.
 + *	Return
 + *		0 if *iph* and *th* are a valid SYN cookie ACK.
 + *
 + *		On failure, the returned value is one of the following:
 + *
 + *		**-EACCES** if the SYN cookie is not valid.
 + *
 + * long bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th)
 + *	Description
 + *		Check whether *iph* and *th* contain a valid SYN cookie ACK
 + *		without depending on a listening socket.
 + *
 + *		*iph* points to the IPv6 header.
 + *
 + *		*th* points to the TCP header.
 + *	Return
 + *		0 if *iph* and *th* are a valid SYN cookie ACK.
 + *
 + *		On failure, the returned value is one of the following:
 + *
 + *		**-EACCES** if the SYN cookie is not valid.
 + *
 + *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
 + *
 + * u64 bpf_ktime_get_tai_ns(void)
 + *	Description
 + *		A nonsettable system-wide clock derived from wall-clock time but
 + *		ignoring leap seconds.  This clock does not experience
 + *		discontinuities and backwards jumps caused by NTP inserting leap
 + *		seconds as CLOCK_REALTIME does.
 + *
 + *		See: **clock_gettime**\ (**CLOCK_TAI**)
 + *	Return
 + *		Current *ktime*.
 + *
 + * long bpf_user_ringbuf_drain(struct bpf_map *map, void *callback_fn, void *ctx, u64 flags)
 + *	Description
 + *		Drain samples from the specified user ring buffer, and invoke
 + *		the provided callback for each such sample:
 + *
 + *		long (\*callback_fn)(const struct bpf_dynptr \*dynptr, void \*ctx);
 + *
 + *		If **callback_fn** returns 0, the helper will continue to try
 + *		and drain the next sample, up to a maximum of
 + *		BPF_MAX_USER_RINGBUF_SAMPLES samples. If the return value is 1,
 + *		the helper will skip the rest of the samples and return. Other
 + *		return values are not used now, and will be rejected by the
 + *		verifier.
 + *	Return
 + *		The number of drained samples if no error was encountered while
 + *		draining samples, or 0 if no samples were present in the ring
 + *		buffer. If a user-space producer was epoll-waiting on this map,
 + *		and at least one sample was drained, they will receive an event
 + *		notification notifying them of available space in the ring
 + *		buffer. If the BPF_RB_NO_WAKEUP flag is passed to this
 + *		function, no wakeup notification will be sent. If the
 + *		BPF_RB_FORCE_WAKEUP flag is passed, a wakeup notification will
 + *		be sent even if no sample was drained.
 + *
 + *		On failure, the returned value is one of the following:
 + *
 + *		**-EBUSY** if the ring buffer is contended, and another calling
 + *		context was concurrently draining the ring buffer.
 + *
 + *		**-EINVAL** if user-space is not properly tracking the ring
 + *		buffer due to the producer position not being aligned to 8
 + *		bytes, a sample not being aligned to 8 bytes, or the producer
 + *		position not matching the advertised length of a sample.
 + *
 + *		**-E2BIG** if user-space has tried to publish a sample which is
 + *		larger than the size of the ring buffer, or which cannot fit
 + *		within a struct bpf_dynptr.
 + *
 + * void *bpf_cgrp_storage_get(struct bpf_map *map, struct cgroup *cgroup, void *value, u64 flags)
 + *	Description
 + *		Get a bpf_local_storage from the *cgroup*.
 + *
 + *		Logically, it could be thought of as getting the value from
 + *		a *map* with *cgroup* as the **key**.  From this
 + *		perspective,  the usage is not much different from
 + *		**bpf_map_lookup_elem**\ (*map*, **&**\ *cgroup*) except this
 + *		helper enforces the key must be a cgroup struct and the map must also
 + *		be a **BPF_MAP_TYPE_CGRP_STORAGE**.
 + *
 + *		In reality, the local-storage value is embedded directly inside of the
 + *		*cgroup* object itself, rather than being located in the
 + *		**BPF_MAP_TYPE_CGRP_STORAGE** map. When the local-storage value is
 + *		queried for some *map* on a *cgroup* object, the kernel will perform an
 + *		O(n) iteration over all of the live local-storage values for that
 + *		*cgroup* object until the local-storage value for the *map* is found.
 + *
 + *		An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
 + *		used such that a new bpf_local_storage will be
 + *		created if one does not exist.  *value* can be used
 + *		together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
 + *		the initial value of a bpf_local_storage.  If *value* is
 + *		**NULL**, the new bpf_local_storage will be zero initialized.
 + *	Return
 + *		A bpf_local_storage pointer is returned on success.
 + *
 + *		**NULL** if not found or there was an error in adding
 + *		a new bpf_local_storage.
 + *
 + * long bpf_cgrp_storage_delete(struct bpf_map *map, struct cgroup *cgroup)
 + *	Description
 + *		Delete a bpf_local_storage from a *cgroup*.
 + *	Return
 + *		0 on success.
 + *
 + *		**-ENOENT** if the bpf_local_storage cannot be found.
 + */
 +#define ___BPF_FUNC_MAPPER(FN, ctx...)			\
 +	FN(unspec, 0, ##ctx)				\
 +	FN(map_lookup_elem, 1, ##ctx)			\
 +	FN(map_update_elem, 2, ##ctx)			\
 +	FN(map_delete_elem, 3, ##ctx)			\
 +	FN(probe_read, 4, ##ctx)			\
 +	FN(ktime_get_ns, 5, ##ctx)			\
 +	FN(trace_printk, 6, ##ctx)			\
 +	FN(get_prandom_u32, 7, ##ctx)			\
 +	FN(get_smp_processor_id, 8, ##ctx)		\
 +	FN(skb_store_bytes, 9, ##ctx)			\
 +	FN(l3_csum_replace, 10, ##ctx)			\
 +	FN(l4_csum_replace, 11, ##ctx)			\
 +	FN(tail_call, 12, ##ctx)			\
 +	FN(clone_redirect, 13, ##ctx)			\
 +	FN(get_current_pid_tgid, 14, ##ctx)		\
 +	FN(get_current_uid_gid, 15, ##ctx)		\
 +	FN(get_current_comm, 16, ##ctx)			\
 +	FN(get_cgroup_classid, 17, ##ctx)		\
 +	FN(skb_vlan_push, 18, ##ctx)			\
 +	FN(skb_vlan_pop, 19, ##ctx)			\
 +	FN(skb_get_tunnel_key, 20, ##ctx)		\
 +	FN(skb_set_tunnel_key, 21, ##ctx)		\
 +	FN(perf_event_read, 22, ##ctx)			\
 +	FN(redirect, 23, ##ctx)				\
 +	FN(get_route_realm, 24, ##ctx)			\
 +	FN(perf_event_output, 25, ##ctx)		\
 +	FN(skb_load_bytes, 26, ##ctx)			\
 +	FN(get_stackid, 27, ##ctx)			\
 +	FN(csum_diff, 28, ##ctx)			\
 +	FN(skb_get_tunnel_opt, 29, ##ctx)		\
 +	FN(skb_set_tunnel_opt, 30, ##ctx)		\
 +	FN(skb_change_proto, 31, ##ctx)			\
 +	FN(skb_change_type, 32, ##ctx)			\
 +	FN(skb_under_cgroup, 33, ##ctx)			\
 +	FN(get_hash_recalc, 34, ##ctx)			\
 +	FN(get_current_task, 35, ##ctx)			\
 +	FN(probe_write_user, 36, ##ctx)			\
 +	FN(current_task_under_cgroup, 37, ##ctx)	\
 +	FN(skb_change_tail, 38, ##ctx)			\
 +	FN(skb_pull_data, 39, ##ctx)			\
 +	FN(csum_update, 40, ##ctx)			\
 +	FN(set_hash_invalid, 41, ##ctx)			\
 +	FN(get_numa_node_id, 42, ##ctx)			\
 +	FN(skb_change_head, 43, ##ctx)			\
 +	FN(xdp_adjust_head, 44, ##ctx)			\
 +	FN(probe_read_str, 45, ##ctx)			\
 +	FN(get_socket_cookie, 46, ##ctx)		\
 +	FN(get_socket_uid, 47, ##ctx)			\
 +	FN(set_hash, 48, ##ctx)				\
 +	FN(setsockopt, 49, ##ctx)			\
 +	FN(skb_adjust_room, 50, ##ctx)			\
 +	FN(redirect_map, 51, ##ctx)			\
 +	FN(sk_redirect_map, 52, ##ctx)			\
 +	FN(sock_map_update, 53, ##ctx)			\
 +	FN(xdp_adjust_meta, 54, ##ctx)			\
 +	FN(perf_event_read_value, 55, ##ctx)		\
 +	FN(perf_prog_read_value, 56, ##ctx)		\
 +	FN(getsockopt, 57, ##ctx)			\
 +	FN(override_return, 58, ##ctx)			\
 +	FN(sock_ops_cb_flags_set, 59, ##ctx)		\
 +	FN(msg_redirect_map, 60, ##ctx)			\
 +	FN(msg_apply_bytes, 61, ##ctx)			\
 +	FN(msg_cork_bytes, 62, ##ctx)			\
 +	FN(msg_pull_data, 63, ##ctx)			\
 +	FN(bind, 64, ##ctx)				\
 +	FN(xdp_adjust_tail, 65, ##ctx)			\
 +	FN(skb_get_xfrm_state, 66, ##ctx)		\
 +	FN(get_stack, 67, ##ctx)			\
 +	FN(skb_load_bytes_relative, 68, ##ctx)		\
 +	FN(fib_lookup, 69, ##ctx)			\
 +	FN(sock_hash_update, 70, ##ctx)			\
 +	FN(msg_redirect_hash, 71, ##ctx)		\
 +	FN(sk_redirect_hash, 72, ##ctx)			\
 +	FN(lwt_push_encap, 73, ##ctx)			\
 +	FN(lwt_seg6_store_bytes, 74, ##ctx)		\
 +	FN(lwt_seg6_adjust_srh, 75, ##ctx)		\
 +	FN(lwt_seg6_action, 76, ##ctx)			\
 +	FN(rc_repeat, 77, ##ctx)			\
 +	FN(rc_keydown, 78, ##ctx)			\
 +	FN(skb_cgroup_id, 79, ##ctx)			\
 +	FN(get_current_cgroup_id, 80, ##ctx)		\
 +	FN(get_local_storage, 81, ##ctx)		\
 +	FN(sk_select_reuseport, 82, ##ctx)		\
 +	FN(skb_ancestor_cgroup_id, 83, ##ctx)		\
 +	FN(sk_lookup_tcp, 84, ##ctx)			\
 +	FN(sk_lookup_udp, 85, ##ctx)			\
 +	FN(sk_release, 86, ##ctx)			\
 +	FN(map_push_elem, 87, ##ctx)			\
 +	FN(map_pop_elem, 88, ##ctx)			\
 +	FN(map_peek_elem, 89, ##ctx)			\
 +	FN(msg_push_data, 90, ##ctx)			\
 +	FN(msg_pop_data, 91, ##ctx)			\
 +	FN(rc_pointer_rel, 92, ##ctx)			\
 +	FN(spin_lock, 93, ##ctx)			\
 +	FN(spin_unlock, 94, ##ctx)			\
 +	FN(sk_fullsock, 95, ##ctx)			\
 +	FN(tcp_sock, 96, ##ctx)				\
 +	FN(skb_ecn_set_ce, 97, ##ctx)			\
 +	FN(get_listener_sock, 98, ##ctx)		\
 +	FN(skc_lookup_tcp, 99, ##ctx)			\
 +	FN(tcp_check_syncookie, 100, ##ctx)		\
 +	FN(sysctl_get_name, 101, ##ctx)			\
 +	FN(sysctl_get_current_value, 102, ##ctx)	\
 +	FN(sysctl_get_new_value, 103, ##ctx)		\
 +	FN(sysctl_set_new_value, 104, ##ctx)		\
 +	FN(strtol, 105, ##ctx)				\
 +	FN(strtoul, 106, ##ctx)				\
 +	FN(sk_storage_get, 107, ##ctx)			\
 +	FN(sk_storage_delete, 108, ##ctx)		\
 +	FN(send_signal, 109, ##ctx)			\
 +	FN(tcp_gen_syncookie, 110, ##ctx)		\
 +	FN(skb_output, 111, ##ctx)			\
 +	FN(probe_read_user, 112, ##ctx)			\
 +	FN(probe_read_kernel, 113, ##ctx)		\
 +	FN(probe_read_user_str, 114, ##ctx)		\
 +	FN(probe_read_kernel_str, 115, ##ctx)		\
 +	FN(tcp_send_ack, 116, ##ctx)			\
 +	FN(send_signal_thread, 117, ##ctx)		\
 +	FN(jiffies64, 118, ##ctx)			\
 +	FN(read_branch_records, 119, ##ctx)		\
 +	FN(get_ns_current_pid_tgid, 120, ##ctx)		\
 +	FN(xdp_output, 121, ##ctx)			\
 +	FN(get_netns_cookie, 122, ##ctx)		\
 +	FN(get_current_ancestor_cgroup_id, 123, ##ctx)	\
 +	FN(sk_assign, 124, ##ctx)			\
 +	FN(ktime_get_boot_ns, 125, ##ctx)		\
 +	FN(seq_printf, 126, ##ctx)			\
 +	FN(seq_write, 127, ##ctx)			\
 +	FN(sk_cgroup_id, 128, ##ctx)			\
 +	FN(sk_ancestor_cgroup_id, 129, ##ctx)		\
 +	FN(ringbuf_output, 130, ##ctx)			\
 +	FN(ringbuf_reserve, 131, ##ctx)			\
 +	FN(ringbuf_submit, 132, ##ctx)			\
 +	FN(ringbuf_discard, 133, ##ctx)			\
 +	FN(ringbuf_query, 134, ##ctx)			\
 +	FN(csum_level, 135, ##ctx)			\
 +	FN(skc_to_tcp6_sock, 136, ##ctx)		\
 +	FN(skc_to_tcp_sock, 137, ##ctx)			\
 +	FN(skc_to_tcp_timewait_sock, 138, ##ctx)	\
 +	FN(skc_to_tcp_request_sock, 139, ##ctx)		\
 +	FN(skc_to_udp6_sock, 140, ##ctx)		\
 +	FN(get_task_stack, 141, ##ctx)			\
 +	FN(load_hdr_opt, 142, ##ctx)			\
 +	FN(store_hdr_opt, 143, ##ctx)			\
 +	FN(reserve_hdr_opt, 144, ##ctx)			\
 +	FN(inode_storage_get, 145, ##ctx)		\
 +	FN(inode_storage_delete, 146, ##ctx)		\
 +	FN(d_path, 147, ##ctx)				\
 +	FN(copy_from_user, 148, ##ctx)			\
 +	FN(snprintf_btf, 149, ##ctx)			\
 +	FN(seq_printf_btf, 150, ##ctx)			\
 +	FN(skb_cgroup_classid, 151, ##ctx)		\
 +	FN(redirect_neigh, 152, ##ctx)			\
 +	FN(per_cpu_ptr, 153, ##ctx)			\
 +	FN(this_cpu_ptr, 154, ##ctx)			\
 +	FN(redirect_peer, 155, ##ctx)			\
 +	FN(task_storage_get, 156, ##ctx)		\
 +	FN(task_storage_delete, 157, ##ctx)		\
 +	FN(get_current_task_btf, 158, ##ctx)		\
 +	FN(bprm_opts_set, 159, ##ctx)			\
 +	FN(ktime_get_coarse_ns, 160, ##ctx)		\
 +	FN(ima_inode_hash, 161, ##ctx)			\
 +	FN(sock_from_file, 162, ##ctx)			\
 +	FN(check_mtu, 163, ##ctx)			\
 +	FN(for_each_map_elem, 164, ##ctx)		\
 +	FN(snprintf, 165, ##ctx)			\
 +	FN(sys_bpf, 166, ##ctx)				\
 +	FN(btf_find_by_name_kind, 167, ##ctx)		\
 +	FN(sys_close, 168, ##ctx)			\
 +	FN(timer_init, 169, ##ctx)			\
 +	FN(timer_set_callback, 170, ##ctx)		\
 +	FN(timer_start, 171, ##ctx)			\
 +	FN(timer_cancel, 172, ##ctx)			\
 +	FN(get_func_ip, 173, ##ctx)			\
 +	FN(get_attach_cookie, 174, ##ctx)		\
 +	FN(task_pt_regs, 175, ##ctx)			\
 +	FN(get_branch_snapshot, 176, ##ctx)		\
 +	FN(trace_vprintk, 177, ##ctx)			\
 +	FN(skc_to_unix_sock, 178, ##ctx)		\
 +	FN(kallsyms_lookup_name, 179, ##ctx)		\
 +	FN(find_vma, 180, ##ctx)			\
 +	FN(loop, 181, ##ctx)				\
 +	FN(strncmp, 182, ##ctx)				\
 +	FN(get_func_arg, 183, ##ctx)			\
 +	FN(get_func_ret, 184, ##ctx)			\
 +	FN(get_func_arg_cnt, 185, ##ctx)		\
 +	FN(get_retval, 186, ##ctx)			\
 +	FN(set_retval, 187, ##ctx)			\
 +	FN(xdp_get_buff_len, 188, ##ctx)		\
 +	FN(xdp_load_bytes, 189, ##ctx)			\
 +	FN(xdp_store_bytes, 190, ##ctx)			\
 +	FN(copy_from_user_task, 191, ##ctx)		\
 +	FN(skb_set_tstamp, 192, ##ctx)			\
 +	FN(ima_file_hash, 193, ##ctx)			\
 +	FN(kptr_xchg, 194, ##ctx)			\
 +	FN(map_lookup_percpu_elem, 195, ##ctx)		\
 +	FN(skc_to_mptcp_sock, 196, ##ctx)		\
 +	FN(dynptr_from_mem, 197, ##ctx)			\
 +	FN(ringbuf_reserve_dynptr, 198, ##ctx)		\
 +	FN(ringbuf_submit_dynptr, 199, ##ctx)		\
 +	FN(ringbuf_discard_dynptr, 200, ##ctx)		\
 +	FN(dynptr_read, 201, ##ctx)			\
 +	FN(dynptr_write, 202, ##ctx)			\
 +	FN(dynptr_data, 203, ##ctx)			\
 +	FN(tcp_raw_gen_syncookie_ipv4, 204, ##ctx)	\
 +	FN(tcp_raw_gen_syncookie_ipv6, 205, ##ctx)	\
 +	FN(tcp_raw_check_syncookie_ipv4, 206, ##ctx)	\
 +	FN(tcp_raw_check_syncookie_ipv6, 207, ##ctx)	\
 +	FN(ktime_get_tai_ns, 208, ##ctx)		\
 +	FN(user_ringbuf_drain, 209, ##ctx)		\
 +	FN(cgrp_storage_get, 210, ##ctx)		\
 +	FN(cgrp_storage_delete, 211, ##ctx)		\
++=======
+  * long bpf_create_slub_cache(u32 size, u32 gfp_flags, u64 ip_size_priv_type)
+  *	Description
+  *		Create a new slab cache according to the ip/size/privilege/zone
+  *	Return
+  *		The cache's address or NULL.
+  *
+  * long bpf_cache_alloc(u64 cache, u32 gfp_flags)
+  *	Description
+  *		allocate memory objects from the slab cache
+  *	Return
+  *		The allocated memory or NULL.
+  *
+  * long bpf_jmp_next(struct pt_regs *ctx, u64 nextip, u64 ret)
+  *	Description
+  *		jump the `call kmalloc`'s next instruction, and set rax.
+  *	Return
+  *		0.
+  *
+  */
+ #define __BPF_FUNC_MAPPER(FN)		\
+ 	FN(unspec),			\
+ 	FN(map_lookup_elem),		\
+ 	FN(map_update_elem),		\
+ 	FN(map_delete_elem),		\
+ 	FN(probe_read),			\
+ 	FN(ktime_get_ns),		\
+ 	FN(trace_printk),		\
+ 	FN(get_prandom_u32),		\
+ 	FN(get_smp_processor_id),	\
+ 	FN(skb_store_bytes),		\
+ 	FN(l3_csum_replace),		\
+ 	FN(l4_csum_replace),		\
+ 	FN(tail_call),			\
+ 	FN(clone_redirect),		\
+ 	FN(get_current_pid_tgid),	\
+ 	FN(get_current_uid_gid),	\
+ 	FN(get_current_comm),		\
+ 	FN(get_cgroup_classid),		\
+ 	FN(skb_vlan_push),		\
+ 	FN(skb_vlan_pop),		\
+ 	FN(skb_get_tunnel_key),		\
+ 	FN(skb_set_tunnel_key),		\
+ 	FN(perf_event_read),		\
+ 	FN(redirect),			\
+ 	FN(get_route_realm),		\
+ 	FN(perf_event_output),		\
+ 	FN(skb_load_bytes),		\
+ 	FN(get_stackid),		\
+ 	FN(csum_diff),			\
+ 	FN(skb_get_tunnel_opt),		\
+ 	FN(skb_set_tunnel_opt),		\
+ 	FN(skb_change_proto),		\
+ 	FN(skb_change_type),		\
+ 	FN(skb_under_cgroup),		\
+ 	FN(get_hash_recalc),		\
+ 	FN(get_current_task),		\
+ 	FN(probe_write_user),		\
+ 	FN(current_task_under_cgroup),	\
+ 	FN(skb_change_tail),		\
+ 	FN(skb_pull_data),		\
+ 	FN(csum_update),		\
+ 	FN(set_hash_invalid),		\
+ 	FN(get_numa_node_id),		\
+ 	FN(skb_change_head),		\
+ 	FN(xdp_adjust_head),		\
+ 	FN(probe_read_str),		\
+ 	FN(get_socket_cookie),		\
+ 	FN(get_socket_uid),		\
+ 	FN(set_hash),			\
+ 	FN(setsockopt),			\
+ 	FN(skb_adjust_room),		\
+ 	FN(redirect_map),		\
+ 	FN(sk_redirect_map),		\
+ 	FN(sock_map_update),		\
+ 	FN(xdp_adjust_meta),		\
+ 	FN(perf_event_read_value),	\
+ 	FN(perf_prog_read_value),	\
+ 	FN(getsockopt),			\
+ 	FN(override_return),		\
+ 	FN(sock_ops_cb_flags_set),	\
+ 	FN(msg_redirect_map),		\
+ 	FN(msg_apply_bytes),		\
+ 	FN(msg_cork_bytes),		\
+ 	FN(msg_pull_data),		\
+ 	FN(bind),			\
+ 	FN(xdp_adjust_tail),		\
+ 	FN(skb_get_xfrm_state),		\
+ 	FN(get_stack),			\
+ 	FN(skb_load_bytes_relative),	\
+ 	FN(fib_lookup),			\
+ 	FN(sock_hash_update),		\
+ 	FN(msg_redirect_hash),		\
+ 	FN(sk_redirect_hash),		\
+ 	FN(lwt_push_encap),		\
+ 	FN(lwt_seg6_store_bytes),	\
+ 	FN(lwt_seg6_adjust_srh),	\
+ 	FN(lwt_seg6_action),		\
+ 	FN(rc_repeat),			\
+ 	FN(rc_keydown),			\
+ 	FN(skb_cgroup_id),		\
+ 	FN(get_current_cgroup_id),	\
+ 	FN(get_local_storage),		\
+ 	FN(sk_select_reuseport),	\
+ 	FN(skb_ancestor_cgroup_id),	\
+ 	FN(sk_lookup_tcp),		\
+ 	FN(sk_lookup_udp),		\
+ 	FN(sk_release),			\
+ 	FN(map_push_elem),		\
+ 	FN(map_pop_elem),		\
+ 	FN(map_peek_elem),		\
+ 	FN(msg_push_data),		\
+ 	FN(msg_pop_data),		\
+ 	FN(rc_pointer_rel),		\
+ 	FN(spin_lock),			\
+ 	FN(spin_unlock),		\
+ 	FN(sk_fullsock),		\
+ 	FN(tcp_sock),			\
+ 	FN(skb_ecn_set_ce),		\
+ 	FN(get_listener_sock),		\
+ 	FN(skc_lookup_tcp),		\
+ 	FN(tcp_check_syncookie),	\
+ 	FN(sysctl_get_name),		\
+ 	FN(sysctl_get_current_value),	\
+ 	FN(sysctl_get_new_value),	\
+ 	FN(sysctl_set_new_value),	\
+ 	FN(strtol),			\
+ 	FN(strtoul),			\
+ 	FN(sk_storage_get),		\
+ 	FN(sk_storage_delete),		\
+ 	FN(send_signal),		\
+ 	FN(tcp_gen_syncookie),		\
+ 	FN(skb_output),			\
+ 	FN(probe_read_user),		\
+ 	FN(probe_read_kernel),		\
+ 	FN(probe_read_user_str),	\
+ 	FN(probe_read_kernel_str),	\
+ 	FN(tcp_send_ack),		\
+ 	FN(send_signal_thread),		\
+ 	FN(jiffies64),			\
+ 	FN(read_branch_records),	\
+ 	FN(get_ns_current_pid_tgid),	\
+ 	FN(xdp_output),			\
+ 	FN(get_netns_cookie),		\
+ 	FN(get_current_ancestor_cgroup_id),	\
+ 	FN(sk_assign),			\
+ 	FN(ktime_get_boot_ns),		\
+ 	FN(seq_printf),			\
+ 	FN(seq_write),			\
+ 	FN(sk_cgroup_id),		\
+ 	FN(sk_ancestor_cgroup_id),	\
+ 	FN(ringbuf_output),		\
+ 	FN(ringbuf_reserve),		\
+ 	FN(ringbuf_submit),		\
+ 	FN(ringbuf_discard),		\
+ 	FN(ringbuf_query),		\
+ 	FN(csum_level),			\
+ 	FN(skc_to_tcp6_sock),		\
+ 	FN(skc_to_tcp_sock),		\
+ 	FN(skc_to_tcp_timewait_sock),	\
+ 	FN(skc_to_tcp_request_sock),	\
+ 	FN(skc_to_udp6_sock),		\
+ 	FN(get_task_stack),		\
+ 	FN(load_hdr_opt),		\
+ 	FN(store_hdr_opt),		\
+ 	FN(reserve_hdr_opt),		\
+ 	FN(inode_storage_get),		\
+ 	FN(inode_storage_delete),	\
+ 	FN(d_path),			\
+ 	FN(copy_from_user),		\
+ 	FN(snprintf_btf),		\
+ 	FN(seq_printf_btf),		\
+ 	FN(skb_cgroup_classid),		\
+ 	FN(redirect_neigh),		\
+ 	FN(per_cpu_ptr),		\
+ 	FN(this_cpu_ptr),		\
+ 	FN(redirect_peer),		\
+ 	FN(task_storage_get),		\
+ 	FN(task_storage_delete),	\
+ 	FN(get_current_task_btf),	\
+ 	FN(bprm_opts_set),		\
+ 	FN(ktime_get_coarse_ns),	\
+ 	FN(ima_inode_hash),		\
+ 	FN(sock_from_file),		\
+ 	FN(check_mtu),			\
+ 	FN(for_each_map_elem),		\
+ 	FN(snprintf),			\
+ 	FN(sys_bpf),			\
+ 	FN(btf_find_by_name_kind),	\
+ 	FN(sys_close),			\
+ 	FN(timer_init),			\
+ 	FN(timer_set_callback),		\
+ 	FN(timer_start),		\
+ 	FN(timer_cancel),		\
+ 	FN(get_func_ip),		\
+ 	FN(get_attach_cookie),		\
+ 	FN(task_pt_regs),		\
+ 	FN(create_slub_cache),		\
+ 	FN(cache_alloc),		\
+ 	FN(jmp_next),			\
++>>>>>>> HotBPF: Prevent Kernel Heap-based Exploitation
  	/* */
  
 +/* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't
 + * know or care about integer value that is now passed as second argument
 + */
 +#define __BPF_FUNC_MAPPER_APPLY(name, value, FN) FN(name),
 +#define __BPF_FUNC_MAPPER(FN) ___BPF_FUNC_MAPPER(__BPF_FUNC_MAPPER_APPLY, FN)
 +
  /* integer value in 'imm' field of BPF_CALL instruction selects which helper
   * function eBPF program intends to call
   */
diff --cc kernel/trace/bpf_trace.c
index 5f2dcabad202,6847e9d78b06..000000000000
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@@ -1125,273 -1017,72 +1125,342 @@@ static const struct bpf_func_proto bpf_
  	.arg1_type	= ARG_PTR_TO_CTX,
  };
  
++<<<<<<< HEAD
 +BPF_CALL_1(bpf_get_attach_cookie_tracing, void *, ctx)
 +{
 +	struct bpf_trace_run_ctx *run_ctx;
 +
 +	run_ctx = container_of(current->bpf_ctx, struct bpf_trace_run_ctx, run_ctx);
 +	return run_ctx->bpf_cookie;
 +}
 +
 +static const struct bpf_func_proto bpf_get_attach_cookie_proto_tracing = {
 +	.func		= bpf_get_attach_cookie_tracing,
 +	.gpl_only	= false,
 +	.ret_type	= RET_INTEGER,
 +	.arg1_type	= ARG_PTR_TO_CTX,
 +};
 +
 +BPF_CALL_3(bpf_get_branch_snapshot, void *, buf, u32, size, u64, flags)
 +{
 +#ifndef CONFIG_X86
 +	return -ENOENT;
 +#else
 +	static const u32 br_entry_size = sizeof(struct perf_branch_entry);
 +	u32 entry_cnt = size / br_entry_size;
 +
 +	entry_cnt = static_call(perf_snapshot_branch_stack)(buf, entry_cnt);
 +
 +	if (unlikely(flags))
 +		return -EINVAL;
 +
 +	if (!entry_cnt)
 +		return -ENOENT;
 +
 +	return entry_cnt * br_entry_size;
 +#endif
 +}
 +
 +static const struct bpf_func_proto bpf_get_branch_snapshot_proto = {
 +	.func		= bpf_get_branch_snapshot,
 +	.gpl_only	= true,
 +	.ret_type	= RET_INTEGER,
 +	.arg1_type	= ARG_PTR_TO_UNINIT_MEM,
 +	.arg2_type	= ARG_CONST_SIZE_OR_ZERO,
 +};
 +
 +BPF_CALL_3(get_func_arg, void *, ctx, u32, n, u64 *, value)
 +{
 +	/* This helper call is inlined by verifier. */
 +	u64 nr_args = ((u64 *)ctx)[-1];
 +
 +	if ((u64) n >= nr_args)
 +		return -EINVAL;
 +	*value = ((u64 *)ctx)[n];
 +	return 0;
 +}
 +
 +static const struct bpf_func_proto bpf_get_func_arg_proto = {
 +	.func		= get_func_arg,
 +	.ret_type	= RET_INTEGER,
 +	.arg1_type	= ARG_PTR_TO_CTX,
 +	.arg2_type	= ARG_ANYTHING,
 +	.arg3_type	= ARG_PTR_TO_LONG,
 +};
 +
 +BPF_CALL_2(get_func_ret, void *, ctx, u64 *, value)
 +{
 +	/* This helper call is inlined by verifier. */
 +	u64 nr_args = ((u64 *)ctx)[-1];
 +
 +	*value = ((u64 *)ctx)[nr_args];
 +	return 0;
 +}
 +
 +static const struct bpf_func_proto bpf_get_func_ret_proto = {
 +	.func		= get_func_ret,
 +	.ret_type	= RET_INTEGER,
 +	.arg1_type	= ARG_PTR_TO_CTX,
 +	.arg2_type	= ARG_PTR_TO_LONG,
 +};
 +
 +BPF_CALL_1(get_func_arg_cnt, void *, ctx)
 +{
 +	/* This helper call is inlined by verifier. */
 +	return ((u64 *)ctx)[-1];
 +}
 +
 +static const struct bpf_func_proto bpf_get_func_arg_cnt_proto = {
 +	.func		= get_func_arg_cnt,
 +	.ret_type	= RET_INTEGER,
 +	.arg1_type	= ARG_PTR_TO_CTX,
 +};
 +
 +#ifdef CONFIG_KEYS
 +__diag_push();
 +__diag_ignore_all("-Wmissing-prototypes",
 +		  "kfuncs which will be used in BPF programs");
 +
 +/**
 + * bpf_lookup_user_key - lookup a key by its serial
 + * @serial: key handle serial number
 + * @flags: lookup-specific flags
 + *
 + * Search a key with a given *serial* and the provided *flags*.
 + * If found, increment the reference count of the key by one, and
 + * return it in the bpf_key structure.
 + *
 + * The bpf_key structure must be passed to bpf_key_put() when done
 + * with it, so that the key reference count is decremented and the
 + * bpf_key structure is freed.
 + *
 + * Permission checks are deferred to the time the key is used by
 + * one of the available key-specific kfuncs.
 + *
 + * Set *flags* with KEY_LOOKUP_CREATE, to attempt creating a requested
 + * special keyring (e.g. session keyring), if it doesn't yet exist.
 + * Set *flags* with KEY_LOOKUP_PARTIAL, to lookup a key without waiting
 + * for the key construction, and to retrieve uninstantiated keys (keys
 + * without data attached to them).
 + *
 + * Return: a bpf_key pointer with a valid key pointer if the key is found, a
 + *         NULL pointer otherwise.
 + */
 +__bpf_kfunc struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)
 +{
 +	key_ref_t key_ref;
 +	struct bpf_key *bkey;
 +
 +	if (flags & ~KEY_LOOKUP_ALL)
 +		return NULL;
 +
 +	/*
 +	 * Permission check is deferred until the key is used, as the
 +	 * intent of the caller is unknown here.
 +	 */
 +	key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
 +	if (IS_ERR(key_ref))
 +		return NULL;
 +
 +	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
 +	if (!bkey) {
 +		key_put(key_ref_to_ptr(key_ref));
 +		return NULL;
 +	}
 +
 +	bkey->key = key_ref_to_ptr(key_ref);
 +	bkey->has_ref = true;
 +
 +	return bkey;
 +}
 +
 +/**
 + * bpf_lookup_system_key - lookup a key by a system-defined ID
 + * @id: key ID
 + *
 + * Obtain a bpf_key structure with a key pointer set to the passed key ID.
 + * The key pointer is marked as invalid, to prevent bpf_key_put() from
 + * attempting to decrement the key reference count on that pointer. The key
 + * pointer set in such way is currently understood only by
 + * verify_pkcs7_signature().
 + *
 + * Set *id* to one of the values defined in include/linux/verification.h:
 + * 0 for the primary keyring (immutable keyring of system keys);
 + * VERIFY_USE_SECONDARY_KEYRING for both the primary and secondary keyring
 + * (where keys can be added only if they are vouched for by existing keys
 + * in those keyrings); VERIFY_USE_PLATFORM_KEYRING for the platform
 + * keyring (primarily used by the integrity subsystem to verify a kexec'ed
 + * kerned image and, possibly, the initramfs signature).
 + *
 + * Return: a bpf_key pointer with an invalid key pointer set from the
 + *         pre-determined ID on success, a NULL pointer otherwise
 + */
 +__bpf_kfunc struct bpf_key *bpf_lookup_system_key(u64 id)
 +{
 +	struct bpf_key *bkey;
 +
 +	if (system_keyring_id_check(id) < 0)
 +		return NULL;
 +
 +	bkey = kmalloc(sizeof(*bkey), GFP_ATOMIC);
 +	if (!bkey)
 +		return NULL;
 +
 +	bkey->key = (struct key *)(unsigned long)id;
 +	bkey->has_ref = false;
 +
 +	return bkey;
 +}
 +
 +/**
 + * bpf_key_put - decrement key reference count if key is valid and free bpf_key
 + * @bkey: bpf_key structure
 + *
 + * Decrement the reference count of the key inside *bkey*, if the pointer
 + * is valid, and free *bkey*.
 + */
 +__bpf_kfunc void bpf_key_put(struct bpf_key *bkey)
 +{
 +	if (bkey->has_ref)
 +		key_put(bkey->key);
 +
 +	kfree(bkey);
 +}
 +
 +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
 +/**
 + * bpf_verify_pkcs7_signature - verify a PKCS#7 signature
 + * @data_ptr: data to verify
 + * @sig_ptr: signature of the data
 + * @trusted_keyring: keyring with keys trusted for signature verification
 + *
 + * Verify the PKCS#7 signature *sig_ptr* against the supplied *data_ptr*
 + * with keys in a keyring referenced by *trusted_keyring*.
 + *
 + * Return: 0 on success, a negative value on error.
 + */
 +__bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
 +			       struct bpf_dynptr_kern *sig_ptr,
 +			       struct bpf_key *trusted_keyring)
 +{
 +	int ret;
 +
 +	if (trusted_keyring->has_ref) {
 +		/*
 +		 * Do the permission check deferred in bpf_lookup_user_key().
 +		 * See bpf_lookup_user_key() for more details.
 +		 *
 +		 * A call to key_task_permission() here would be redundant, as
 +		 * it is already done by keyring_search() called by
 +		 * find_asymmetric_key().
 +		 */
 +		ret = key_validate(trusted_keyring->key);
 +		if (ret < 0)
 +			return ret;
 +	}
 +
 +	return verify_pkcs7_signature(data_ptr->data,
 +				      __bpf_dynptr_size(data_ptr),
 +				      sig_ptr->data,
 +				      __bpf_dynptr_size(sig_ptr),
 +				      trusted_keyring->key,
 +				      VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
 +				      NULL);
 +}
 +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
 +
 +__diag_pop();
 +
 +BTF_SET8_START(key_sig_kfunc_set)
 +BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
 +BTF_ID_FLAGS(func, bpf_lookup_system_key, KF_ACQUIRE | KF_RET_NULL)
 +BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE)
 +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
 +BTF_ID_FLAGS(func, bpf_verify_pkcs7_signature, KF_SLEEPABLE)
 +#endif
 +BTF_SET8_END(key_sig_kfunc_set)
 +
 +static const struct btf_kfunc_id_set bpf_key_sig_kfunc_set = {
 +	.owner = THIS_MODULE,
 +	.set = &key_sig_kfunc_set,
 +};
 +
 +static int __init bpf_key_sig_kfuncs_init(void)
 +{
 +	return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
 +					 &bpf_key_sig_kfunc_set);
 +}
 +
 +late_initcall(bpf_key_sig_kfuncs_init);
 +#endif /* CONFIG_KEYS */
++=======
+ #ifdef CONFIG_HOTBPF
+ 
+ BPF_CALL_3(bpf_create_slub_cache, u32, size, u32, gfp_flags, u64, ip_size_priv_type)
+ {
+ 	slab_flags_t sflags = 0;
+ 	struct kmem_cache *new_cache;
+ 	char new_cache_name[64];
+ 
+ 	if (gfp_flags & ___GFP_DMA)
+ 		sflags |= SLAB_CACHE_DMA;
+ 
+ 	if (gfp_flags & ___GFP_RECLAIMABLE)
+ 		sflags |= SLAB_RECLAIM_ACCOUNT;
+ 
+ 	if (gfp_flags & ___GFP_ACCOUNT)
+ 		sflags |= SLAB_ACCOUNT;
+ 
+ 	snprintf(new_cache_name, sizeof(new_cache_name), "hotbpf_0x%x_%llx",
+ 				size, ip_size_priv_type);
+ 	new_cache = kmem_cache_create(new_cache_name, size, 4096, sflags, NULL);
+ 
+ 	return (u64)new_cache;
+ }
+ 
+ const struct bpf_func_proto bpf_create_slub_cache_proto = {
+ 	.func		= bpf_create_slub_cache,
+ 	.gpl_only	= true,
+ 	.ret_type	= RET_INTEGER,
+ 	.arg1_type	= ARG_ANYTHING,
+ 	.arg2_type	= ARG_ANYTHING,
+ 	.arg3_type	= ARG_ANYTHING,
+ };
+ 
+ BPF_CALL_2(bpf_cache_alloc, u64, cache, u32, gfp_flags)
+ {
+ 	struct kmem_cache *s = (struct kmem_cache *) cache;
+ 
+ 	return (u64)kmem_cache_alloc(s, gfp_flags);
+ }
+ 
+ const struct bpf_func_proto bpf_cache_alloc_proto = {
+ 	.func		= bpf_cache_alloc,
+ 	.gpl_only	= true,
+ 	.ret_type	= RET_INTEGER,
+ 	.arg1_type	= ARG_ANYTHING,
+ 	.arg2_type	= ARG_ANYTHING,
+ };
+ 
+ BPF_CALL_3(bpf_jmp_next, struct pt_regs *, ctx, u64, nextip, u64, ret)
+ {
+ 	ctx->ip = nextip;
+ 	ctx->ax = ret;
+ 	return 0;
+ }
+ 
+ const struct bpf_func_proto bpf_jmp_next_proto = {
+ 	.func		= bpf_jmp_next,
+ 	.gpl_only	= true,
+ 	.ret_type	= RET_INTEGER,
+ 	.arg1_type	= ARG_PTR_TO_CTX,
+ 	.arg1_type	= ARG_ANYTHING,
+ 	.arg2_type	= ARG_ANYTHING,
+ };
+ 
+ #endif
+ 
++>>>>>>> HotBPF: Prevent Kernel Heap-based Exploitation
  
  static const struct bpf_func_proto *
  bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
@@@ -1514,12 -1199,14 +1583,23 @@@
  		return &bpf_snprintf_proto;
  	case BPF_FUNC_get_func_ip:
  		return &bpf_get_func_ip_proto_tracing;
++<<<<<<< HEAD
 +	case BPF_FUNC_get_branch_snapshot:
 +		return &bpf_get_branch_snapshot_proto;
 +	case BPF_FUNC_find_vma:
 +		return &bpf_find_vma_proto;
 +	case BPF_FUNC_trace_vprintk:
 +		return bpf_get_trace_vprintk_proto();
++=======
+ #ifdef CONFIG_HOTBPF
+ 	case BPF_FUNC_create_slub_cache:
+ 		return &bpf_create_slub_cache_proto;
+ 	case BPF_FUNC_cache_alloc:
+ 		return &bpf_cache_alloc_proto;
+ 	case BPF_FUNC_jmp_next:
+ 		return &bpf_jmp_next_proto;
+ #endif
++>>>>>>> HotBPF: Prevent Kernel Heap-based Exploitation
  	default:
  		return bpf_base_func_proto(func_id);
  	}

@kernel-patches-daemon-bpf
Copy link
Author

At least one diff in series https://patchwork.kernel.org/project/netdevbpf/list/?series=767463 expired. Closing PR.

@kernel-patches-daemon-bpf kernel-patches-daemon-bpf bot deleted the series/767463=>bpf branch July 21, 2023 23:58
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 14, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 14, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 14, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 14, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 14, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 14, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel-patches-daemon-bpf bot pushed a commit that referenced this pull request Feb 15, 2024
Verifier log avoids printing the same source code line multiple times
when a consecutive block of BPF assembly instructions are covered by the
same original (C) source code line. This greatly improves verifier log
legibility.

Unfortunately, this check is imperfect and in production applications it
quite often happens that verifier log will have multiple duplicated
source lines emitted, for no apparently good reason. E.g., this is
excerpt from a real-world BPF application (with register states omitted
for clarity):

BEFORE
======
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

As can be seen, line 394 is emitted thrice, 396 is emitted twice, and
line 400 is duplicated as well. Note that there are no intermingling
other lines of source code in between these duplicates, so the issue is
not compiler reordering assembly instruction such that multiple original
source code lines are in effect.

It becomes more obvious what's going on if we look at *full* original line info
information (using btfdump for this, [0]):

  #2764: line: insn #5363 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2765: line: insn #5373 --> 394:21 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2766: line: insn #5375 --> 394:47 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2767: line: insn #5377 --> 394:3 @ ./././strobemeta_probe.bpf.c
            for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) {
  #2768: line: insn #5378 --> 414:10 @ ./././strobemeta_probe.bpf.c
            return off;

We can see that there are four line info records covering
instructions #5363 through #5377 (instruction indices are shifted due to
subprog instruction being appended to main program), all of them are
pointing to the same C source code line #394. But each of them points to
a different part of that line, which is denoted by differing column
numbers (3, 21, 47, 3).

But verifier log doesn't distinguish between parts of the same source code line
and doesn't emit this column number information, so for end user it's just a
repetitive visual noise. So let's improve the detection of repeated source code
line and avoid this.

With the changes in this patch, we get this output for the same piece of BPF
program log:

AFTER
=====
; for (int i = 0; i < STROBE_MAX_MAP_ENTRIES; ++i) { @ strobemeta_probe.bpf.c:394
5369: (07) r8 += 2                    ;
5370: (07) r7 += 16                   ;
5371: (07) r9 += 1                    ;
5372: (79) r4 = *(u64 *)(r10 -32)     ;
5373: (55) if r9 != 0xf goto pc+2
; if (i >= map->cnt) @ strobemeta_probe.bpf.c:396
5376: (79) r1 = *(u64 *)(r10 -40)     ;
5377: (79) r1 = *(u64 *)(r1 +8)       ;
5378: (dd) if r1 s<= r9 goto pc-5     ;
; descr->key_lens[i] = 0; @ strobemeta_probe.bpf.c:398
5379: (b4) w1 = 0                     ;
5380: (6b) *(u16 *)(r8 -30) = r1      ;
; task, data, off, STROBE_MAX_STR_LEN, map->entries[i].key); @ strobemeta_probe.bpf.c:400
5381: (79) r3 = *(u64 *)(r7 -8)       ;
5382: (7b) *(u64 *)(r10 -24) = r6     ;
5383: (bc) w6 = w6                    ;
; barrier_var(payload_off); @ strobemeta_probe.bpf.c:280
5384: (bf) r2 = r6                    ;
5385: (bf) r1 = r4                    ;

All the duplication is gone and the log is cleaner and less distracting.

  [0] https://github.com/anakryiko/btfdump

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20240214174100.2847419-1-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants