From: Longjun Tang 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 --- 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