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

Signed-off-by: Benjamin Gray <bg...@linux.ibm.com>

---

v4:     * Implement as C functions, remove macros
        * Declare parsers for intmax_t, uintmax_t
        * Stop validating the remaining buffer after a null terminator
        * Set errno to match the return code
---
 .../testing/selftests/powerpc/include/utils.h |   7 +
 tools/testing/selftests/powerpc/pmu/lib.c     |   6 +-
 tools/testing/selftests/powerpc/utils.c       | 126 +++++++++++++++++-
 3 files changed, 132 insertions(+), 7 deletions(-)

diff --git a/tools/testing/selftests/powerpc/include/utils.h 
b/tools/testing/selftests/powerpc/include/utils.h
index de5e3790f397..7c1fa385824c 100644
--- a/tools/testing/selftests/powerpc/include/utils.h
+++ b/tools/testing/selftests/powerpc/include/utils.h
@@ -33,6 +33,13 @@ void *get_auxv_entry(int type);
 
 int pick_online_cpu(void);
 
+int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base);
+int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int 
base);
+int parse_int(const char *buffer, size_t count, int *result, int base);
+int parse_uint(const char *buffer, size_t count, unsigned int *result, int 
base);
+int parse_long(const char *buffer, size_t count, long *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 960915304a65..1cfc13a25aee 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.c
+++ b/tools/testing/selftests/powerpc/pmu/lib.c
@@ -192,7 +192,6 @@ bool require_paranoia_below(int level)
 {
        int err;
        long current;
-       char *end;
        char buf[16] = {0};
 
        err = read_file(PARANOID_PATH, buf, sizeof(buf) - 1, NULL);
@@ -201,9 +200,8 @@ bool require_paranoia_below(int level)
                return false;
        }
 
-       current = strtol(buf, &end, 10);
-
-       if (end == buf) {
+       err = parse_long(buf, sizeof(buf), &current, 10);
+       if (err) {
                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 495299a79f50..ddfd871881bf 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>
@@ -123,6 +125,126 @@ int write_debugfs_file(const char *subpath, const char 
*buf, size_t count)
        return write_file(path, buf, count);
 }
 
+static int validate_int_parse(const char *buffer, size_t count, char *end)
+{
+       int err = 0;
+
+       /* Require at least one digit */
+       if (end == buffer) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       /* Require all remaining characters be whitespace-ish */
+       for (; end < buffer + count; end++) {
+               if (*end == '\0')
+                       break;
+
+               if (*end != ' ' && *end != '\n') {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+out:
+       errno = -err;
+       return err;
+}
+
+static int parse_bounded_int(const char *buffer, size_t count, intmax_t 
*result,
+                            int base, intmax_t min, intmax_t max)
+{
+       int err;
+       char *end;
+
+       errno = 0;
+       *result = strtoimax(buffer, &end, base);
+
+       if (errno)
+               return -errno;
+
+       err = validate_int_parse(buffer, count, end);
+       if (err)
+               goto out;
+
+       if (*result < min || *result > max)
+               err = -EOVERFLOW;
+
+out:
+       errno = -err;
+       return err;
+}
+
+static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t 
*result,
+                             int base, uintmax_t max)
+{
+       int err = 0;
+       char *end;
+
+       errno = 0;
+       *result = strtoumax(buffer, &end, base);
+
+       if (errno)
+               return -errno;
+
+       err = validate_int_parse(buffer, count, end);
+       if (err)
+               goto out;
+
+       if (*result > max)
+               err = -EOVERFLOW;
+
+out:
+       errno = -err;
+       return err;
+}
+
+int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base)
+{
+       return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, 
INTMAX_MAX);
+}
+
+int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int 
base)
+{
+       return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX);
+}
+
+int parse_int(const char *buffer, size_t count, int *result, int base)
+{
+       intmax_t parsed;
+       int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, 
INT_MAX);
+
+       *result = parsed;
+       return err;
+}
+
+int parse_uint(const char *buffer, size_t count, unsigned int *result, int 
base)
+{
+       uintmax_t parsed;
+       int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX);
+
+       *result = parsed;
+       return err;
+}
+
+int parse_long(const char *buffer, size_t count, long *result, int base)
+{
+       intmax_t parsed;
+       int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, 
LONG_MAX);
+
+       *result = parsed;
+       return err;
+}
+
+int parse_ulong(const char *buffer, size_t count, unsigned long *result, int 
base)
+{
+       uintmax_t parsed;
+       int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX);
+
+       *result = parsed;
+       return err;
+}
+
 void *find_auxv_entry(int type, char *auxv)
 {
        ElfW(auxv_t) *p;
@@ -224,9 +346,7 @@ int read_debugfs_int(const char *debugfs_file, int *result)
        if (err)
                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.39.1

Reply via email to