Often a file is expected to hold an integral value. Existing functions
will use a C stdlib function like atoi or strtol to parse the file.
These operations are error prone, with complicated error conditions
(atoi returns 0 if not a number, and is undefined behaviour if not in
range. strtol returns 0 if not a number, and LONG_MIN/MAX if not in
range + sets errno to ERANGE).

Add a dedicated parse function that accounts for these error conditions
so tests can safely parse numbers without undetected bad data. It's a
bit ugly to generate the functions through a macro, but it beats copying
the error check logic multiple times over.

define_parse_number
---
 .../testing/selftests/powerpc/include/utils.h |  5 ++
 tools/testing/selftests/powerpc/pmu/lib.c     |  9 ++--
 tools/testing/selftests/powerpc/utils.c       | 53 +++++++++++++++++--
 3 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/tools/testing/selftests/powerpc/include/utils.h 
b/tools/testing/selftests/powerpc/include/utils.h
index de5e3790f397..b82e143a07c6 100644
--- a/tools/testing/selftests/powerpc/include/utils.h
+++ b/tools/testing/selftests/powerpc/include/utils.h
@@ -33,6 +33,11 @@ void *get_auxv_entry(int type);
 
 int pick_online_cpu(void);
 
+int parse_int(const char *buffer, size_t count, int *result, int base);
+int parse_long(const char *buffer, size_t count, long *result, int base);
+int parse_uint(const char *buffer, size_t count, unsigned int *result, int 
base);
+int parse_ulong(const char *buffer, size_t count, unsigned long *result, int 
base);
+
 int read_file(const char *path, char *buf, size_t count, size_t *len);
 int write_file(const char *path, const char *buf, size_t count);
 int read_debugfs_file(const char *debugfs_file, char *buf, size_t count);
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c 
b/tools/testing/selftests/powerpc/pmu/lib.c
index e8960e7a1271..771658278f55 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.c
+++ b/tools/testing/selftests/powerpc/pmu/lib.c
@@ -192,16 +192,15 @@ bool require_paranoia_below(int level)
 {
        int err;
        long current;
-       char *end, buf[16];
+       char buf[16] = {0};
+       char *end;
 
-       if ((err = read_file(PARANOID_PATH, buf, sizeof(buf), NULL))) {
+       if ((err = read_file(PARANOID_PATH, buf, sizeof(buf) - 1, NULL))) {
                printf("Couldn't read " PARANOID_PATH "?\n");
                return false;
        }
 
-       current = strtol(buf, &end, 10);
-
-       if (end == buf) {
+       if ((err = parse_long(buf, sizeof(buf), &current, 10))) {
                printf("Couldn't parse " PARANOID_PATH "?\n");
                return false;
        }
diff --git a/tools/testing/selftests/powerpc/utils.c 
b/tools/testing/selftests/powerpc/utils.c
index 22a255cd43d1..7c7d8aaa69fb 100644
--- a/tools/testing/selftests/powerpc/utils.c
+++ b/tools/testing/selftests/powerpc/utils.c
@@ -8,6 +8,8 @@
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
 #include <link.h>
 #include <sched.h>
 #include <stdio.h>
@@ -113,6 +115,53 @@ int write_debugfs_file(const char *subpath, const char 
*buf, size_t count)
        return write_file(path, buf, count);
 }
 
+#define TYPE_MIN(x)                            \
+       _Generic((x),                           \
+               int:            INT_MIN,        \
+               long:           LONG_MIN,       \
+               unsigned int:   0,              \
+               unsigned long:  0)
+
+#define TYPE_MAX(x)                            \
+       _Generic((x),                           \
+               int:            INT_MAX,        \
+               long:           LONG_MAX,       \
+               unsigned int:   INT_MAX,        \
+               unsigned long:  LONG_MAX)
+
+#define define_parse_number(fn, type, super_type)                              
\
+       int fn(const char *buffer, size_t count, type *result, int base)        
\
+       {                                                                       
\
+               char *end;                                                      
\
+               super_type parsed;                                              
\
+                                                                               
\
+               errno = 0;                                                      
\
+               parsed = _Generic(parsed,                                       
\
+                                 intmax_t:     strtoimax,                      
\
+                                 uintmax_t:    strtoumax)(buffer, &end, base); 
\
+                                                                               
\
+               if (errno == ERANGE ||                                          
\
+                   parsed < TYPE_MIN(*result) || parsed > TYPE_MAX(*result))   
\
+                       return ERANGE;                                          
\
+                                                                               
\
+               /* Require at least one digit */                                
\
+               if (end == buffer)                                              
\
+                       return EINVAL;                                          
\
+                                                                               
\
+               /* Require all remaining characters be whitespace-ish */        
\
+               for (; end < buffer + count; end++)                             
\
+                       if (!(*end == ' ' || *end == '\n' || *end == '\0'))     
\
+                               return EINVAL;                                  
\
+                                                                               
\
+               *result = parsed;                                               
\
+               return 0;                                                       
\
+       }
+
+define_parse_number(parse_int, int, intmax_t);
+define_parse_number(parse_long, long, intmax_t);
+define_parse_number(parse_uint, unsigned int, uintmax_t);
+define_parse_number(parse_ulong, unsigned long, uintmax_t);
+
 void *find_auxv_entry(int type, char *auxv)
 {
        ElfW(auxv_t) *p;
@@ -213,9 +262,7 @@ int read_debugfs_int(const char *debugfs_file, int *result)
        if ((err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1)))
                return err;
 
-       *result = atoi(value);
-
-       return 0;
+       return parse_int(value, sizeof(value), result, 10);
 }
 
 int write_debugfs_int(const char *debugfs_file, int result)
-- 
2.38.1

Reply via email to