* [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs
@ 2026-06-24 9:36 Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc Longjun Tang
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Longjun Tang @ 2026-06-24 9:36 UTC (permalink / raw)
To: ast, andrii; +Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang
From: Longjun Tang <tanglongjun@kylinos.cn>
This series adds two new BPF kfuncs to kernel/bpf/helpers.c.
=== bpf_memcmp() ===
BPF programs frequently need to compare raw binary buffers, such as:
- MAC addresses (6 bytes) in XDP/TC for L2 filtering and forwarding;
- UUIDs (16 bytes) and hash digests for allowlist/denylist policies;
Currently there is no safe way to do this — bpf_strncmp (helper) stops
at null bytes which corrupts binary data, and hand-rolled loops are
fragile with the verifier (as evidenced by the local_memcmp() workaround
in selftests/bpf/progs/dynptr_success.c). __builtin_memcmp may work in
some cases but is not verifier-safe for arbitrary kernel memory.
bpf_memcmp() compares two memory regions byte by byte, does not treat
null bytes as terminators, and returns the standard memcmp-style -1/0/1.
=== bpf_strpbrk() ===
BPF programs parsing kernel strings need to locate delimiter characters
to split tokens. Common use cases include:
- Parsing comma-separated mount options (fscrypt,noatime);
- Tokenizing space-separated /proc entries (/proc/mounts);
- Splitting colon-separated xattr values into individual entries.
The existing strspn/strcspn kfuncs can approximate this but require two
separate calls — strcspn to find the span before a delimiter, then
strspn to skip past it. bpf_strpbrk provides the missing primitive:
locate the delimiter position directly in one call. Together, strspn +
strcspn + strpbrk cover the complete set of character-set search
operations.
=== Changelog ===
v1 -> v2:
- Fixed bpf_memcmp to use unsigned char for correct comparison of
bytes >= 0x80 (sashiko-bot)
- Improved commit messages explaining use cases and motivation
(Alexei Starovoitov, bot+bpf-ci)
- Removed duplicated kernel-doc block in bpf_strpbrk commit
(sashiko-bot)
- Fixed test_strpbrk_found_multiple to test with "l" (3 occurrences
in "hello world") instead of "w" (single occurrence) (sashiko-bot)
- Added -E2BIG too-long-string tests for bpf_strpbrk (sashiko-bot)
Longjun Tang (5):
bpf: add bpf_memcmp kfunc
selftests/bpf: rename local bpf_memcmp to avoid conflict
selftests/bpf: add tests for bpf_memcmp
bpf: add bpf_strpbrk kfunc
selftests/bpf: add tests for bpf_strpbrk
kernel/bpf/helpers.c | 88 +++++++++++++++++++
.../selftests/bpf/prog_tests/string_kfuncs.c | 2 +
.../selftests/bpf/progs/dynptr_success.c | 30 +++----
.../bpf/progs/string_kfuncs_failure1.c | 16 ++++
.../bpf/progs/string_kfuncs_failure2.c | 2 +
.../bpf/progs/string_kfuncs_success.c | 25 ++++++
6 files changed, 148 insertions(+), 15 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc
2026-06-24 9:36 [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs Longjun Tang
@ 2026-06-24 9:36 ` Longjun Tang
2026-06-24 9:48 ` sashiko-bot
2026-06-24 9:36 ` [PATCH bpf-next v2 2/5] selftests/bpf: rename local bpf_memcmp to avoid conflict Longjun Tang
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Longjun Tang @ 2026-06-24 9:36 UTC (permalink / raw)
To: ast, andrii; +Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang
From: Longjun Tang <tanglongjun@kylinos.cn>
Add bpf_memcmp() for binary memory comparison. Unlike bpf_strcmp,
null bytes do not terminate the comparison, making it suitable for
comparing fixed-size binary data.
BPF programs frequently need to compare raw binary buffers such as:
- MAC addresses (6 bytes) in XDP/TC for L2 filtering and forwarding;
- UUIDs (16 bytes) and hash digests in security policies;
Currently there is no safe way to do this:
- bpf_strncmp (helper) stops at null bytes, corrupting binary data;
- __builtin_memcmp may be used in some cases but is not verifier-safe
for arbitrary kernel memory;
- hand-rolled loops are fragile and slow, as evidenced by the
local_memcmp() workaround in selftests/bpf/progs/dynptr_success.c.
Signed-off-by: Longjun Tang <tanglongjun@kylinos.cn>
---
kernel/bpf/helpers.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b5314c9fed3c..aadcc4bfbee6 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3971,6 +3971,48 @@ __bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)
return -EFAULT;
}
+/**
+ * bpf_memcmp - Compare two memory regions byte by byte
+ * @ptr1__ign: First memory region
+ * @ptr2__ign: Second memory region
+ * @size: Number of bytes to compare
+ *
+ * Compares the first @size bytes of the memory areas @ptr1__ign and
+ * @ptr2__ign. Unlike bpf_strcmp, null bytes do not terminate the comparison.
+ *
+ * Return:
+ * * %0 - The memory regions are equal
+ * * %-1 - @ptr1__ign is less than @ptr2__ign
+ * * %1 - @ptr1__ign is greater than @ptr2__ign
+ * * %-EFAULT - Cannot read one of the memory regions
+ * * %-E2BIG - @size exceeds the maximum allowed size
+ * * %-ERANGE - One of the memory regions is outside of kernel address space
+ */
+__bpf_kfunc int bpf_memcmp(const void *ptr1__ign, const void *ptr2__ign,
+ size_t size)
+{
+ const unsigned char *p1 = ptr1__ign, *p2 = ptr2__ign;
+ unsigned char c1, c2;
+ size_t i;
+
+ if (size > XATTR_SIZE_MAX)
+ return -E2BIG;
+ if (!copy_from_kernel_nofault_allowed(p1, size) ||
+ !copy_from_kernel_nofault_allowed(p2, size))
+ return -ERANGE;
+
+ guard(pagefault)();
+ for (i = 0; i < size; i++) {
+ __get_kernel_nofault(&c1, p1 + i, char, err_out);
+ __get_kernel_nofault(&c2, p2 + i, char, err_out);
+ if (c1 != c2)
+ return c1 < c2 ? -1 : 1;
+ }
+ return 0;
+err_out:
+ return -EFAULT;
+}
+
static int __bpf_strnstr(const char *s1, const char *s2, size_t len,
bool ignore_case)
{
@@ -4850,6 +4892,7 @@ BTF_ID_FLAGS(func, bpf_strlen);
BTF_ID_FLAGS(func, bpf_strnlen);
BTF_ID_FLAGS(func, bpf_strspn);
BTF_ID_FLAGS(func, bpf_strcspn);
+BTF_ID_FLAGS(func, bpf_memcmp);
BTF_ID_FLAGS(func, bpf_strstr);
BTF_ID_FLAGS(func, bpf_strcasestr);
BTF_ID_FLAGS(func, bpf_strnstr);
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH bpf-next v2 2/5] selftests/bpf: rename local bpf_memcmp to avoid conflict
2026-06-24 9:36 [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc Longjun Tang
@ 2026-06-24 9:36 ` Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 3/5] selftests/bpf: add tests for bpf_memcmp Longjun Tang
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Longjun Tang @ 2026-06-24 9:36 UTC (permalink / raw)
To: ast, andrii; +Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang
From: Longjun Tang <tanglongjun@kylinos.cn>
The dynptr_success test defines a local inline bpf_memcmp() which
conflicts with the newly added bpf_memcmp kfunc when vmlinux.h
includes the kfunc declaration. Rename it to local_memcmp.
Signed-off-by: Longjun Tang <tanglongjun@kylinos.cn>
---
.../selftests/bpf/progs/dynptr_success.c | 30 +++++++++----------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/dynptr_success.c b/tools/testing/selftests/bpf/progs/dynptr_success.c
index e0d672d93adf..071581ff0833 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_success.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_success.c
@@ -624,7 +624,7 @@ int BPF_PROG(test_dynptr_skb_tp_btf, void *skb, void *location)
return 1;
}
-static inline int bpf_memcmp(const char *a, const char *b, u32 size)
+static inline int local_memcmp(const char *a, const char *b, u32 size)
{
int i;
@@ -650,12 +650,12 @@ int test_dynptr_copy(void *ctx)
err = bpf_dynptr_write(&src, 0, data, sz, 0);
err = err ?: bpf_dynptr_copy(&dst, 0, &src, 0, sz);
err = err ?: bpf_dynptr_read(buf, sz, &dst, 0, 0);
- err = err ?: bpf_memcmp(data, buf, sz);
+ err = err ?: local_memcmp(data, buf, sz);
/* Test that offsets are handled correctly */
err = err ?: bpf_dynptr_copy(&dst, 3, &src, 5, sz - 5);
err = err ?: bpf_dynptr_read(buf, sz - 5, &dst, 3, 0);
- err = err ?: bpf_memcmp(data + 5, buf, sz - 5);
+ err = err ?: local_memcmp(data + 5, buf, sz - 5);
bpf_ringbuf_discard_dynptr(&src, 0);
bpf_ringbuf_discard_dynptr(&dst, 0);
@@ -692,7 +692,7 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp)
err = bpf_dynptr_read(&buf, len, &ptr_xdp, i * len, 0);
if (err)
goto out;
- if (bpf_memcmp(data, buf, len) != 0)
+ if (local_memcmp(data, buf, len) != 0)
goto out;
}
@@ -713,7 +713,7 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp)
err = bpf_dynptr_read(&buf, len, &ptr_buf, i * len, 0);
if (err)
goto out;
- if (bpf_memcmp(data, buf, len) != 0)
+ if (local_memcmp(data, buf, len) != 0)
goto out;
}
@@ -727,7 +727,7 @@ int test_dynptr_copy_xdp(struct xdp_md *xdp)
err = bpf_dynptr_read(&buf, len, &ptr_xdp, 2 + i * len, 0);
if (err)
goto out;
- if (bpf_memcmp(data, buf, len) != 0)
+ if (local_memcmp(data, buf, len) != 0)
goto out;
}
@@ -750,7 +750,7 @@ int test_dynptr_memset_zero(void *ctx)
err = bpf_dynptr_from_mem(memset_zero_data, data_sz, 0, &ptr);
err = err ?: bpf_dynptr_memset(&ptr, 0, data_sz, 0);
- err = err ?: bpf_memcmp(zeroes, memset_zero_data, data_sz);
+ err = err ?: local_memcmp(zeroes, memset_zero_data, data_sz);
return 0;
}
@@ -770,7 +770,7 @@ int test_dynptr_memset_notzero(void *ctx)
err = bpf_dynptr_from_mem(memset_notzero_data, data_sz, 0, &ptr);
err = err ?: bpf_dynptr_memset(&ptr, 0, data_sz, DYNPTR_MEMSET_VAL);
- err = err ?: bpf_memcmp(expected, memset_notzero_data, data_sz);
+ err = err ?: local_memcmp(expected, memset_notzero_data, data_sz);
return 0;
}
@@ -786,7 +786,7 @@ int test_dynptr_memset_zero_offset(void *ctx)
err = bpf_dynptr_from_mem(memset_zero_offset_data, data_sz, 0, &ptr);
err = err ?: bpf_dynptr_memset(&ptr, 8, 4, 0);
- err = err ?: bpf_memcmp(expected, memset_zero_offset_data, data_sz);
+ err = err ?: local_memcmp(expected, memset_zero_offset_data, data_sz);
return 0;
}
@@ -803,7 +803,7 @@ int test_dynptr_memset_zero_adjusted(void *ctx)
err = bpf_dynptr_from_mem(memset_zero_adjusted_data, data_sz, 0, &ptr);
err = err ?: bpf_dynptr_adjust(&ptr, 4, 8);
err = err ?: bpf_dynptr_memset(&ptr, 0, bpf_dynptr_size(&ptr), 0);
- err = err ?: bpf_memcmp(expected, memset_zero_adjusted_data, data_sz);
+ err = err ?: local_memcmp(expected, memset_zero_adjusted_data, data_sz);
return 0;
}
@@ -898,7 +898,7 @@ int test_dynptr_memset_xdp_chunks(struct xdp_md *xdp)
err = bpf_dynptr_read(&buf, chunk_sz, &ptr_xdp, offset, 0);
if (err)
goto out;
- err = bpf_memcmp(buf, expected_buf, sizeof(buf));
+ err = local_memcmp(buf, expected_buf, sizeof(buf));
if (err)
goto out;
}
@@ -952,7 +952,7 @@ static __always_inline void test_dynptr_probe(void *ptr, bpf_read_dynptr_fn_t bp
break;
err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0);
- if (err || bpf_memcmp(expected_str, buf, len))
+ if (err || local_memcmp(expected_str, buf, len))
err = 1;
/* Reset buffer and dynptr */
@@ -986,7 +986,7 @@ static __always_inline void test_dynptr_probe_str(void *ptr,
err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0);
if (!len)
continue;
- if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0')
+ if (err || local_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0')
err = 1;
}
bpf_ringbuf_discard_dynptr(&ptr_buf, 0);
@@ -1012,7 +1012,7 @@ static __always_inline void test_dynptr_probe_xdp(struct xdp_md *xdp, void *ptr,
if (len > sizeof(buf))
continue;
err = err ?: bpf_dynptr_read(&buf, len, &ptr_xdp, off, 0);
- if (err || bpf_memcmp(expected_str, buf, len))
+ if (err || local_memcmp(expected_str, buf, len))
err = 1;
/* Reset buffer and dynptr */
__builtin_memset(buf, 0, sizeof(buf));
@@ -1048,7 +1048,7 @@ static __always_inline void test_dynptr_probe_str_xdp(struct xdp_md *xdp, void *
if (!len)
continue;
- if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0')
+ if (err || local_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0')
err = 1;
__builtin_memset(buf, 0, sizeof(buf));
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH bpf-next v2 3/5] selftests/bpf: add tests for bpf_memcmp
2026-06-24 9:36 [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 2/5] selftests/bpf: rename local bpf_memcmp to avoid conflict Longjun Tang
@ 2026-06-24 9:36 ` Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 4/5] bpf: add bpf_strpbrk kfunc Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk Longjun Tang
4 siblings, 0 replies; 10+ messages in thread
From: Longjun Tang @ 2026-06-24 9:36 UTC (permalink / raw)
To: ast, andrii; +Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang
From: Longjun Tang <tanglongjun@kylinos.cn>
Add functional tests for equality, ordering, zero-size, and null-byte
handling. Add negative tests for NULL, user-space, and invalid kernel
pointer arguments.
Signed-off-by: Longjun Tang <tanglongjun@kylinos.cn>
---
.../bpf/progs/string_kfuncs_failure1.c | 9 +++++++++
.../selftests/bpf/progs/string_kfuncs_success.c | 17 +++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
index bddc4e8579d2..a85d64605d0e 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
@@ -9,6 +9,9 @@
char *user_ptr = (char *)1;
char *invalid_kern_ptr = (char *)-1;
+extern int bpf_memcmp(const void *ptr1__ign, const void *ptr2__ign,
+ size_t size) __ksym;
+
/*
* When passing userspace pointers, the error code differs based on arch:
* -ERANGE on arches with non-overlapping address spaces
@@ -53,6 +56,8 @@ SEC("syscall") __retval(USER_PTR_ERR)int test_strnstr_null1(void *ctx) { return
SEC("syscall") __retval(USER_PTR_ERR)int test_strnstr_null2(void *ctx) { return bpf_strnstr("hello", NULL, 1); }
SEC("syscall") __retval(USER_PTR_ERR)int test_strncasestr_null1(void *ctx) { return bpf_strncasestr(NULL, "hello", 1); }
SEC("syscall") __retval(USER_PTR_ERR)int test_strncasestr_null2(void *ctx) { return bpf_strncasestr("hello", NULL, 1); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_memcmp_null1(void *ctx) { return bpf_memcmp(NULL, "x", 1); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_memcmp_null2(void *ctx) { return bpf_memcmp("x", NULL, 1); }
/* Passing userspace ptr to string kfuncs */
SEC("syscall") __retval(USER_PTR_ERR) int test_strcmp_user_ptr1(void *ctx) { return bpf_strcmp(user_ptr, "hello"); }
@@ -79,6 +84,8 @@ SEC("syscall") __retval(USER_PTR_ERR) int test_strnstr_user_ptr1(void *ctx) { re
SEC("syscall") __retval(USER_PTR_ERR) int test_strnstr_user_ptr2(void *ctx) { return bpf_strnstr("hello", user_ptr, 1); }
SEC("syscall") __retval(USER_PTR_ERR) int test_strncasestr_user_ptr1(void *ctx) { return bpf_strncasestr(user_ptr, "hello", 1); }
SEC("syscall") __retval(USER_PTR_ERR) int test_strncasestr_user_ptr2(void *ctx) { return bpf_strncasestr("hello", user_ptr, 1); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_memcmp_user_ptr1(void *ctx) { return bpf_memcmp(user_ptr, "x", 1); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_memcmp_user_ptr2(void *ctx) { return bpf_memcmp("x", user_ptr, 1); }
#endif /* __TARGET_ARCH_s390 */
@@ -107,5 +114,7 @@ SEC("syscall") __retval(-EFAULT) int test_strnstr_pagefault1(void *ctx) { return
SEC("syscall") __retval(-EFAULT) int test_strnstr_pagefault2(void *ctx) { return bpf_strnstr("hello", invalid_kern_ptr, 1); }
SEC("syscall") __retval(-EFAULT) int test_strncasestr_pagefault1(void *ctx) { return bpf_strncasestr(invalid_kern_ptr, "hello", 1); }
SEC("syscall") __retval(-EFAULT) int test_strncasestr_pagefault2(void *ctx) { return bpf_strncasestr("hello", invalid_kern_ptr, 1); }
+SEC("syscall") __retval(-EFAULT) int test_memcmp_pagefault1(void *ctx) { return bpf_memcmp(invalid_kern_ptr, "x", 1); }
+SEC("syscall") __retval(-EFAULT) int test_memcmp_pagefault2(void *ctx) { return bpf_memcmp("x", invalid_kern_ptr, 1); }
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_success.c b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
index f65b1226a81a..c2be9edcd282 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
@@ -7,6 +7,9 @@
char str[] = "hello world";
+extern int bpf_memcmp(const void *ptr1__ign, const void *ptr2__ign,
+ size_t size) __ksym;
+
#define __test(retval) SEC("syscall") __success __retval(retval)
/* Functional tests */
@@ -60,4 +63,18 @@ __test(-ENOENT) int test_strncasestr_notfound2(void *ctx) { return bpf_strncases
__test(-ENOENT) int test_strncasestr_notfound3(void *ctx) { return bpf_strncasestr("", "a", 0); }
__test(0) int test_strncasestr_empty(void *ctx) { return bpf_strncasestr(str, "", 1); }
+/* bpf_memcmp - functional tests */
+char data1[] = "hello world";
+char data2[] = "hello world";
+char data3[] = "hello worle";
+__test(0) int test_memcmp_eq(void *ctx) { return bpf_memcmp(data1, data2, sizeof(data1)); }
+__test(-1) int test_memcmp_neq_less(void *ctx) { return bpf_memcmp(data1, data3, sizeof(data1)); }
+__test(1) int test_memcmp_neq_greater(void *ctx) { return bpf_memcmp(data3, data1, sizeof(data1)); }
+__test(0) int test_memcmp_size_zero(void *ctx) { return bpf_memcmp(data1, data3, 0); }
+/* memcmp should NOT stop at null byte - compare past '\0' */
+char bin1[] = { 'a', '\0', 'c' };
+char bin2[] = { 'a', '\0', 'd' };
+__test(0) int test_memcmp_null_byte_eq(void *ctx) { return bpf_memcmp(bin1, bin1, sizeof(bin1)); }
+__test(-1) int test_memcmp_null_byte_neq(void *ctx) { return bpf_memcmp(bin1, bin2, sizeof(bin1)); }
+
char _license[] SEC("license") = "GPL";
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH bpf-next v2 4/5] bpf: add bpf_strpbrk kfunc
2026-06-24 9:36 [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs Longjun Tang
` (2 preceding siblings ...)
2026-06-24 9:36 ` [PATCH bpf-next v2 3/5] selftests/bpf: add tests for bpf_memcmp Longjun Tang
@ 2026-06-24 9:36 ` Longjun Tang
2026-06-24 9:49 ` sashiko-bot
2026-06-24 9:36 ` [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk Longjun Tang
4 siblings, 1 reply; 10+ messages in thread
From: Longjun Tang @ 2026-06-24 9:36 UTC (permalink / raw)
To: ast, andrii; +Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang
From: Longjun Tang <tanglongjun@kylinos.cn>
Add bpf_strpbrk() that returns the index of the first character in a
string matching any character from the accept set, or -ENOENT if no
match is found.
BPF programs parsing kernel strings often need to locate delimiter
characters to split tokens. Common use cases include:
- Parsing comma-separated mount options (e.g., fscrypt,noatime);
- Tokenizing space-separated /proc entries (e.g., /proc/mounts);
- Splitting colon-separated xattr values into individual entries.
The existing strspn/strcspn kfuncs can be used for this but require
two separate calls: strcspn to find the span before a delimiter,
then strspn to skip past it. bpf_strpbrk provides the missing
primitive — find the delimiter position directly in one call.
Together, strspn + strcspn + strpbrk cover the complete set of
C standard library character-set search operations.
Signed-off-by: Longjun Tang <tanglongjun@kylinos.cn>
---
kernel/bpf/helpers.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index aadcc4bfbee6..ead00ec314f5 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3971,6 +3971,50 @@ __bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)
return -EFAULT;
}
+/**
+ * bpf_strpbrk - Find the first occurrence of a set of characters in a string
+ * @s__ign: The string to be searched
+ * @accept__ign: The string containing the characters to search for
+ *
+ * Return:
+ * * >=0 - Index of the first character in @s__ign that matches any
+ * character in @accept__ign
+ * * %-ENOENT - No character of @accept__ign found in @s__ign
+ * * %-EFAULT - Cannot read one of the strings
+ * * %-E2BIG - One of the strings is too large
+ * * %-ERANGE - One of the strings is outside of kernel address space
+ */
+__bpf_kfunc int bpf_strpbrk(const char *s__ign, const char *accept__ign)
+{
+ char cs, ca;
+ int i, j;
+
+ if (!copy_from_kernel_nofault_allowed(s__ign, 1) ||
+ !copy_from_kernel_nofault_allowed(accept__ign, 1)) {
+ return -ERANGE;
+ }
+
+ guard(pagefault)();
+ for (i = 0; i < XATTR_SIZE_MAX; i++) {
+ __get_kernel_nofault(&cs, s__ign, char, err_out);
+ if (cs == '\0')
+ return -ENOENT;
+ for (j = 0; j < XATTR_SIZE_MAX; j++) {
+ __get_kernel_nofault(&ca, accept__ign + j, char, err_out);
+ if (ca == '\0')
+ break;
+ if (cs == ca)
+ return i;
+ }
+ if (j == XATTR_SIZE_MAX)
+ return -E2BIG;
+ s__ign++;
+ }
+ return -E2BIG;
+err_out:
+ return -EFAULT;
+}
+
/**
* bpf_memcmp - Compare two memory regions byte by byte
* @ptr1__ign: First memory region
@@ -4892,6 +4936,7 @@ BTF_ID_FLAGS(func, bpf_strlen);
BTF_ID_FLAGS(func, bpf_strnlen);
BTF_ID_FLAGS(func, bpf_strspn);
BTF_ID_FLAGS(func, bpf_strcspn);
+BTF_ID_FLAGS(func, bpf_strpbrk);
BTF_ID_FLAGS(func, bpf_memcmp);
BTF_ID_FLAGS(func, bpf_strstr);
BTF_ID_FLAGS(func, bpf_strcasestr);
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk
2026-06-24 9:36 [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs Longjun Tang
` (3 preceding siblings ...)
2026-06-24 9:36 ` [PATCH bpf-next v2 4/5] bpf: add bpf_strpbrk kfunc Longjun Tang
@ 2026-06-24 9:36 ` Longjun Tang
2026-06-24 9:48 ` sashiko-bot
2026-06-24 12:08 ` bot+bpf-ci
4 siblings, 2 replies; 10+ messages in thread
From: Longjun Tang @ 2026-06-24 9:36 UTC (permalink / raw)
To: ast, andrii; +Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang
From: Longjun Tang <tanglongjun@kylinos.cn>
Add functional tests for first-match, multiple-match, not-found, and
empty-accept cases. Add negative tests for NULL, user-space, and
invalid kernel pointer arguments.
Signed-off-by: Longjun Tang <tanglongjun@kylinos.cn>
---
tools/testing/selftests/bpf/prog_tests/string_kfuncs.c | 2 ++
.../testing/selftests/bpf/progs/string_kfuncs_failure1.c | 7 +++++++
.../testing/selftests/bpf/progs/string_kfuncs_failure2.c | 2 ++
tools/testing/selftests/bpf/progs/string_kfuncs_success.c | 8 ++++++++
4 files changed, 19 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
index 300032a19445..959ff37110a1 100644
--- a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
+++ b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
@@ -20,6 +20,8 @@ static const char * const test_cases[] = {
"strspn_accept",
"strcspn_str",
"strcspn_reject",
+ "strpbrk_str",
+ "strpbrk_accept",
"strstr",
"strcasestr",
"strnstr",
diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
index a85d64605d0e..e67fe9700e59 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
@@ -9,6 +9,7 @@
char *user_ptr = (char *)1;
char *invalid_kern_ptr = (char *)-1;
+extern int bpf_strpbrk(const char *s__ign, const char *accept__ign) __ksym;
extern int bpf_memcmp(const void *ptr1__ign, const void *ptr2__ign,
size_t size) __ksym;
@@ -58,6 +59,8 @@ SEC("syscall") __retval(USER_PTR_ERR)int test_strncasestr_null1(void *ctx) { re
SEC("syscall") __retval(USER_PTR_ERR)int test_strncasestr_null2(void *ctx) { return bpf_strncasestr("hello", NULL, 1); }
SEC("syscall") __retval(USER_PTR_ERR)int test_memcmp_null1(void *ctx) { return bpf_memcmp(NULL, "x", 1); }
SEC("syscall") __retval(USER_PTR_ERR)int test_memcmp_null2(void *ctx) { return bpf_memcmp("x", NULL, 1); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_strpbrk_null1(void *ctx) { return bpf_strpbrk(NULL, "x"); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_strpbrk_null2(void *ctx) { return bpf_strpbrk("x", NULL); }
/* Passing userspace ptr to string kfuncs */
SEC("syscall") __retval(USER_PTR_ERR) int test_strcmp_user_ptr1(void *ctx) { return bpf_strcmp(user_ptr, "hello"); }
@@ -86,6 +89,8 @@ SEC("syscall") __retval(USER_PTR_ERR) int test_strncasestr_user_ptr1(void *ctx)
SEC("syscall") __retval(USER_PTR_ERR) int test_strncasestr_user_ptr2(void *ctx) { return bpf_strncasestr("hello", user_ptr, 1); }
SEC("syscall") __retval(USER_PTR_ERR) int test_memcmp_user_ptr1(void *ctx) { return bpf_memcmp(user_ptr, "x", 1); }
SEC("syscall") __retval(USER_PTR_ERR) int test_memcmp_user_ptr2(void *ctx) { return bpf_memcmp("x", user_ptr, 1); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_strpbrk_user_ptr1(void *ctx) { return bpf_strpbrk(user_ptr, "x"); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_strpbrk_user_ptr2(void *ctx) { return bpf_strpbrk("x", user_ptr); }
#endif /* __TARGET_ARCH_s390 */
@@ -116,5 +121,7 @@ SEC("syscall") __retval(-EFAULT) int test_strncasestr_pagefault1(void *ctx) { re
SEC("syscall") __retval(-EFAULT) int test_strncasestr_pagefault2(void *ctx) { return bpf_strncasestr("hello", invalid_kern_ptr, 1); }
SEC("syscall") __retval(-EFAULT) int test_memcmp_pagefault1(void *ctx) { return bpf_memcmp(invalid_kern_ptr, "x", 1); }
SEC("syscall") __retval(-EFAULT) int test_memcmp_pagefault2(void *ctx) { return bpf_memcmp("x", invalid_kern_ptr, 1); }
+SEC("syscall") __retval(-EFAULT) int test_strpbrk_pagefault1(void *ctx) { return bpf_strpbrk(invalid_kern_ptr, "x"); }
+SEC("syscall") __retval(-EFAULT) int test_strpbrk_pagefault2(void *ctx) { return bpf_strpbrk("x", invalid_kern_ptr); }
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
index 412c53b87b18..d3b978e453bd 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
@@ -19,6 +19,8 @@ SEC("syscall") int test_strspn_str_too_long(void *ctx) { return bpf_strspn(long_
SEC("syscall") int test_strspn_accept_too_long(void *ctx) { return bpf_strspn("b", long_str); }
SEC("syscall") int test_strcspn_str_too_long(void *ctx) { return bpf_strcspn(long_str, "b"); }
SEC("syscall") int test_strcspn_reject_too_long(void *ctx) { return bpf_strcspn("b", long_str); }
+SEC("syscall") int test_strpbrk_str_too_long(void *ctx) { return bpf_strpbrk(long_str, "z"); }
+SEC("syscall") int test_strpbrk_accept_too_long(void *ctx) { return bpf_strpbrk("b", long_str); }
SEC("syscall") int test_strstr_too_long(void *ctx) { return bpf_strstr(long_str, "hello"); }
SEC("syscall") int test_strcasestr_too_long(void *ctx) { return bpf_strcasestr(long_str, "hello"); }
SEC("syscall") int test_strnstr_too_long(void *ctx) { return bpf_strnstr(long_str, "hello", sizeof(long_str)); }
diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_success.c b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
index c2be9edcd282..25b8c897da54 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
@@ -7,6 +7,7 @@
char str[] = "hello world";
+extern int bpf_strpbrk(const char *s__ign, const char *accept__ign) __ksym;
extern int bpf_memcmp(const void *ptr1__ign, const void *ptr2__ign,
size_t size) __ksym;
@@ -63,6 +64,13 @@ __test(-ENOENT) int test_strncasestr_notfound2(void *ctx) { return bpf_strncases
__test(-ENOENT) int test_strncasestr_notfound3(void *ctx) { return bpf_strncasestr("", "a", 0); }
__test(0) int test_strncasestr_empty(void *ctx) { return bpf_strncasestr(str, "", 1); }
+/* bpf_strpbrk - functional tests */
+__test(0) int test_strpbrk_found_first(void *ctx) { return bpf_strpbrk(str, "h"); }
+__test(4) int test_strpbrk_found_middle(void *ctx) { return bpf_strpbrk(str, "ow"); }
+__test(2) int test_strpbrk_found_multiple(void *ctx) { return bpf_strpbrk(str, "l"); }
+__test(-ENOENT) int test_strpbrk_notfound(void *ctx) { return bpf_strpbrk(str, "xyz"); }
+__test(-ENOENT) int test_strpbrk_empty_accept(void *ctx) { return bpf_strpbrk(str, ""); }
+
/* bpf_memcmp - functional tests */
char data1[] = "hello world";
char data2[] = "hello world";
--
2.25.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk
2026-06-24 9:36 ` [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk Longjun Tang
@ 2026-06-24 9:48 ` sashiko-bot
2026-06-24 12:08 ` bot+bpf-ci
1 sibling, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-06-24 9:48 UTC (permalink / raw)
To: Longjun Tang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The string search kfuncs (bpf_strpbrk, bpf_strspn, bpf_strcspn) have an O(N*M) algorithmic complexity that can trigger up to 4.29 billion memory reads in the kernel, stalling the CPU. The newly added tests explicitly mask this vulnerability.
--
commit 8efd73628e6ad8d84061c17ed578ece973a232c2
Author: Longjun Tang <tanglongjun@kylinos.cn>
selftests/bpf: add tests for bpf_strpbrk
Add functional tests for first-match, multiple-match, not-found, and
empty-accept cases. Add negative tests for NULL, user-space, and
invalid kernel pointer arguments.
> diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
> index 412c53b87b182..d3b978e453bd7 100644
> --- a/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
> +++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
> @@ -19,6 +19,8 @@ SEC("syscall") int test_strspn_str_too_long(void *ctx) { return bpf_strspn(long_
> SEC("syscall") int test_strspn_accept_too_long(void *ctx) { return bpf_strspn("b", long_str); }
> SEC("syscall") int test_strcspn_str_too_long(void *ctx) { return bpf_strcspn(long_str, "b"); }
> SEC("syscall") int test_strcspn_reject_too_long(void *ctx) { return bpf_strcspn("b", long_str); }
> +SEC("syscall") int test_strpbrk_str_too_long(void *ctx) { return bpf_strpbrk(long_str, "z"); }
> +SEC("syscall") int test_strpbrk_accept_too_long(void *ctx) { return bpf_strpbrk("b", long_str); }
[Severity: High]
This is a pre-existing issue, but by only testing a single large string
against a 1-byte string, do these tests mask an O(N*M) algorithmic
complexity in the underlying implementation?
Looking at the nested loops in bpf_strpbrk():
kernel/bpf/helpers.c:bpf_strpbrk() {
...
for (i = 0; i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&cs, s__ign, char, err_out);
if (cs == '\0')
return -ENOENT;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
__get_kernel_nofault(&ca, accept__ign + j, char, err_out);
...
}
Since XATTR_SIZE_MAX is 65536, if a BPF program passes two 64KB buffers
constructed to avoid early loop termination (e.g., no matching characters
and a null-terminator only at the very end of the inner string), couldn't
the function perform up to 4.29 billion calls to __get_kernel_nofault()?
Could this stall the CPU for several seconds, bypassing BPF execution time
limits and potentially triggering a watchdog soft-lockup?
Would it be worth adding a test case that passes long_str for both arguments
to expose how the kernel handles this worst-case scenario?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624093638.117062-1-lange_tang@163.com?part=5
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc
2026-06-24 9:36 ` [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc Longjun Tang
@ 2026-06-24 9:48 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-06-24 9:48 UTC (permalink / raw)
To: Longjun Tang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] bpf_memcmp bypasses verifier memory bounds checking by using __ign arguments, creating an arbitrary kernel memory read oracle.
--
commit 29136173f7ef78c9e1fa53cea458359e9ef94899
Author: Longjun Tang <tanglongjun@kylinos.cn>
bpf: add bpf_memcmp kfunc
This commit adds a new bpf_memcmp kfunc for binary memory comparison in BPF
programs. Unlike bpf_strcmp, null bytes do not terminate the comparison,
making it suitable for comparing fixed-size binary data like MAC addresses
and UUIDs.
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 8e196c9b7c50f..897e8d71eae45 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
[ ... ]
> @@ -4070,6 +4070,48 @@ __bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)
> return -EFAULT;
> }
>
> +/**
> + * bpf_memcmp - Compare two memory regions byte by byte
> + * @ptr1__ign: First memory region
> + * @ptr2__ign: Second memory region
> + * @size: Number of bytes to compare
> + *
> + * Compares the first @size bytes of the memory areas @ptr1__ign and
> + * @ptr2__ign. Unlike bpf_strcmp, null bytes do not terminate the comparison.
> + *
> + * Return:
> + * * %0 - The memory regions are equal
> + * * %-1 - @ptr1__ign is less than @ptr2__ign
> + * * %1 - @ptr1__ign is greater than @ptr2__ign
> + * * %-EFAULT - Cannot read one of the memory regions
> + * * %-E2BIG - @size exceeds the maximum allowed size
> + * * %-ERANGE - One of the memory regions is outside of kernel address space
> + */
> +__bpf_kfunc int bpf_memcmp(const void *ptr1__ign, const void *ptr2__ign,
> + size_t size)
[Severity: Critical]
Does the __ign suffix on the pointer arguments bypass the verifier's memory
bounds checking?
If the verifier skips type and bounds checking for ptr1__ign and ptr2__ign,
could this regression allow passing arbitrary scalars as pointers?
Since the function reads from these addresses using __get_kernel_nofault
and returns a byte-by-byte comparison result, can this be used as an
arbitrary kernel memory read oracle via binary search?
Would it be safer to use KF_ARG_PTR_TO_MEM (for example, by using a __sz
suffix) to enforce static verifier bounds checking instead of relying on
runtime nofault reads?
> +{
> + const unsigned char *p1 = ptr1__ign, *p2 = ptr2__ign;
> + unsigned char c1, c2;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624093638.117062-1-lange_tang@163.com?part=1
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH bpf-next v2 4/5] bpf: add bpf_strpbrk kfunc
2026-06-24 9:36 ` [PATCH bpf-next v2 4/5] bpf: add bpf_strpbrk kfunc Longjun Tang
@ 2026-06-24 9:49 ` sashiko-bot
0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-06-24 9:49 UTC (permalink / raw)
To: Longjun Tang; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [Critical] Incomplete bounds check in `bpf_strpbrk` allows pointer wraparound and out-of-bounds reads.
- [High] Unoptimized O(N*M) nested loop in `bpf_strpbrk` causes CPU exhaustion and Denial of Service.
Pre-existing issues:
- [Critical] Incomplete bounds checks in pre-existing string kfuncs allow pointer wraparound and arbitrary memory reads.
- [High] Pre-existing string kfuncs (`bpf_strcspn`, `bpf_strspn`, `bpf_strnstr`, etc.) contain O(N*M) nested loops vulnerable to CPU exhaustion.
--
commit eccea6a6a708ac6a6e30a1e094434ed92118f8f0
Author: Longjun Tang <tanglongjun@kylinos.cn>
bpf: add bpf_strpbrk kfunc
Add bpf_strpbrk() that returns the index of the first character in a
string matching any character from the accept set, or -ENOENT if no
match is found.
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
[ ... ]
> +__bpf_kfunc int bpf_strpbrk(const char *s__ign, const char *accept__ign)
> +{
> + char cs, ca;
> + int i, j;
> +
> + if (!copy_from_kernel_nofault_allowed(s__ign, 1) ||
> + !copy_from_kernel_nofault_allowed(accept__ign, 1)) {
[Severity: Critical]
Since the __ign suffix allows arbitrary scalar values as pointers, could
checking only the first byte allow a pointer near ULONG_MAX
(e.g. ULONG_MAX - 5) to pass this validation?
Because the loop below increments the pointer up to XATTR_SIZE_MAX times,
it appears the pointer could wrap around to user space (0). Since
__get_kernel_nofault() uses __get_user(), could this allow reading
arbitrary user-space memory, bypassing security boundaries?
[Severity: Critical]
This is a pre-existing issue, but other string functions like bpf_strcspn()
and __bpf_strnstr() use this same 1-byte validation pattern.
In __bpf_strnstr(), accessing s1 + i + j can reach up to 131,070 bytes past
the start pointer, bypassing mmap_min_addr and allowing direct user space
memory reads from within BPF. Could this be updated across all string kfuncs
to validate the entire memory range being accessed?
> + return -ERANGE;
> + }
> +
> + guard(pagefault)();
> + for (i = 0; i < XATTR_SIZE_MAX; i++) {
> + __get_kernel_nofault(&cs, s__ign, char, err_out);
> + if (cs == '\0')
> + return -ENOENT;
> + for (j = 0; j < XATTR_SIZE_MAX; j++) {
[Severity: High]
Does this nested loop introduce a potential CPU stall or soft lockup?
If a BPF program passes 64KB strings for both s__ign and accept__ign where
no characters match and the null terminator is placed near the end of
accept__ign, the outer loop could execute 65,536 times with the inner loop
executing ~65,535 times per iteration.
With page faults disabled via guard(pagefault)(), could this lead to
~4.29 billion memory accesses, potentially causing a Denial of Service?
[Severity: High]
This is a pre-existing issue, but other kfuncs like bpf_strcspn() and
bpf_strspn() also contain these XATTR_SIZE_MAX nested loops.
Could crafted strings force those inner loops to run fully for every
iteration of the outer loop, causing the same CPU exhaustion? Is there a
way to optimize these searches or limit the execution time?
> + __get_kernel_nofault(&ca, accept__ign + j, char, err_out);
> + if (ca == '\0')
> + break;
> + if (cs == ca)
> + return i;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624093638.117062-1-lange_tang@163.com?part=4
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk
2026-06-24 9:36 ` [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk Longjun Tang
2026-06-24 9:48 ` sashiko-bot
@ 2026-06-24 12:08 ` bot+bpf-ci
1 sibling, 0 replies; 10+ messages in thread
From: bot+bpf-ci @ 2026-06-24 12:08 UTC (permalink / raw)
To: lange_tang, ast, andrii
Cc: yonghong.song, jolsa, bpf, tanglongjun, lange_tang, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 869 bytes --]
> diff --git a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
> index 300032a19445..959ff37110a1 100644
> --- a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
> +++ b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
> @@ -20,6 +20,8 @@ static const char * const test_cases[] = {
> "strspn_accept",
> "strcspn_str",
> "strcspn_reject",
> + "strpbrk_str",
> + "strpbrk_accept",
This isn't a bug, but should the two new "strpbrk_str" and
"strpbrk_accept" entries use a single tab to match the indentation of
the other entries in test_cases[]?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28095722357
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-24 12:08 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-24 9:36 [PATCH bpf-next v2 0/5] bpf: add bpf_memcmp and bpf_strpbrk kfuncs Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 1/5] bpf: add bpf_memcmp kfunc Longjun Tang
2026-06-24 9:48 ` sashiko-bot
2026-06-24 9:36 ` [PATCH bpf-next v2 2/5] selftests/bpf: rename local bpf_memcmp to avoid conflict Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 3/5] selftests/bpf: add tests for bpf_memcmp Longjun Tang
2026-06-24 9:36 ` [PATCH bpf-next v2 4/5] bpf: add bpf_strpbrk kfunc Longjun Tang
2026-06-24 9:49 ` sashiko-bot
2026-06-24 9:36 ` [PATCH bpf-next v2 5/5] selftests/bpf: add tests for bpf_strpbrk Longjun Tang
2026-06-24 9:48 ` sashiko-bot
2026-06-24 12:08 ` bot+bpf-ci
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.