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 | 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

