From 5728fb3009f5f797d1ff68e5ad6de341850c35d4 Mon Sep 17 00:00:00 2001 From: James Simmons Date: Tue, 8 May 2018 09:30:15 -0400 Subject: [PATCH] LU-9325 libcfs: handle complex strings in cfs_str2num_check Originally the function cfs_str2num_check used simple_strtoul but has been updated to kstrtoul. The string passed into cfs_str2num_check can be a very complex, for example we could have 10.37.202.[59-61]. When simple_strtoul was used the first number until we hit a non-digit character could be extracted but testing showed that kstrtoul will not return any value if it detects any non-digit character. Because of this change in behavior a different approach is needed to handle these types of complex strings. The use of sscanf was investigated to see if it could be used to extract numbers from the passed in string but unlike its glibc counterpart the kernel version also just reported a error with no results if a non-digit value in the string was encountered. Another possible approach would be to use __parse_int directly but that class of functions is not exported by the kernel. So the approach in this patch is to scan the string passed in for the first non-digit character and replace that character with a '\0' so kstrtoul can be used. Once completed the original character is restored. We also restore a original behavior that was removed to return 0 when we encounter any non digit character before the nob count. Linux-commit : 3ad6152d766039cb8ffd8633d971fb79402e5464 Change-Id: If73d605499e2f05224a14417b0029036db38c8ba Signed-off-by: Oleg Drokin Signed-off-by: Greg Kroah-Hartman Signed-off-by: James Simmons Reviewed-on: https://review.whamcloud.com/32217 Tested-by: Jenkins Reviewed-by: John L. Hammond Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Dmitry Eremin Reviewed-by: Oleg Drokin --- libcfs/libcfs/libcfs_string.c | 46 +++++++++++++++++++++++++++++++++++-------- libcfs/libcfs/tracefile.c | 17 +++++++++------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/libcfs/libcfs/libcfs_string.c b/libcfs/libcfs/libcfs_string.c index cd93921..b460df3 100644 --- a/libcfs/libcfs/libcfs_string.c +++ b/libcfs/libcfs/libcfs_string.c @@ -254,17 +254,47 @@ int cfs_str2num_check(char *str, int nob, unsigned *num, unsigned min, unsigned max) { - char *endp; - - *num = simple_strtoul(str, &endp, 0); - if (endp == str) - return 0; + bool all_numbers = true; + char *endp, cache; + int len; + int rc; + + endp = strim(str); + /** + * kstrouint can only handle strings composed + * of only numbers. We need to scan the string + * passed in for the first non-digit character + * and end the string at that location. If we + * don't find any non-digit character we still + * need to place a '\0' at position len since + * we are not interested in the rest of the + * string which is longer than len in size. + * After we are done the character at the + * position we placed '\0' must be restored. + */ + len = min((int)strlen(endp), nob); + for (; endp < str + len; endp++) { + if (!isxdigit(*endp) && *endp != '-' && + *endp != '+') { + all_numbers = false; + break; + } + } - for (; endp < str + nob; endp++) { - if (!isspace(*endp)) - return 0; + /* Eat trailing space */ + if (!all_numbers && isspace(*endp)) { + all_numbers = true; + endp--; } + cache = *endp; + *endp = '\0'; + + rc = kstrtouint(str, 0, num); + *endp = cache; + if (rc || !all_numbers) + return 0; + return (*num >= min && *num <= max); } EXPORT_SYMBOL(cfs_str2num_check); diff --git a/libcfs/libcfs/tracefile.c b/libcfs/libcfs/tracefile.c index 6e6a28f..54e0fab 100644 --- a/libcfs/libcfs/tracefile.c +++ b/libcfs/libcfs/tracefile.c @@ -833,13 +833,16 @@ int cfs_trace_daemon_command(char *str) cfs_tracefile_write_lock(); memset(cfs_tracefile, 0, sizeof(cfs_tracefile)); - } else if (strncmp(str, "size=", 5) == 0) { - cfs_tracefile_size = simple_strtoul(str + 5, NULL, 0); - if (cfs_tracefile_size < 10 || cfs_tracefile_size > 20480) - cfs_tracefile_size = CFS_TRACEFILE_SIZE; - else - cfs_tracefile_size <<= 20; - + } else if (strncmp(str, "size=", 5) == 0) { + unsigned long tmp; + + rc = kstrtoul(str + 5, 10, &tmp); + if (!rc) { + if (tmp < 10 || tmp > 20480) + cfs_tracefile_size = CFS_TRACEFILE_SIZE; + else + cfs_tracefile_size = tmp << 20; + } } else if (strlen(str) >= sizeof(cfs_tracefile)) { rc = -ENAMETOOLONG; } else if (str[0] != '/') { -- 1.8.3.1