From: Rong Tao <[email protected]>

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 <[email protected]>
---
 kernel/bpf/helpers.c | 91 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index c18f1e16edee..e99b64313682 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4195,6 +4195,95 @@ __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, XATTR_SIZE_MAX);
+       if (dlen < 0)
+               return dlen;
+       slen = bpf_strnlen(src, XATTR_SIZE_MAX);
+       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;
+
+       for (copied = 0; copied < space - 1 && copied < sz; 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 copied;
+err_out:
+       return -EFAULT;
+}
+
+/**
+ * bpf_strcat - Append non-null bytes from a source string, and null-terminate
+ *              the result
+ * @dst__ign: 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__ign, u32 dst__sz, const char *src__ign)
+{
+       return __bpf_strncat(dst__ign, dst__sz, src__ign, XATTR_SIZE_MAX);
+}
+
+/**
+ * bpf_strncat - Append non-null bytes from a source string, and null-terminate
+ *               the result
+ * @dst__ign: 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__ign, u32 dst__sz, const char *src__ign,
+                           u32 len)
+{
+       return __bpf_strncat(dst__ign, dst__sz, src__ign, len);
+}
+
 #ifdef CONFIG_KEYS
 /**
  * bpf_lookup_user_key - lookup a key by its serial
@@ -4958,6 +5047,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