git: 59bbb62b6078 - main - cpucontrol: Be more strict with input validation

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 28 Jan 2026 17:47:16 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=59bbb62b6078afffd1c182b63d43934248289675

commit 59bbb62b6078afffd1c182b63d43934248289675
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-01-28 16:31:41 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-01-28 17:47:02 +0000

    cpucontrol: Be more strict with input validation
    
    Avoid truncating 32-bit values.  This would have saved me a bit of time
    when I was looking at a cpuid leaf on my system and typed 0x80000001f
    instead of 0x8000001f.
    
    Reviewed by:    kib
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D54919
---
 usr.sbin/cpucontrol/cpucontrol.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c
index 9cc3968de01d..52e74a83b85b 100644
--- a/usr.sbin/cpucontrol/cpucontrol.c
+++ b/usr.sbin/cpucontrol/cpucontrol.c
@@ -36,7 +36,9 @@
 #include <errno.h>
 #include <dirent.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <paths.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -112,6 +114,21 @@ usage(void)
 	exit(EX_USAGE);
 }
 
+static uint32_t
+strtouint32(const char *str, char **endptr, int base)
+{
+	uintmax_t val;
+
+	errno = 0;
+	val = strtoumax(str, endptr, base);
+	if (*str == '\0' || errno == ERANGE || val > UINT32_MAX) {
+		WARNX(0, "invalid operand: %s", str);
+		exit(EX_USAGE);
+		/* NOTREACHED */
+	}
+	return ((uint32_t)val);
+}
+
 static int
 do_cpuid(const char *cmdarg, const char *dev)
 {
@@ -123,7 +140,7 @@ do_cpuid(const char *cmdarg, const char *dev)
 	assert(cmdarg != NULL);
 	assert(dev != NULL);
 
-	level = strtoul(cmdarg, &endptr, 16);
+	level = strtouint32(cmdarg, &endptr, 16);
 	if (*cmdarg == '\0' || *endptr != '\0') {
 		WARNX(0, "incorrect operand: %s", cmdarg);
 		usage();
@@ -162,7 +179,7 @@ do_cpuid_count(const char *cmdarg, const char *dev)
 	assert(cmdarg != NULL);
 	assert(dev != NULL);
 
-	level = strtoul(cmdarg, &endptr, 16);
+	level = strtouint32(cmdarg, &endptr, 16);
 	if (*cmdarg == '\0' || *endptr == '\0') {
 		WARNX(0, "incorrect or missing operand: %s", cmdarg);
 		usage();
@@ -172,7 +189,7 @@ do_cpuid_count(const char *cmdarg, const char *dev)
 	cmdarg1 = strstr(endptr, ",");
 	/* ... and skip past it */
 	cmdarg1 += 1;
-	level_type = strtoul(cmdarg1, &endptr1, 16);
+	level_type = strtouint32(cmdarg1, &endptr1, 16);
 	if (*cmdarg1 == '\0' || *endptr1 != '\0') {
 		WARNX(0, "incorrect or missing operand: %s", cmdarg);
 		usage();
@@ -228,7 +245,7 @@ do_msr(const char *cmdarg, const char *dev)
 	/*
 	 * Parse command string.
 	 */
-	msr = strtoul(cmdarg, &endptr, 16);
+	msr = strtouint32(cmdarg, &endptr, 16);
 	switch (*endptr) {
 	case '\0':
 		op = OP_READ;