git: 406da392ef8d - main - sysctl(9): Booleans: Accept integers to ease knob conversion

From: Olivier Certner <olce_at_FreeBSD.org>
Date: Tue, 03 Feb 2026 17:20:20 UTC
The branch main has been updated by olce:

URL: https://cgit.FreeBSD.org/src/commit/?id=406da392ef8d77b39fe9bfd36bdb440eb3e5ae0a

commit 406da392ef8d77b39fe9bfd36bdb440eb3e5ae0a
Author:     Olivier Certner <olce@FreeBSD.org>
AuthorDate: 2026-01-27 09:45:03 +0000
Commit:     Olivier Certner <olce@FreeBSD.org>
CommitDate: 2026-02-03 17:19:07 +0000

    sysctl(9): Booleans: Accept integers to ease knob conversion
    
    In sysctl_handle_bool(), if the output buffer (for the old value) has
    room for exactly 4 bytes (sizeof(int)), then output the current boolean
    value as an integer rather than a 'uint8_t'.  Conversely, if 4 bytes
    exactly remain in the input buffer (for the new value), treat them as an
    integer and derive the new boolean value from it.
    
    Doing so allows to convert existing integer syscstl knobs that are
    interpreted as a boolean into true boolean ones while staying
    backwards-compatible.
    
    That brings no drawback as no code currently uses sysctl_handle_bool()
    as part of a series of calls to sysctl_handle_*() functions for
    (de)serialization of some compound structure.  If that case ever
    materializes, it can be easily solved, e.g., by creating
    a sysctl_handle_bool_strict() variant.
    
    In the future, we might want to go further and generally be more liberal
    in the external type of integers we accept and output, by tolerating any
    kind of supported integers (8-bit to 64-bit), enabling integer type
    changes of knob's internal representations without breaking the ABI for
    consumers hardcoding the passed integers (instead of relying on sysctl
    knob type information).
    
    Reviewed by:    jhb
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D54925
---
 sys/kern/kern_sysctl.c | 38 +++++++++++++++++++++++++++++++++-----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c
index 25da134661e9..dbe509b3e8e2 100644
--- a/sys/kern/kern_sysctl.c
+++ b/sys/kern/kern_sysctl.c
@@ -1619,8 +1619,8 @@ static SYSCTL_NODE(_sysctl, CTL_SYSCTL_OIDLABEL, oidlabel, CTLFLAG_RD |
 int
 sysctl_handle_bool(SYSCTL_HANDLER_ARGS)
 {
-	uint8_t temp;
 	int error;
+	uint8_t temp;
 
 	/*
 	 * Attempt to get a coherent snapshot by making a copy of the data.
@@ -1630,16 +1630,44 @@ sysctl_handle_bool(SYSCTL_HANDLER_ARGS)
 	else
 		temp = arg2 ? 1 : 0;
 
-	error = SYSCTL_OUT(req, &temp, sizeof(temp));
+	/*
+	 * In order to support backwards-compatible conversion of integer knobs
+	 * that are used as booleans to true boolean knobs, whose internal state
+	 * is stored as a 'bool' and not an 'int', if exactly 4 bytes remain in
+	 * the output buffer, we assume that the caller expected an 'int'
+	 * instead of a 'uint8_t'.
+	 */
+	if (req->oldidx >= req->oldlen)
+		return (ENOMEM);
+	if (req->oldlen - req->oldidx == sizeof(int)) {
+		int temp_int = temp;
+
+		error = SYSCTL_OUT(req, &temp_int, sizeof(temp_int));
+	} else
+		error = SYSCTL_OUT(req, &temp, sizeof(temp));
 	if (error || !req->newptr)
 		return (error);
 
 	if (!arg1)
 		error = EPERM;
 	else {
-		error = SYSCTL_IN(req, &temp, sizeof(temp));
-		if (!error)
-			*(bool *)arg1 = temp ? 1 : 0;
+		/*
+		 * Conversely, if the input buffer has exactly 4 bytes to read,
+		 * use them all to produce a bool.
+		 */
+		if (req->newidx >= req->newlen)
+			return (ENOMEM);
+		if (req->newlen - req->newidx == sizeof(int)) {
+			int temp_int;
+
+			error = SYSCTL_IN(req, &temp_int, sizeof(temp_int));
+			if (error == 0)
+				*(bool *)arg1 = temp_int != 0 ? 1 : 0;
+		} else {
+			error = SYSCTL_IN(req, &temp, sizeof(temp));
+			if (error == 0)
+				*(bool *)arg1 = temp != 0 ? 1 : 0;
+		}
 	}
 	return (error);
 }