Index | Thread | Search

From:
Vitaliy Makkoveev <mvs@openbsd.org>
Subject:
sysctl(2): make sysctl_int{,_lower}() mp-safe and unlock KERN_HOSTID
To:
tech@openbsd.org
Date:
Tue, 13 Aug 2024 14:24:34 +0300

Download raw body.

Thread
Make sysctl_int() mp-safe in the sysctl_int_bounded() way and unlock
KERN_HOSTID. Except bootstrap called clockattach() of sparc64, `hostid'
never used outside kern_sysctl().

mp-safe sysctl_int() is meaningless for sysctl_int_lower(), so rework it
too. This time all affected paths are kernel locked, but this doesn't
make sysctl_int_lower() worse.

ok?

Index: sys/kern/kern_sysctl.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sysctl.c,v
diff -u -p -r1.437 kern_sysctl.c
--- sys/kern/kern_sysctl.c	11 Aug 2024 15:10:53 -0000	1.437
+++ sys/kern/kern_sysctl.c	13 Aug 2024 11:03:24 -0000
@@ -507,6 +507,10 @@ kern_sysctl(int *name, u_int namelen, vo
 		return (sysctl_rdstring(oldp, oldlenp, newp, version));
 	case KERN_NUMVNODES:  /* XXX numvnodes is a long */
 		return (sysctl_rdint(oldp, oldlenp, newp, numvnodes));
+	case KERN_HOSTID:
+		/* XXX assumes sizeof long >= sizeof int */
+		return (sysctl_int(oldp, oldlenp, newp, newlen,
+		    (int *)&hostid));
 	case KERN_CLOCKRATE:
 		return (sysctl_clockrate(oldp, oldlenp, newp));
 	case KERN_BOOTTIME: {
@@ -585,7 +589,7 @@ int
 kern_sysctl_locked(int *name, u_int namelen, void *oldp, size_t *oldlenp,
     void *newp, size_t newlen, struct proc *p)
 {
-	int error, level, inthostid, stackgap;
+	int error, level, stackgap;
 	dev_t dev;
 	extern int pool_debug;
 
@@ -623,11 +627,6 @@ kern_sysctl_locked(int *name, u_int name
 		if (newp && !error)
 			domainnamelen = newlen;
 		return (error);
-	case KERN_HOSTID:
-		inthostid = hostid;  /* XXX assumes sizeof long <= sizeof int */
-		error =  sysctl_int(oldp, oldlenp, newp, newlen, &inthostid);
-		hostid = inthostid;
-		return (error);
 	case KERN_CONSBUF:
 		if ((error = suser(p)))
 			return (error);
@@ -1055,17 +1054,36 @@ int
 sysctl_int_lower(void *oldp, size_t *oldlenp, void *newp, size_t newlen,
     int *valp)
 {
-	unsigned int oval = *valp, val = *valp;
+	unsigned int oldval, newval;
 	int error;
 
-	if (newp == NULL)
-		return (sysctl_rdint(oldp, oldlenp, newp, val));
+	if (oldp && *oldlenp < sizeof(int))
+		return (ENOMEM);
+	if (newp && newlen != sizeof(int))
+		return (EINVAL);
+	*oldlenp = sizeof(int);
+
+	if (newp) {
+		if ((error = copyin(newp, &newval, sizeof(int))))
+			return (error);
+		do {
+			oldval = atomic_load_int(valp);
+			if (oldval < (unsigned int)newval)
+				return (EPERM);	/* do not allow raising */
+		} while (atomic_cas_uint(valp, oldval, newval) != oldval);
+
+		if (oldp) {
+			/* new value has been set although user gets error */
+			if ((error = copyout(&oldval, oldp, sizeof(int))))
+				return (error);
+		}
+	} else if (oldp) {
+		oldval = atomic_load_int(valp);
+
+		if ((error = copyout(&oldval, oldp, sizeof(int))))
+			return (error);	
+	}
 
-	if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)))
-		return (error);
-	if (val > oval)
-		return (EPERM);		/* do not allow raising */
-	*(unsigned int *)valp = val;
 	return (0);
 }
 
@@ -1076,6 +1094,7 @@ sysctl_int_lower(void *oldp, size_t *old
 int
 sysctl_int(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int *valp)
 {
+	int oldval, newval;
 	int error = 0;
 
 	if (oldp && *oldlenp < sizeof(int))
@@ -1083,11 +1102,25 @@ sysctl_int(void *oldp, size_t *oldlenp, 
 	if (newp && newlen != sizeof(int))
 		return (EINVAL);
 	*oldlenp = sizeof(int);
-	if (oldp)
-		error = copyout(valp, oldp, sizeof(int));
-	if (error == 0 && newp)
-		error = copyin(newp, valp, sizeof(int));
-	return (error);
+
+	/* copyin() may sleep, call it first */
+	if (newp) {
+		if ((error = copyin(newp, &newval, sizeof(int))))
+			return (error);
+	}
+	if (oldp) {
+		if (newp)
+			oldval = atomic_swap_uint(valp, newval);
+		else
+			oldval = atomic_load_int(valp);
+		if ((error = copyout(&oldval, oldp, sizeof(int)))) {
+			/* new value has been set although user gets error */
+			return (error);
+		}
+	} else if (newp)
+		atomic_store_int(valp, newval);
+
+	return (0);
 }
 
 /*