From: Benjamin Gray <bgray@linux.ibm.com>
To: linuxppc-dev@lists.ozlabs.org
Cc: Benjamin Gray <bgray@linux.ibm.com>
Subject: [PATCH 3/5] selftests/powerpc: Parse long/unsigned long value safely
Date: Fri, 3 Feb 2023 11:39:45 +1100 [thread overview]
Message-ID: <20230203003947.38033-4-bgray@linux.ibm.com> (raw)
In-Reply-To: <20230203003947.38033-1-bgray@linux.ibm.com>
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 <bgray@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), ¤t, 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
next prev parent reply other threads:[~2023-02-03 0:41 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-02-03 0:39 [PATCH 0/5] Expand selftest utils Benjamin Gray
2023-02-03 0:39 ` [PATCH 1/5] selftests/powerpc: Add generic read/write file util Benjamin Gray
2023-02-03 0:39 ` [PATCH 2/5] selftests/powerpc: Add read/write debugfs file, int Benjamin Gray
2023-02-03 0:39 ` Benjamin Gray [this message]
2023-02-03 0:39 ` [PATCH 4/5] selftests/powerpc: Add {read,write}_{long,ulong} Benjamin Gray
2023-02-03 0:39 ` [PATCH 5/5] selftests/powerpc: Add automatically allocating read_file Benjamin Gray
2023-02-15 12:40 ` [PATCH 0/5] Expand selftest utils Michael Ellerman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230203003947.38033-4-bgray@linux.ibm.com \
--to=bgray@linux.ibm.com \
--cc=linuxppc-dev@lists.ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.