Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Benjamin Gray <bgray@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20230203003947.38033-4-bgray@linux.ibm.com

authored by

Benjamin Gray and committed by
Michael Ellerman
d1bc05b7 121d340b

+132 -7
+7
tools/testing/selftests/powerpc/include/utils.h
··· 33 33 34 34 int pick_online_cpu(void); 35 35 36 + int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base); 37 + int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base); 38 + int parse_int(const char *buffer, size_t count, int *result, int base); 39 + int parse_uint(const char *buffer, size_t count, unsigned int *result, int base); 40 + int parse_long(const char *buffer, size_t count, long *result, int base); 41 + int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base); 42 + 36 43 int read_file(const char *path, char *buf, size_t count, size_t *len); 37 44 int write_file(const char *path, const char *buf, size_t count); 38 45 int read_debugfs_file(const char *debugfs_file, char *buf, size_t count);
+2 -4
tools/testing/selftests/powerpc/pmu/lib.c
··· 192 192 { 193 193 int err; 194 194 long current; 195 - char *end; 196 195 char buf[16] = {0}; 197 196 198 197 err = read_file(PARANOID_PATH, buf, sizeof(buf) - 1, NULL); ··· 200 201 return false; 201 202 } 202 203 203 - current = strtol(buf, &end, 10); 204 - 205 - if (end == buf) { 204 + err = parse_long(buf, sizeof(buf), &current, 10); 205 + if (err) { 206 206 printf("Couldn't parse " PARANOID_PATH "?\n"); 207 207 return false; 208 208 }
+123 -3
tools/testing/selftests/powerpc/utils.c
··· 8 8 #include <elf.h> 9 9 #include <errno.h> 10 10 #include <fcntl.h> 11 + #include <inttypes.h> 12 + #include <limits.h> 11 13 #include <link.h> 12 14 #include <sched.h> 13 15 #include <stdio.h> ··· 125 123 return write_file(path, buf, count); 126 124 } 127 125 126 + static int validate_int_parse(const char *buffer, size_t count, char *end) 127 + { 128 + int err = 0; 129 + 130 + /* Require at least one digit */ 131 + if (end == buffer) { 132 + err = -EINVAL; 133 + goto out; 134 + } 135 + 136 + /* Require all remaining characters be whitespace-ish */ 137 + for (; end < buffer + count; end++) { 138 + if (*end == '\0') 139 + break; 140 + 141 + if (*end != ' ' && *end != '\n') { 142 + err = -EINVAL; 143 + goto out; 144 + } 145 + } 146 + 147 + out: 148 + errno = -err; 149 + return err; 150 + } 151 + 152 + static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result, 153 + int base, intmax_t min, intmax_t max) 154 + { 155 + int err; 156 + char *end; 157 + 158 + errno = 0; 159 + *result = strtoimax(buffer, &end, base); 160 + 161 + if (errno) 162 + return -errno; 163 + 164 + err = validate_int_parse(buffer, count, end); 165 + if (err) 166 + goto out; 167 + 168 + if (*result < min || *result > max) 169 + err = -EOVERFLOW; 170 + 171 + out: 172 + errno = -err; 173 + return err; 174 + } 175 + 176 + static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result, 177 + int base, uintmax_t max) 178 + { 179 + int err = 0; 180 + char *end; 181 + 182 + errno = 0; 183 + *result = strtoumax(buffer, &end, base); 184 + 185 + if (errno) 186 + return -errno; 187 + 188 + err = validate_int_parse(buffer, count, end); 189 + if (err) 190 + goto out; 191 + 192 + if (*result > max) 193 + err = -EOVERFLOW; 194 + 195 + out: 196 + errno = -err; 197 + return err; 198 + } 199 + 200 + int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base) 201 + { 202 + return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX); 203 + } 204 + 205 + int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base) 206 + { 207 + return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX); 208 + } 209 + 210 + int parse_int(const char *buffer, size_t count, int *result, int base) 211 + { 212 + intmax_t parsed; 213 + int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX); 214 + 215 + *result = parsed; 216 + return err; 217 + } 218 + 219 + int parse_uint(const char *buffer, size_t count, unsigned int *result, int base) 220 + { 221 + uintmax_t parsed; 222 + int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX); 223 + 224 + *result = parsed; 225 + return err; 226 + } 227 + 228 + int parse_long(const char *buffer, size_t count, long *result, int base) 229 + { 230 + intmax_t parsed; 231 + int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX); 232 + 233 + *result = parsed; 234 + return err; 235 + } 236 + 237 + int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base) 238 + { 239 + uintmax_t parsed; 240 + int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX); 241 + 242 + *result = parsed; 243 + return err; 244 + } 245 + 128 246 void *find_auxv_entry(int type, char *auxv) 129 247 { 130 248 ElfW(auxv_t) *p; ··· 346 224 if (err) 347 225 return err; 348 226 349 - *result = atoi(value); 350 - 351 - return 0; 227 + return parse_int(value, sizeof(value), result, 10); 352 228 } 353 229 354 230 int write_debugfs_int(const char *debugfs_file, int result)