From: Vitaliy Makkoveev 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 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); } /*