* [PATCH bpf-next v2 1/2] bpf: add bpf_strcat,bpf_strncat kfunc
[not found] <cover.1784078494.git.rtoax@foxmail.com>
@ 2026-07-15 1:27 ` Rong Tao
2026-07-15 8:56 ` Viktor Malik
2026-07-15 1:27 ` [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs Rong Tao
1 sibling, 1 reply; 6+ messages in thread
From: Rong Tao @ 2026-07-15 1:27 UTC (permalink / raw)
To: andrii, vmalik, ast
Cc: rtoax, Rong Tao, Daniel Borkmann, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Yuzuki Ishiyama,
open list:BPF [GENERAL] (Safe Dynamic Programs and Tools),
open list, open list:KERNEL SELFTEST FRAMEWORK
From: Rong Tao <rongtao@cestc.cn>
Add string concatenation kfuncs, prototype:
int bpf_strcat(char *dst__ign, u32 dst__sz, const char *src__ign);
int bpf_strncat(char *dst__ign, u32 dst__sz, const char *src__ign, u32 len);
This differs from the glibc library functions strcat and strncat, which,
for safety reasons, require the size of the target string's memory space
as a parameter.
Signed-off-by: Rong Tao <rongtao@cestc.cn>
---
kernel/bpf/helpers.c | 92 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index c18f1e16edee..401f94efd687 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4195,6 +4195,96 @@ __bpf_kfunc int bpf_strncasestr(const char *s1__ign, const char *s2__ign,
return __bpf_strnstr(s1__ign, s2__ign, len, true);
}
+static int __bpf_strncat(char *dst, u32 dsz, const char *src, u32 sz)
+{
+ int dlen, slen, space, copied;
+ char cs = '?';
+
+ if (!copy_from_kernel_nofault_allowed(dst, 1) ||
+ !copy_from_kernel_nofault_allowed(src, 1)) {
+ return -ERANGE;
+ }
+
+ dlen = bpf_strnlen(dst, dsz);
+ if (dlen < 0)
+ return dlen;
+ slen = bpf_strnlen(src, sz);
+ if (slen < 0)
+ return slen;
+
+ if (dlen >= dsz || sz == 0 || dsz == 0)
+ return -EINVAL;
+
+ space = dsz - dlen;
+ if (space <= 1 || space < min(slen, sz) + 1)
+ return -E2BIG;
+
+ guard(pagefault)();
+ for (copied = 0; copied < space - 1 && copied < slen; copied++) {
+ __get_kernel_nofault(&cs, src, char, err_out);
+ if (cs == '\0')
+ break;
+
+ __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
+
+ src++;
+ }
+ cs = '\0';
+ __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
+
+ __get_kernel_nofault(&cs, src, char, err_out);
+ if (cs != '\0' && sz > copied)
+ return -E2BIG;
+
+ return dlen + copied;
+err_out:
+ return -EFAULT;
+}
+
+/**
+ * bpf_strcat - Append non-null bytes from a source string, and null-terminate
+ * the result
+ * @dst: Destination string.
+ * @dst__sz: Maximum bytes of @dst__ign, includes the trailing NUL.
+ * @src__ign: Source string.
+ *
+ * Return:
+ * * >=0 - Length of the concatenated string.
+ *
+ * * %-EINVAL - String @dst__ign is invalid.
+ * * %-EFAULT - Cannot read or write one of the strings.
+ * * %-E2BIG - String @src__ign is too large or the remaining space in
+ * @dst__ign is too small.
+ * * %-ERANGE - One of the strings is outside of kernel address space
+ */
+__bpf_kfunc int bpf_strcat(char *dst, u32 dst__sz, const char *src__ign)
+{
+ return __bpf_strncat(dst, dst__sz, src__ign, XATTR_SIZE_MAX);
+}
+
+/**
+ * bpf_strncat - Append non-null bytes from a source string, and null-terminate
+ * the result
+ * @dst: Destination string.
+ * @dst__sz: Maximum bytes of @dst__ign, includes the trailing NUL.
+ * @src__ign: Source string.
+ * @len: the maximum number of characters to concatenate
+ *
+ * Return:
+ * * >=0 - Length of the concatenated string.
+ *
+ * * %-EINVAL - String @dst__ign is invalid.
+ * * %-EFAULT - Cannot read or write one of the strings.
+ * * %-E2BIG - String @src__ign is too large or the remaining space in
+ * @dst__ign is too small.
+ * * %-ERANGE - One of the strings is outside of kernel address space
+ */
+__bpf_kfunc int bpf_strncat(char *dst, u32 dst__sz, const char *src__ign,
+ u32 len)
+{
+ return __bpf_strncat(dst, dst__sz, src__ign, len);
+}
+
#ifdef CONFIG_KEYS
/**
* bpf_lookup_user_key - lookup a key by its serial
@@ -4958,6 +5048,8 @@ BTF_ID_FLAGS(func, bpf_strstr);
BTF_ID_FLAGS(func, bpf_strcasestr);
BTF_ID_FLAGS(func, bpf_strnstr);
BTF_ID_FLAGS(func, bpf_strncasestr);
+BTF_ID_FLAGS(func, bpf_strcat);
+BTF_ID_FLAGS(func, bpf_strncat);
#if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
#endif
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs
[not found] <cover.1784078494.git.rtoax@foxmail.com>
2026-07-15 1:27 ` [PATCH bpf-next v2 1/2] bpf: add bpf_strcat,bpf_strncat kfunc Rong Tao
@ 2026-07-15 1:27 ` Rong Tao
2026-08-07 21:58 ` kernel test robot
2026-08-08 2:59 ` kernel test robot
1 sibling, 2 replies; 6+ messages in thread
From: Rong Tao @ 2026-07-15 1:27 UTC (permalink / raw)
To: andrii, vmalik, ast
Cc: rtoax, Rong Tao, Daniel Borkmann, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Yuzuki Ishiyama,
open list:BPF [GENERAL] (Safe Dynamic Programs and Tools),
open list:KERNEL SELFTEST FRAMEWORK, open list
From: Rong Tao <rongtao@cestc.cn>
Add tests for new kfuncs bpf_strcat() and bpf_strncat().
Signed-off-by: Rong Tao <rongtao@cestc.cn>
---
.../selftests/bpf/prog_tests/string_kfuncs.c | 2 ++
.../selftests/bpf/progs/string_kfuncs_failure1.c | 13 +++++++++++++
.../selftests/bpf/progs/string_kfuncs_failure2.c | 2 ++
.../selftests/bpf/progs/string_kfuncs_success.c | 3 +++
4 files changed, 20 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..460567ef622a 100644
--- a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
+++ b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c
@@ -24,6 +24,8 @@ static const char * const test_cases[] = {
"strcasestr",
"strnstr",
"strncasestr",
+ "strcat",
+ "strncat",
};
void run_too_long_tests(void)
diff --git a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
index bddc4e8579d2..29bc7eabbb6a 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c
@@ -8,6 +8,7 @@
char *user_ptr = (char *)1;
char *invalid_kern_ptr = (char *)-1;
+char kern_buf[32] = { "hello" };
/*
* When passing userspace pointers, the error code differs based on arch:
@@ -53,6 +54,10 @@ 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_strcat_null1(void *ctx) { return bpf_strcat(NULL, 6, "hello"); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_strcat_null2(void *ctx) { return bpf_strcat(kern_buf, sizeof(kern_buf), NULL); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_strncat_null1(void *ctx) { return bpf_strncat(NULL, 6, "hello", 2); }
+SEC("syscall") __retval(USER_PTR_ERR)int test_strncat_null2(void *ctx) { return bpf_strncat(kern_buf, sizeof(kern_buf), NULL, 2); }
/* 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,10 @@ 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_strcat_user_ptr1(void *ctx) { return bpf_strcat(user_ptr, 1, "hello"); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_strcat_user_ptr2(void *ctx) { return bpf_strcat(kern_buf, sizeof(kern_buf), user_ptr); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_strncat_user_ptr1(void *ctx) { return bpf_strncat(user_ptr, 1, "hello", 2); }
+SEC("syscall") __retval(USER_PTR_ERR) int test_strncat_user_ptr2(void *ctx) { return bpf_strncat(kern_buf, sizeof(kern_buf), user_ptr, 1); }
#endif /* __TARGET_ARCH_s390 */
@@ -107,5 +116,9 @@ 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_strcat_pagefault1(void *ctx) { return bpf_strcat(invalid_kern_ptr, 1, "hello"); }
+SEC("syscall") __retval(-EFAULT) int test_strcat_pagefault2(void *ctx) { return bpf_strcat(kern_buf, sizeof(kern_buf), invalid_kern_ptr); }
+SEC("syscall") __retval(-EFAULT) int test_strncat_pagefault1(void *ctx) { return bpf_strncat(invalid_kern_ptr, 1, "hello", 2); }
+SEC("syscall") __retval(-EFAULT) int test_strncat_pagefault2(void *ctx) { return bpf_strncat(kern_buf, sizeof(kern_buf), invalid_kern_ptr, 2); }
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..38f0a5f326b5 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c
@@ -23,5 +23,7 @@ SEC("syscall") int test_strstr_too_long(void *ctx) { return bpf_strstr(long_str,
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)); }
SEC("syscall") int test_strncasestr_too_long(void *ctx) { return bpf_strncasestr(long_str, "hello", sizeof(long_str)); }
+SEC("syscall") int test_strcat_too_long(void *ctx) { return bpf_strcat(long_str, sizeof(long_str), "hello"); }
+SEC("syscall") int test_strncat_too_long(void *ctx) { return bpf_strncat(long_str, sizeof(long_str), "hello", 3); }
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..93996b0e9595 100644
--- a/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
+++ b/tools/testing/selftests/bpf/progs/string_kfuncs_success.c
@@ -6,6 +6,7 @@
#include "errno.h"
char str[] = "hello world";
+char buf[32] = "hello";
#define __test(retval) SEC("syscall") __success __retval(retval)
@@ -59,5 +60,7 @@ __test(-ENOENT) int test_strncasestr_notfound1(void *ctx) { return bpf_strncases
__test(-ENOENT) int test_strncasestr_notfound2(void *ctx) { return bpf_strncasestr(str, "hello", 4); }
__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); }
+__test(10) int test_strcat_success(void *ctx) { return bpf_strcat(buf, sizeof(buf), "world"); }
+__test(13) int test_strncat_success(void *ctx) { return bpf_strncat(buf, sizeof(buf), "world", 3); }
char _license[] SEC("license") = "GPL";
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf: add bpf_strcat,bpf_strncat kfunc
2026-07-15 1:27 ` [PATCH bpf-next v2 1/2] bpf: add bpf_strcat,bpf_strncat kfunc Rong Tao
@ 2026-07-15 8:56 ` Viktor Malik
2026-07-16 8:16 ` Rong Tao
0 siblings, 1 reply; 6+ messages in thread
From: Viktor Malik @ 2026-07-15 8:56 UTC (permalink / raw)
To: Rong Tao, andrii, ast
Cc: Rong Tao, Daniel Borkmann, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Yuzuki Ishiyama,
open list:BPF [GENERAL] (Safe Dynamic Programs and Tools),
open list, open list:KERNEL SELFTEST FRAMEWORK
On 7/15/26 03:27, Rong Tao wrote:
> From: Rong Tao <rongtao@cestc.cn>
>
> Add string concatenation kfuncs, prototype:
>
> int bpf_strcat(char *dst__ign, u32 dst__sz, const char *src__ign);
> int bpf_strncat(char *dst__ign, u32 dst__sz, const char *src__ign, u32 len);
>
> This differs from the glibc library functions strcat and strncat, which,
> for safety reasons, require the size of the target string's memory space
> as a parameter.
>
> Signed-off-by: Rong Tao <rongtao@cestc.cn>
> ---
> kernel/bpf/helpers.c | 92 ++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 92 insertions(+)
>
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index c18f1e16edee..401f94efd687 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -4195,6 +4195,96 @@ __bpf_kfunc int bpf_strncasestr(const char *s1__ign, const char *s2__ign,
> return __bpf_strnstr(s1__ign, s2__ign, len, true);
> }
>
> +static int __bpf_strncat(char *dst, u32 dsz, const char *src, u32 sz)
> +{
> + int dlen, slen, space, copied;
> + char cs = '?';
> +
> + if (!copy_from_kernel_nofault_allowed(dst, 1) ||
> + !copy_from_kernel_nofault_allowed(src, 1)) {
> + return -ERANGE;
> + }
> +
> + dlen = bpf_strnlen(dst, dsz);
> + if (dlen < 0)
> + return dlen;
> + slen = bpf_strnlen(src, sz);
> + if (slen < 0)
> + return slen;
> +
> + if (dlen >= dsz || sz == 0 || dsz == 0)
> + return -EINVAL;
> +
> + space = dsz - dlen;
> + if (space <= 1 || space < min(slen, sz) + 1)
> + return -E2BIG;
> +
> + guard(pagefault)();
> + for (copied = 0; copied < space - 1 && copied < slen; copied++) {
> + __get_kernel_nofault(&cs, src, char, err_out);
> + if (cs == '\0')
> + break;
> +
> + __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
I don't think that we need __put_kernel_nofault() here. My understanding
is that since we don't use the `__ign` suffix for the destination
string, the verifier should make sure that `dst` points to a valid
memory with sufficient capacity (thanks to the `dst__sz` arg). We could
use `strncpy_from_kernel_nofault(dst + dlen, src, space)`, which makes
me wonder how would `bpf_strcat` be different from directly using
bpf_probe_read_kernel_str().
Viktor
> +
> + src++;
> + }
> + cs = '\0';
> + __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
> +
> + __get_kernel_nofault(&cs, src, char, err_out);
> + if (cs != '\0' && sz > copied)
> + return -E2BIG;
> +
> + return dlen + copied;
> +err_out:
> + return -EFAULT;
> +}
> +
> +/**
> + * bpf_strcat - Append non-null bytes from a source string, and null-terminate
> + * the result
> + * @dst: Destination string.
> + * @dst__sz: Maximum bytes of @dst__ign, includes the trailing NUL.
> + * @src__ign: Source string.
> + *
> + * Return:
> + * * >=0 - Length of the concatenated string.
> + *
> + * * %-EINVAL - String @dst__ign is invalid.
> + * * %-EFAULT - Cannot read or write one of the strings.
> + * * %-E2BIG - String @src__ign is too large or the remaining space in
> + * @dst__ign is too small.
> + * * %-ERANGE - One of the strings is outside of kernel address space
> + */
> +__bpf_kfunc int bpf_strcat(char *dst, u32 dst__sz, const char *src__ign)
> +{
> + return __bpf_strncat(dst, dst__sz, src__ign, XATTR_SIZE_MAX);
> +}
> +
> +/**
> + * bpf_strncat - Append non-null bytes from a source string, and null-terminate
> + * the result
> + * @dst: Destination string.
> + * @dst__sz: Maximum bytes of @dst__ign, includes the trailing NUL.
> + * @src__ign: Source string.
> + * @len: the maximum number of characters to concatenate
> + *
> + * Return:
> + * * >=0 - Length of the concatenated string.
> + *
> + * * %-EINVAL - String @dst__ign is invalid.
> + * * %-EFAULT - Cannot read or write one of the strings.
> + * * %-E2BIG - String @src__ign is too large or the remaining space in
> + * @dst__ign is too small.
> + * * %-ERANGE - One of the strings is outside of kernel address space
> + */
> +__bpf_kfunc int bpf_strncat(char *dst, u32 dst__sz, const char *src__ign,
> + u32 len)
> +{
> + return __bpf_strncat(dst, dst__sz, src__ign, len);
> +}
> +
> #ifdef CONFIG_KEYS
> /**
> * bpf_lookup_user_key - lookup a key by its serial
> @@ -4958,6 +5048,8 @@ BTF_ID_FLAGS(func, bpf_strstr);
> BTF_ID_FLAGS(func, bpf_strcasestr);
> BTF_ID_FLAGS(func, bpf_strnstr);
> BTF_ID_FLAGS(func, bpf_strncasestr);
> +BTF_ID_FLAGS(func, bpf_strcat);
> +BTF_ID_FLAGS(func, bpf_strncat);
> #if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
> BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
> #endif
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 1/2] bpf: add bpf_strcat,bpf_strncat kfunc
2026-07-15 8:56 ` Viktor Malik
@ 2026-07-16 8:16 ` Rong Tao
0 siblings, 0 replies; 6+ messages in thread
From: Rong Tao @ 2026-07-16 8:16 UTC (permalink / raw)
To: Viktor Malik, andrii, ast
Cc: Rong Tao, Daniel Borkmann, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Yuzuki Ishiyama,
open list:BPF [GENERAL] (Safe Dynamic Programs and Tools),
open list, open list:KERNEL SELFTEST FRAMEWORK
On 7/15/26 16:56, Viktor Malik wrote:
> On 7/15/26 03:27, Rong Tao wrote:
>> From: Rong Tao <rongtao@cestc.cn>
>>
>> Add string concatenation kfuncs, prototype:
>>
>> int bpf_strcat(char *dst__ign, u32 dst__sz, const char *src__ign);
>> int bpf_strncat(char *dst__ign, u32 dst__sz, const char *src__ign, u32 len);
>>
>> This differs from the glibc library functions strcat and strncat, which,
>> for safety reasons, require the size of the target string's memory space
>> as a parameter.
>>
>> Signed-off-by: Rong Tao <rongtao@cestc.cn>
>> ---
>> kernel/bpf/helpers.c | 92 ++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 92 insertions(+)
>>
>> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
>> index c18f1e16edee..401f94efd687 100644
>> --- a/kernel/bpf/helpers.c
>> +++ b/kernel/bpf/helpers.c
>> @@ -4195,6 +4195,96 @@ __bpf_kfunc int bpf_strncasestr(const char *s1__ign, const char *s2__ign,
>> return __bpf_strnstr(s1__ign, s2__ign, len, true);
>> }
>>
>> +static int __bpf_strncat(char *dst, u32 dsz, const char *src, u32 sz)
>> +{
>> + int dlen, slen, space, copied;
>> + char cs = '?';
>> +
>> + if (!copy_from_kernel_nofault_allowed(dst, 1) ||
>> + !copy_from_kernel_nofault_allowed(src, 1)) {
>> + return -ERANGE;
>> + }
>> +
>> + dlen = bpf_strnlen(dst, dsz);
>> + if (dlen < 0)
>> + return dlen;
>> + slen = bpf_strnlen(src, sz);
>> + if (slen < 0)
>> + return slen;
>> +
>> + if (dlen >= dsz || sz == 0 || dsz == 0)
>> + return -EINVAL;
>> +
>> + space = dsz - dlen;
>> + if (space <= 1 || space < min(slen, sz) + 1)
>> + return -E2BIG;
>> +
>> + guard(pagefault)();
>> + for (copied = 0; copied < space - 1 && copied < slen; copied++) {
>> + __get_kernel_nofault(&cs, src, char, err_out);
>> + if (cs == '\0')
>> + break;
>> +
>> + __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
> I don't think that we need __put_kernel_nofault() here. My understanding
> is that since we don't use the `__ign` suffix for the destination
> string, the verifier should make sure that `dst` points to a valid
> memory with sufficient capacity (thanks to the `dst__sz` arg). We could
> use `strncpy_from_kernel_nofault(dst + dlen, src, space)`, which makes
> me wonder how would `bpf_strcat` be different from directly using
> bpf_probe_read_kernel_str().
Indeed, bpf_probe_read_kernel_str() and bpf_strcat() seem to have no
fundamental difference. Perhaps helpers are not as convenient to use
as kfunc? For example, in bpftrace's stdlib, to avoid including certain
header files that would make it inconvenient to use helpers, one would
have to use a complex method like asm() + BPF_FUNC_ to call the helper.
Using kfunc is much more convenient.
Rong Tao
>
> Viktor
>
>> +
>> + src++;
>> + }
>> + cs = '\0';
>> + __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
>> +
>> + __get_kernel_nofault(&cs, src, char, err_out);
>> + if (cs != '\0' && sz > copied)
>> + return -E2BIG;
>> +
>> + return dlen + copied;
>> +err_out:
>> + return -EFAULT;
>> +}
>> +
>> +/**
>> + * bpf_strcat - Append non-null bytes from a source string, and null-terminate
>> + * the result
>> + * @dst: Destination string.
>> + * @dst__sz: Maximum bytes of @dst__ign, includes the trailing NUL.
>> + * @src__ign: Source string.
>> + *
>> + * Return:
>> + * * >=0 - Length of the concatenated string.
>> + *
>> + * * %-EINVAL - String @dst__ign is invalid.
>> + * * %-EFAULT - Cannot read or write one of the strings.
>> + * * %-E2BIG - String @src__ign is too large or the remaining space in
>> + * @dst__ign is too small.
>> + * * %-ERANGE - One of the strings is outside of kernel address space
>> + */
>> +__bpf_kfunc int bpf_strcat(char *dst, u32 dst__sz, const char *src__ign)
>> +{
>> + return __bpf_strncat(dst, dst__sz, src__ign, XATTR_SIZE_MAX);
>> +}
>> +
>> +/**
>> + * bpf_strncat - Append non-null bytes from a source string, and null-terminate
>> + * the result
>> + * @dst: Destination string.
>> + * @dst__sz: Maximum bytes of @dst__ign, includes the trailing NUL.
>> + * @src__ign: Source string.
>> + * @len: the maximum number of characters to concatenate
>> + *
>> + * Return:
>> + * * >=0 - Length of the concatenated string.
>> + *
>> + * * %-EINVAL - String @dst__ign is invalid.
>> + * * %-EFAULT - Cannot read or write one of the strings.
>> + * * %-E2BIG - String @src__ign is too large or the remaining space in
>> + * @dst__ign is too small.
>> + * * %-ERANGE - One of the strings is outside of kernel address space
>> + */
>> +__bpf_kfunc int bpf_strncat(char *dst, u32 dst__sz, const char *src__ign,
>> + u32 len)
>> +{
>> + return __bpf_strncat(dst, dst__sz, src__ign, len);
>> +}
>> +
>> #ifdef CONFIG_KEYS
>> /**
>> * bpf_lookup_user_key - lookup a key by its serial
>> @@ -4958,6 +5048,8 @@ BTF_ID_FLAGS(func, bpf_strstr);
>> BTF_ID_FLAGS(func, bpf_strcasestr);
>> BTF_ID_FLAGS(func, bpf_strnstr);
>> BTF_ID_FLAGS(func, bpf_strncasestr);
>> +BTF_ID_FLAGS(func, bpf_strcat);
>> +BTF_ID_FLAGS(func, bpf_strncat);
>> #if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
>> BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
>> #endif
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs
2026-07-15 1:27 ` [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs Rong Tao
@ 2026-08-07 21:58 ` kernel test robot
2026-08-08 2:59 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-08-07 21:58 UTC (permalink / raw)
To: Rong Tao, andrii, vmalik, ast
Cc: oe-kbuild-all, rtoax, Rong Tao, Daniel Borkmann, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Yuzuki Ishiyama,
(open list:BPF \(Safe Dynamic Programs and Tools\)),
linux-kselftest, linux-kernel
Hi Rong,
kernel test robot noticed the following build errors:
[auto build test ERROR on bpf-next/master]
url: https://github.com/intel-lab-lkp/linux/commits/Rong-Tao/selftests-bpf-Test-bpf_strcat-bpf_strncat-kfuncs/20260807-065842
base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link: https://lore.kernel.org/r/tencent_DB0510330C3A8574B3C62E6154165FCB4709%40qq.com
patch subject: [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs
config: s390-randconfig-r052-20260807 (https://download.01.org/0day-ci/archive/20260808/202608080502.lI7AVjty-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project 12df34b8469b8095359de8c249cb1b2753fadeea)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260808/202608080502.lI7AVjty-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608080502.lI7AVjty-lkp@intel.com/
All errors (new ones prefixed by >>):
>> kernel/bpf/helpers.c:4228:3: error: invalid lvalue in asm output
4228 | __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/uaccess.h:612:2: note: expanded from macro '__put_kernel_nofault'
612 | arch_put_kernel_nofault(dst, src, type, local_label); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/s390/include/asm/uaccess.h:471:33: note: expanded from macro 'arch_put_kernel_nofault'
471 | #define arch_put_kernel_nofault __mvc_kernel_nofault
| ^
arch/s390/include/asm/uaccess.h:424:19: note: expanded from macro '__mvc_kernel_nofault'
424 | : [_dst] "=Q" (*(type *)dst) \
| ^~~~~~~~~~~~
kernel/bpf/helpers.c:4233:2: error: invalid lvalue in asm output
4233 | __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/uaccess.h:612:2: note: expanded from macro '__put_kernel_nofault'
612 | arch_put_kernel_nofault(dst, src, type, local_label); \
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/s390/include/asm/uaccess.h:471:33: note: expanded from macro 'arch_put_kernel_nofault'
471 | #define arch_put_kernel_nofault __mvc_kernel_nofault
| ^
arch/s390/include/asm/uaccess.h:424:19: note: expanded from macro '__mvc_kernel_nofault'
424 | : [_dst] "=Q" (*(type *)dst) \
| ^~~~~~~~~~~~
2 errors generated.
vim +4228 kernel/bpf/helpers.c
4197
4198 static int __bpf_strncat(char *dst, u32 dsz, const char *src, u32 sz)
4199 {
4200 int dlen, slen, space, copied;
4201 char cs = '?';
4202
4203 if (!copy_from_kernel_nofault_allowed(dst, 1) ||
4204 !copy_from_kernel_nofault_allowed(src, 1)) {
4205 return -ERANGE;
4206 }
4207
4208 dlen = bpf_strnlen(dst, dsz);
4209 if (dlen < 0)
4210 return dlen;
4211 slen = bpf_strnlen(src, sz);
4212 if (slen < 0)
4213 return slen;
4214
4215 if (dlen >= dsz || sz == 0 || dsz == 0)
4216 return -EINVAL;
4217
4218 space = dsz - dlen;
4219 if (space <= 1 || space < min(slen, sz) + 1)
4220 return -E2BIG;
4221
4222 guard(pagefault)();
4223 for (copied = 0; copied < space - 1 && copied < slen; copied++) {
4224 __get_kernel_nofault(&cs, src, char, err_out);
4225 if (cs == '\0')
4226 break;
4227
> 4228 __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
4229
4230 src++;
4231 }
4232 cs = '\0';
4233 __put_kernel_nofault(dst + dlen + copied, &cs, char, err_out);
4234
4235 __get_kernel_nofault(&cs, src, char, err_out);
4236 if (cs != '\0' && sz > copied)
4237 return -E2BIG;
4238
4239 return dlen + copied;
4240 err_out:
4241 return -EFAULT;
4242 }
4243
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs
2026-07-15 1:27 ` [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs Rong Tao
2026-08-07 21:58 ` kernel test robot
@ 2026-08-08 2:59 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-08-08 2:59 UTC (permalink / raw)
To: Rong Tao, andrii, vmalik, ast
Cc: oe-kbuild-all, rtoax, Rong Tao, Daniel Borkmann, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu,
Yonghong Song, Jiri Olsa, Emil Tsalapatis, Shuah Khan,
Yuzuki Ishiyama,
(open list:BPF \(Safe Dynamic Programs and Tools\)),
linux-kselftest, linux-kernel
Hi Rong,
kernel test robot noticed the following build errors:
[auto build test ERROR on bpf-next/master]
url: https://github.com/intel-lab-lkp/linux/commits/Rong-Tao/selftests-bpf-Test-bpf_strcat-bpf_strncat-kfuncs/20260807-065842
base: https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link: https://lore.kernel.org/r/tencent_DB0510330C3A8574B3C62E6154165FCB4709%40qq.com
patch subject: [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs
config: riscv-randconfig-r131-20260807 (https://download.01.org/0day-ci/archive/20260808/202608081054.Y56a5ufe-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 8.5.0
sparse: v0.6.5-rc1
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260808/202608081054.Y56a5ufe-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608081054.Y56a5ufe-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from include/uapi/linux/filter.h:9,
from include/linux/bpf.h:8,
from kernel/bpf/helpers.c:4:
kernel/bpf/helpers.c: In function '__bpf_strncat':
>> include/linux/compiler_types.h:702:38: error: call to '__compiletime_assert_851' declared with attribute error: min(slen, sz) signedness error
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
^
include/linux/compiler.h:68:3: note: in definition of macro '__trace_if_value'
(cond) ? \
^~~~
include/linux/compiler.h:55:28: note: in expansion of macro '__trace_if_var'
#define if(cond, ...) if ( __trace_if_var( !!(cond , ## __VA_ARGS__) ) )
^~~~~~~~~~~~~~
kernel/bpf/helpers.c:4219:2: note: in expansion of macro 'if'
if (space <= 1 || space < min(slen, sz) + 1)
^~
include/linux/compiler_types.h:690:2: note: in expansion of macro '__compiletime_assert'
__compiletime_assert(condition, msg, prefix, suffix)
^~~~~~~~~~~~~~~~~~~~
include/linux/compiler_types.h:702:2: note: in expansion of macro '_compiletime_assert'
_compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
^~~~~~~~~~~~~~~~~~~
include/linux/build_bug.h:40:37: note: in expansion of macro 'compiletime_assert'
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
^~~~~~~~~~~~~~~~~~
include/linux/minmax.h:93:2: note: in expansion of macro 'BUILD_BUG_ON_MSG'
BUILD_BUG_ON_MSG(!__types_ok(ux, uy), \
^~~~~~~~~~~~~~~~
include/linux/minmax.h:98:2: note: in expansion of macro '__careful_cmp_once'
__careful_cmp_once(op, x, y, __UNIQUE_ID(x_), __UNIQUE_ID(y_))
^~~~~~~~~~~~~~~~~~
include/linux/minmax.h:105:19: note: in expansion of macro '__careful_cmp'
#define min(x, y) __careful_cmp(min, x, y)
^~~~~~~~~~~~~
kernel/bpf/helpers.c:4219:28: note: in expansion of macro 'min'
if (space <= 1 || space < min(slen, sz) + 1)
^~~
vim +/__compiletime_assert_851 +702 include/linux/compiler_types.h
eb5c2d4b45e3d2 Will Deacon 2020-07-21 688
eb5c2d4b45e3d2 Will Deacon 2020-07-21 689 #define _compiletime_assert(condition, msg, prefix, suffix) \
eb5c2d4b45e3d2 Will Deacon 2020-07-21 690 __compiletime_assert(condition, msg, prefix, suffix)
eb5c2d4b45e3d2 Will Deacon 2020-07-21 691
eb5c2d4b45e3d2 Will Deacon 2020-07-21 692 /**
eb5c2d4b45e3d2 Will Deacon 2020-07-21 693 * compiletime_assert - break build and emit msg if condition is false
eb5c2d4b45e3d2 Will Deacon 2020-07-21 694 * @condition: a compile-time constant condition to check
eb5c2d4b45e3d2 Will Deacon 2020-07-21 695 * @msg: a message to emit if condition is false
eb5c2d4b45e3d2 Will Deacon 2020-07-21 696 *
eb5c2d4b45e3d2 Will Deacon 2020-07-21 697 * In tradition of POSIX assert, this macro will break the build if the
eb5c2d4b45e3d2 Will Deacon 2020-07-21 698 * supplied condition is *false*, emitting the supplied error message if the
eb5c2d4b45e3d2 Will Deacon 2020-07-21 699 * compiler has support to do so.
eb5c2d4b45e3d2 Will Deacon 2020-07-21 700 */
eb5c2d4b45e3d2 Will Deacon 2020-07-21 701 #define compiletime_assert(condition, msg) \
eb5c2d4b45e3d2 Will Deacon 2020-07-21 @702 _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
eb5c2d4b45e3d2 Will Deacon 2020-07-21 703
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-08-08 3:00 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <cover.1784078494.git.rtoax@foxmail.com>
2026-07-15 1:27 ` [PATCH bpf-next v2 1/2] bpf: add bpf_strcat,bpf_strncat kfunc Rong Tao
2026-07-15 8:56 ` Viktor Malik
2026-07-16 8:16 ` Rong Tao
2026-07-15 1:27 ` [PATCH bpf-next v2 2/2] selftests/bpf: Test bpf_strcat,bpf_strncat kfuncs Rong Tao
2026-08-07 21:58 ` kernel test robot
2026-08-08 2:59 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox