From f1b18a668deb2aab9e2da908a403d58bce029d80 Mon Sep 17 00:00:00 2001 From: Kyle Evans Date: Tue, 8 Dec 2020 18:47:22 +0000 Subject: [PATCH] cpuset_set{affinity,domain}: do not allow empty masks cpuset_modify() would not currently catch this, because it only checks that the new mask is a subset of the root set and circumvents the EDEADLK check in cpuset_testupdate(). This change both directly validates the mask coming in since we can trivially detect an empty mask, and it updates cpuset_testupdate to catch stuff like this going forward by always ensuring we don't end up with an empty mask. The check_mask argument has been renamed because the 'check' verbiage does not imply to me that it's actually doing a different operation. We're either augmenting the existing mask, or we are replacing it entirely. Reported by: syzbot+4e3b1009de98d2fabcda@syzkaller.appspotmail.com Discussed with: andrew Reviewed by: andrew, markj MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D27511 --- sys/kern/kern_cpuset.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sys/kern/kern_cpuset.c b/sys/kern/kern_cpuset.c index be70182b3dea..8b962190a3fa 100644 --- a/sys/kern/kern_cpuset.c +++ b/sys/kern/kern_cpuset.c @@ -633,7 +633,7 @@ domainset_shadow(const struct domainset *pdomain, * empty as well as RDONLY flags. */ static int -cpuset_testupdate(struct cpuset *set, cpuset_t *mask, int check_mask) +cpuset_testupdate(struct cpuset *set, cpuset_t *mask, int augment_mask) { struct cpuset *nset; cpuset_t newmask; @@ -642,13 +642,14 @@ cpuset_testupdate(struct cpuset *set, cpuset_t *mask, int check_mask) mtx_assert(&cpuset_lock, MA_OWNED); if (set->cs_flags & CPU_SET_RDONLY) return (EPERM); - if (check_mask) { - if (!CPU_OVERLAP(&set->cs_mask, mask)) - return (EDEADLK); + if (augment_mask) { CPU_COPY(&set->cs_mask, &newmask); CPU_AND(&newmask, mask); } else CPU_COPY(mask, &newmask); + + if (CPU_EMPTY(&newmask)) + return (EDEADLK); error = 0; LIST_FOREACH(nset, &set->cs_children, cs_siblings) if ((error = cpuset_testupdate(nset, &newmask, 1)) != 0) @@ -723,7 +724,7 @@ cpuset_modify(struct cpuset *set, cpuset_t *mask) */ static int cpuset_testupdate_domain(struct cpuset *set, struct domainset *dset, - struct domainset *orig, int *count, int check_mask) + struct domainset *orig, int *count, int augment_mask __unused) { struct cpuset *nset; struct domainset *domain; @@ -2001,6 +2002,10 @@ kern_cpuset_setaffinity(struct thread *td, cpulevel_t level, cpuwhich_t which, goto out; } } + if (CPU_EMPTY(mask)) { + error = EDEADLK; + goto out; + } switch (level) { case CPU_LEVEL_ROOT: case CPU_LEVEL_CPUSET: @@ -2255,6 +2260,10 @@ kern_cpuset_setdomain(struct thread *td, cpulevel_t level, cpuwhich_t which, goto out; } } + if (DOMAINSET_EMPTY(mask)) { + error = EDEADLK; + goto out; + } DOMAINSET_COPY(mask, &domain.ds_mask); domain.ds_policy = policy;