Index: lib/libc/gen/sched_getaffinity.c =================================================================== --- lib/libc/gen/sched_getaffinity.c +++ lib/libc/gen/sched_getaffinity.c @@ -33,24 +33,15 @@ int sched_getaffinity(pid_t pid, size_t cpusetsz, cpuset_t *cpuset) { - /* - * Be more Linux-compatible: - * - return EINVAL in passed size is less than size of cpuset_t - * in advance, instead of ERANGE from the syscall - * - if passed size is larger than the size of cpuset_t, be - * permissive by claming it back to sizeof(cpuset_t) and - * zeroing the rest. - */ - if (cpusetsz < sizeof(cpuset_t)) { + int error; + + error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, + pid == 0 ? -1 : pid, cpusetsz, cpuset); + if (error == -1 && errno == ERANGE) errno = EINVAL; - return (-1); - } - if (cpusetsz > sizeof(cpuset_t)) { - memset((char *)cpuset + sizeof(cpuset_t), 0, - cpusetsz - sizeof(cpuset_t)); - cpusetsz = sizeof(cpuset_t); - } + if (error == 0) + return (cpusetsz < sizeof(cpuset_t) ? cpusetsz : + sizeof(cpuset_t)); - return (cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, - pid == 0 ? -1 : pid, cpusetsz, cpuset)); + return (error); } Index: lib/libc/gen/sched_setaffinity.c =================================================================== --- lib/libc/gen/sched_setaffinity.c +++ lib/libc/gen/sched_setaffinity.c @@ -33,18 +33,10 @@ int sched_setaffinity(pid_t pid, size_t cpusetsz, const cpuset_t *cpuset) { - cpuset_t c; int error; - if (cpusetsz > sizeof(cpuset_t)) { - errno = EINVAL; - return (-1); - } else { - memset(&c, 0, sizeof(c)); - memcpy(&c, cpuset, cpusetsz); - } error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, - pid == 0 ? -1 : pid, sizeof(cpuset_t), &c); + pid == 0 ? -1 : pid, cpusetsz, cpuset); if (error == -1 && errno == EDEADLK) errno = EINVAL; Index: lib/libc/sys/cpuset_getaffinity.2 =================================================================== --- lib/libc/sys/cpuset_getaffinity.2 +++ lib/libc/sys/cpuset_getaffinity.2 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 23, 2017 +.Dd April 27, 2022 .Dt CPUSET_GETAFFINITY 2 .Os .Sh NAME @@ -71,14 +71,19 @@ are composed using the .Dv CPU_SET macros. -The kernel tolerates large sets as long as all CPUs specified -in the set exist. -Sets smaller than the kernel uses generate an error on calls to +If the user-supplied mask is not large enough to fit all of the matching CPUs, .Fn cpuset_getaffinity -even if the result set would fit within the user supplied set. +fails with +.Er ERANGE . Calls to .Fn cpuset_setaffinity -tolerate small sets with no restrictions. +tolerate masks of any size with no restrictions. +The kernel uses a meaningful part of the mask, where the upper bound is +the maximum CPU id present in the system. +If bits for non-existing CPUs are set, calls to +.Fn cpuset_setaffinity +fails with +.Er EINVAL . .Pp The supplied mask should have a size of .Fa setsize @@ -144,7 +149,7 @@ .It Bq Er ERANGE The .Fa cpusetsize -was either preposterously large or smaller than the kernel set size. +was smaller than needed to fit all of the matching CPUs. .It Bq Er EPERM The calling process did not have the credentials required to complete the operation. Index: share/man/man3/pthread_attr_affinity_np.3 =================================================================== --- share/man/man3/pthread_attr_affinity_np.3 +++ share/man/man3/pthread_attr_affinity_np.3 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 12, 2021 +.Dd April 27, 2022 .Dt PTHREAD_ATTR_AFFINITY_NP 3 .Os .Sh NAME @@ -51,14 +51,19 @@ are composed using the .Dv CPU_SET macros. -The kernel tolerates large sets as long as all CPUs specified -in the set exist. -Sets smaller than the kernel uses generate an error on calls to -.Fn pthread_attr_getaffinity_np -even if the result set would fit within the user supplied set. +If the user-supplied mask is not large enough to fit all of the matching CPUs, +.Fn cpuset_getaffinity +fails with +.Er ERANGE . Calls to -.Fn pthread_attr_setaffinity_np -tolerate small sets with no restrictions. +.Fn cpuset_setaffinity +tolerate masks of any size with no restrictions. +The kernel uses a meaningful part of the mask, where the upper bound is +the maximum CPU id present in the system. +If bits for non-existing CPUs are set, calls to +.Fn cpuset_setaffinity +fails with +.Er EINVAL . .Pp The supplied mask should have a size of .Fa cpusetsize @@ -119,10 +124,6 @@ The .Fa cpusetp specified a CPU that was outside the set supported by the kernel. -.It Bq Er ERANGE -The -.Fa cpusetsize -is too small. .It Bq Er ENOMEM Insufficient memory exists to store the cpuset mask. .El Index: sys/kern/kern_cpuset.c =================================================================== --- sys/kern/kern_cpuset.c +++ sys/kern/kern_cpuset.c @@ -1896,13 +1896,10 @@ int error; size_t size; - if (cpusetsize < sizeof(cpuset_t) || cpusetsize > CPU_MAXSIZE / NBBY) - return (ERANGE); error = cpuset_check_capabilities(td, level, which, id); if (error != 0) return (error); - size = cpusetsize; - mask = malloc(size, M_TEMP, M_WAITOK | M_ZERO); + mask = malloc(sizeof(cpuset_t), M_TEMP, M_WAITOK | M_ZERO); error = cpuset_which(which, id, &p, &ttd, &set); if (error) goto out; @@ -1972,8 +1969,33 @@ cpuset_rel(set); if (p) PROC_UNLOCK(p); - if (error == 0) + if (error == 0) { + if (cpusetsize < howmany(CPU_FLS(mask), NBBY)) { + error = ERANGE; + goto out; + } + size = min(cpusetsize, sizeof(cpuset_t)); error = copyout(mask, maskp, size); + if (error != 0) + goto out; + if (cpusetsize > size) { + char *end; + char *cp; + int rv; + + end = cp = (char *)&maskp->__bits; + end += cpusetsize; + cp += size; + while (cp != end) { + rv = subyte(cp, 0); + if (rv == -1) { + error = EFAULT; + goto out; + } + cp++; + } + } + } out: free(mask, M_TEMP); return (error); @@ -2006,31 +2028,38 @@ struct proc *p; cpuset_t *mask; int error; + size_t size; - if (cpusetsize < sizeof(cpuset_t) || cpusetsize > CPU_MAXSIZE / NBBY) - return (ERANGE); error = cpuset_check_capabilities(td, level, which, id); if (error != 0) return (error); - mask = malloc(cpusetsize, M_TEMP, M_WAITOK | M_ZERO); - error = copyin(maskp, mask, cpusetsize); + size = min(cpusetsize, sizeof(cpuset_t)); + mask = malloc(sizeof(cpuset_t), M_TEMP, M_WAITOK | M_ZERO); + error = copyin(maskp, mask, size); if (error) goto out; /* * Verify that no high bits are set. */ if (cpusetsize > sizeof(cpuset_t)) { - char *end; - char *cp; - - end = cp = (char *)&mask->__bits; + const char *end, *cp; + int val; + end = cp = (const char *)&maskp->__bits; end += cpusetsize; cp += sizeof(cpuset_t); - while (cp != end) - if (*cp++ != 0) { + + while (cp != end) { + val = fubyte(cp); + if (val == -1) { + error = EFAULT; + goto out; + } + if (val != 0) { error = EINVAL; goto out; } + cp++; + } } if (CPU_EMPTY(mask)) { error = EDEADLK;