lib/string: Move helper functions out of string.c

The core functions of string.c are those that may be implemented by
per-architecture functions, or overloaded by FORTIFY_SOURCE. As a
result, it needs to be built with __NO_FORTIFY. Without this, macros
will collide with function declarations. This was accidentally working
due to -ffreestanding (on some architectures). Make this deterministic
by explicitly setting __NO_FORTIFY and move all the helper functions
into string_helpers.c so that they gain the fortification coverage they
had been missing.

Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Andy Lavr <andy.lavr@gmail.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Kees Cook 2021-06-18 10:57:38 -07:00
parent c80d92fbb6
commit cfecea6ead
7 changed files with 208 additions and 204 deletions

View file

@ -696,3 +696,196 @@ void kfree_strarray(char **array, size_t n)
kfree(array);
}
EXPORT_SYMBOL_GPL(kfree_strarray);
/**
* strscpy_pad() - Copy a C-string into a sized buffer
* @dest: Where to copy the string to
* @src: Where to copy the string from
* @count: Size of destination buffer
*
* Copy the string, or as much of it as fits, into the dest buffer. The
* behavior is undefined if the string buffers overlap. The destination
* buffer is always %NUL terminated, unless it's zero-sized.
*
* If the source string is shorter than the destination buffer, zeros
* the tail of the destination buffer.
*
* For full explanation of why you may want to consider using the
* 'strscpy' functions please see the function docstring for strscpy().
*
* Returns:
* * The number of characters copied (not including the trailing %NUL)
* * -E2BIG if count is 0 or @src was truncated.
*/
ssize_t strscpy_pad(char *dest, const char *src, size_t count)
{
ssize_t written;
written = strscpy(dest, src, count);
if (written < 0 || written == count - 1)
return written;
memset(dest + written + 1, 0, count - written - 1);
return written;
}
EXPORT_SYMBOL(strscpy_pad);
/**
* skip_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
*
* Returns a pointer to the first non-whitespace character in @str.
*/
char *skip_spaces(const char *str)
{
while (isspace(*str))
++str;
return (char *)str;
}
EXPORT_SYMBOL(skip_spaces);
/**
* strim - Removes leading and trailing whitespace from @s.
* @s: The string to be stripped.
*
* Note that the first trailing whitespace is replaced with a %NUL-terminator
* in the given string @s. Returns a pointer to the first non-whitespace
* character in @s.
*/
char *strim(char *s)
{
size_t size;
char *end;
size = strlen(s);
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
return skip_spaces(s);
}
EXPORT_SYMBOL(strim);
/**
* sysfs_streq - return true if strings are equal, modulo trailing newline
* @s1: one string
* @s2: another string
*
* This routine returns true iff two strings are equal, treating both
* NUL and newline-then-NUL as equivalent string terminations. It's
* geared for use with sysfs input strings, which generally terminate
* with newlines but are compared against values without newlines.
*/
bool sysfs_streq(const char *s1, const char *s2)
{
while (*s1 && *s1 == *s2) {
s1++;
s2++;
}
if (*s1 == *s2)
return true;
if (!*s1 && *s2 == '\n' && !s2[1])
return true;
if (*s1 == '\n' && !s1[1] && !*s2)
return true;
return false;
}
EXPORT_SYMBOL(sysfs_streq);
/**
* match_string - matches given string in an array
* @array: array of strings
* @n: number of strings in the array or -1 for NULL terminated arrays
* @string: string to match with
*
* This routine will look for a string in an array of strings up to the
* n-th element in the array or until the first NULL element.
*
* Historically the value of -1 for @n, was used to search in arrays that
* are NULL terminated. However, the function does not make a distinction
* when finishing the search: either @n elements have been compared OR
* the first NULL element was found.
*
* Return:
* index of a @string in the @array if matches, or %-EINVAL otherwise.
*/
int match_string(const char * const *array, size_t n, const char *string)
{
int index;
const char *item;
for (index = 0; index < n; index++) {
item = array[index];
if (!item)
break;
if (!strcmp(item, string))
return index;
}
return -EINVAL;
}
EXPORT_SYMBOL(match_string);
/**
* __sysfs_match_string - matches given string in an array
* @array: array of strings
* @n: number of strings in the array or -1 for NULL terminated arrays
* @str: string to match with
*
* Returns index of @str in the @array or -EINVAL, just like match_string().
* Uses sysfs_streq instead of strcmp for matching.
*
* This routine will look for a string in an array of strings up to the
* n-th element in the array or until the first NULL element.
*
* Historically the value of -1 for @n, was used to search in arrays that
* are NULL terminated. However, the function does not make a distinction
* when finishing the search: either @n elements have been compared OR
* the first NULL element was found.
*/
int __sysfs_match_string(const char * const *array, size_t n, const char *str)
{
const char *item;
int index;
for (index = 0; index < n; index++) {
item = array[index];
if (!item)
break;
if (sysfs_streq(item, str))
return index;
}
return -EINVAL;
}
EXPORT_SYMBOL(__sysfs_match_string);
/**
* strreplace - Replace all occurrences of character in string.
* @s: The string to operate on.
* @old: The character being replaced.
* @new: The character @old is replaced with.
*
* Returns pointer to the nul byte at the end of @s.
*/
char *strreplace(char *s, char old, char new)
{
for (; *s; ++s)
if (*s == old)
*s = new;
return s;
}
EXPORT_SYMBOL(strreplace);
void fortify_panic(const char *name)
{
pr_emerg("detected buffer overflow in %s\n", name);
BUG();
}
EXPORT_SYMBOL(fortify_panic);