Commit d1bc05b7 authored by Benjamin Gray's avatar Benjamin Gray Committed by Michael Ellerman
Browse files

selftests/powerpc: Parse long/unsigned long value safely



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: default avatarBenjamin Gray <bgray@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20230203003947.38033-4-bgray@linux.ibm.com
parent 121d340b
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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);
+2 −4
Original line number Diff line number Diff line
@@ -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;
	}
+123 −3
Original line number Diff line number Diff line
@@ -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)