On 7/28/26 06:56, Andrii Nakryiko wrote:
On Tue, Jul 21, 2026 at 9:42 PM Rong Tao <[email protected]> wrote:
From: Rong Tao <[email protected]>

Add string concatenation kfuncs, prototype:

   int bpf_strcat(char *dst, u32 dst__sz, const char *src);
   int bpf_strncat(char *dst, u32 dst__sz, const char *src, 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.

The src parameter cannot directly pass a string constant, such as "XYZ",
otherwise it will throw an error "write into map forbidden".

Signed-off-by: Rong Tao <[email protected]>
---
  kernel/bpf/helpers.c | 75 ++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 75 insertions(+)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index c18f1e16edee..85afff60365c 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4195,6 +4195,79 @@ __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 = 0, slen, space, copied;
+
+       if (sz == 0 || dsz == 0)
+               return -EINVAL;
+
+       while (dlen < dsz && dst[dlen] != '\0')
+               dlen++;
+
+       if (dlen >= dsz)
+               return -E2BIG;
+
+       space = dsz - dlen;
+
+       slen = bpf_strnlen(src, sz);
+       if (space <= slen)
+               return -E2BIG;
+
+       copied = strncpy_from_kernel_nofault(dst + dlen, src, sz);
I'm not sure strncpy_from_kernel_nofault() is designed to work
correctly if dst can be modified concurrently. Which in BPF case, even
though we validate that memory is correct, it still can be modified
concurrently in such a way as to cause strncpy_from_kernel_nofault's
inner dst[-1] based loop logic to go beyond the boundaries of target
buffer.

i.e., I don't believe current implementation is concurrency safe

Thank you very much for your reply. There is indeed a thread safety issue. Let's look at a solution. I will submit v6. Thank you ;)

Rong Tao


pw-bot: cr


+       if (copied < 0)
+               return copied;
+       else if (copied == 0 || copied == 1)
+               return dlen;
+
+       /* The copied character count includes '\0'. */
+       return dlen + copied - 1;
+}
+
+/**
+ * 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: 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)
+{
+       return __bpf_strncat(dst, dst__sz, src, dst__sz);
+}
+
+/**
+ * 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: Source string.
+ * @len: the maximum number of characters to concatenate, the null terminator
+ *       \0 of the target string @dst will also be included.
+ *
+ * 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, u32 len)
+{
+       return __bpf_strncat(dst, dst__sz, src, len);
+}
+
  #ifdef CONFIG_KEYS
  /**
   * bpf_lookup_user_key - lookup a key by its serial
@@ -4958,6 +5031,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



Reply via email to