# TrueNAS Core 13.3-U1.2: further security backports # Apply from the truenas/os source root with: patch -p1 < /tmp/truenas-core-13.3-further-fixes.patch # OS: truenas/os@41f6a830f8ee1254c5a3efcb204d9ed6733b2f76 # Vendor ZFS must be in zfs/: truenas/zfs@4f2aa13822f0db5e9b94a76f3a6b5165a282b30b diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index 0ef8fe949..550b75898 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -211,7 +211,6 @@ static struct pmc_owner *pmc_allocate_owner_descriptor(struct proc *p); static int pmc_attach_one_process(struct proc *p, struct pmc *pm); static int pmc_can_allocate_rowindex(struct proc *p, unsigned int ri, int cpu); -static int pmc_can_attach(struct pmc *pm, struct proc *p); static void pmc_capture_user_callchain(int cpu, int soft, struct trapframe *tf); static void pmc_cleanup(void); static int pmc_detach_process(struct proc *p, struct pmc *pm); @@ -1040,61 +1039,6 @@ pmc_unlink_target_process(struct pmc *pm, struct pmc_process *pp) } } -/* - * Check if PMC 'pm' may be attached to target process 't'. - */ - -static int -pmc_can_attach(struct pmc *pm, struct proc *t) -{ - struct proc *o; /* pmc owner */ - struct ucred *oc, *tc; /* owner, target credentials */ - int decline_attach, i; - - /* - * A PMC's owner can always attach that PMC to itself. - */ - - if ((o = pm->pm_owner->po_owner) == t) - return 0; - - PROC_LOCK(o); - oc = o->p_ucred; - crhold(oc); - PROC_UNLOCK(o); - - PROC_LOCK(t); - tc = t->p_ucred; - crhold(tc); - PROC_UNLOCK(t); - - /* - * The effective uid of the PMC owner should match at least one - * of the {effective,real,saved} uids of the target process. - */ - - decline_attach = oc->cr_uid != tc->cr_uid && - oc->cr_uid != tc->cr_svuid && - oc->cr_uid != tc->cr_ruid; - - /* - * Every one of the target's group ids, must be in the owner's - * group list. - */ - for (i = 0; !decline_attach && i < tc->cr_ngroups; i++) - decline_attach = !groupmember(tc->cr_groups[i], oc); - - /* check the read and saved gids too */ - if (decline_attach == 0) - decline_attach = !groupmember(tc->cr_rgid, oc) || - !groupmember(tc->cr_svgid, oc); - - crfree(tc); - crfree(oc); - - return !decline_attach; -} - /* * Attach a process to a PMC. */ @@ -2188,12 +2132,24 @@ pmc_hook_handler(struct thread *td, int function, void *arg) * the PMC's owner has sufficient privilege. */ - for (ri = 0; ri < md->pmd_npmc; ri++) - if ((pm = pp->pp_pmcs[ri].pp_pmc) != NULL) - if (pmc_can_attach(pm, td->td_proc) != 0) + for (ri = 0; ri < md->pmd_npmc; ri++) { + if ((pm = pp->pp_pmcs[ri].pp_pmc) != NULL) { + struct proc *owner; + struct ucred *cred; + + owner = pm->pm_owner->po_owner; + PROC_LOCK(owner); + cred = crhold(owner->p_ucred); + PROC_UNLOCK(owner); + + if (priv_check_cred(cred, PRIV_DEBUG_DIFFCRED) != 0) pmc_detach_one_process(td->td_proc, pm, PMC_FLAG_NONE); + crfree(cred); + } + } + KASSERT(pp->pp_refcnt >= 0 && pp->pp_refcnt <= (int) md->pmd_npmc, ("[pmc,%d] Illegal ref count %d on pp %p", __LINE__, pp->pp_refcnt, pp)); @@ -2205,7 +2161,6 @@ pmc_hook_handler(struct thread *td, int function, void *arg) */ if (pp->pp_refcnt == 0) { - pmc_remove_process_descriptor(pp); pmc_destroy_process_descriptor(pp); break; } diff --git a/sys/dev/netmap/netmap_freebsd.c b/sys/dev/netmap/netmap_freebsd.c index 63d1cc84d..6a7dd4bd0 100644 --- a/sys/dev/netmap/netmap_freebsd.c +++ b/sys/dev/netmap/netmap_freebsd.c @@ -119,6 +119,7 @@ nm_os_selinfo_uninit(NM_SELINFO_T *si) taskqueue_drain(si->ntfytq, &si->ntfytask); taskqueue_free(si->ntfytq); si->ntfytq = NULL; + seldrain(&si->si); knlist_delete(&si->si.si_note, curthread, /*islocked=*/0); knlist_destroy(&si->si.si_note); /* now we don't need the mutex anymore */ diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index c23d729f4..804542575 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1533,6 +1533,8 @@ typedef void (*segment_callback)(vm_map_entry_t, void *); struct phdr_closure { Elf_Phdr *phdr; /* Program header to fill in */ Elf_Off offset; /* Offset of segment in core file */ + int numsegs; /* Maximum number of segments */ + int nextseg; /* Next segment to fill in */ }; struct note_info { @@ -1646,10 +1648,15 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) } /* - * Allocate memory for building the header, fill it up, - * and write it out following the notes. + * Allocate memory for building the header, fill it up, and write it out + * following the notes. + * + * Note that a process sharing our vmspace might be concurrently + * mutating the map, in which case we could populate fewer than + * seginfo.count headers. Zero the buffer to ensure that unpopulated + * headers are still initialized. */ - hdr = malloc(hdrsize, M_TEMP, M_WAITOK); + hdr = malloc(hdrsize, M_TEMP, M_WAITOK | M_ZERO); error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, notesz, flags); @@ -1702,6 +1709,11 @@ cb_put_phdr(vm_map_entry_t entry, void *closure) struct phdr_closure *phc = (struct phdr_closure *)closure; Elf_Phdr *phdr = phc->phdr; + if (phc->nextseg >= phc->numsegs) { + /* Only write as many headers as we have space for. */ + return; + } + phc->offset = round_page(phc->offset); phdr->p_type = PT_LOAD; @@ -1714,6 +1726,8 @@ cb_put_phdr(vm_map_entry_t entry, void *closure) phc->offset += phdr->p_filesz; phc->phdr++; + + phc->nextseg++; } /* @@ -1974,6 +1988,8 @@ __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, /* All the writable segments from the program. */ phc.phdr = phdr; phc.offset = round_page(hdrsize + notesz); + phc.numsegs = numsegs; + phc.nextseg = 0; each_dumpable_segment(td, cb_put_phdr, &phc, flags); } diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index e8e3d8d88..69ae19d4f 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -940,7 +940,7 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p, */ if (PMC_SYSTEM_SAMPLING_ACTIVE() || PMC_PROC_IS_USING_PMCS(p)) { VOP_UNLOCK(imgp->vp); - pe.pm_credentialschanged = credential_changing; + pe.pm_credentialschanged = imgp->credential_setid; pe.pm_entryaddr = imgp->entry_addr; PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC, (void *) &pe); @@ -1669,7 +1669,7 @@ exec_args_adjust_args(struct image_args *args, size_t consume, ssize_t extend) if (args->stringspace < offset) return (E2BIG); memmove(args->begin_argv + extend, args->begin_argv + consume, - args->endp - args->begin_argv + consume); + args->endp - (args->begin_argv + consume)); if (args->envc > 0) args->begin_envv += offset; args->endp += offset; diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 6b9ea8d11..59ae90654 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -2578,23 +2578,26 @@ ptrace_syscallreq(struct thread *td, struct proc *p, struct sysentvec *sv; struct sysent *se; register_t rv_saved[2]; + unsigned int sc; int error, nerror; - int sc; bool audited, sy_thr_static; - sv = p->p_sysent; - if (sv->sv_table == NULL || sv->sv_size < tsr->ts_sa.code) { - tsr->ts_ret.sr_error = ENOSYS; - return; - } - sc = tsr->ts_sa.code; if (sc == SYS_syscall || sc == SYS___syscall) { + if (tsr->ts_nargs == 0) { + tsr->ts_ret.sr_error = EINVAL; + return; + } sc = tsr->ts_sa.args[0]; memmove(&tsr->ts_sa.args[0], &tsr->ts_sa.args[1], sizeof(register_t) * (tsr->ts_nargs - 1)); } + sv = p->p_sysent; + if (sv->sv_table == NULL || sc >= sv->sv_size) { + tsr->ts_ret.sr_error = ENOSYS; + return; + } tsr->ts_sa.callp = se = &sv->sv_table[sc]; VM_CNT_INC(v_syscall); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index 22c869e68..577f89ce0 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -478,7 +478,7 @@ sys_thr_kill2(struct thread *td, struct thr_kill2_args *uap) p = ttd->td_proc; AUDIT_ARG_PROCESS(p); error = p_cansignal(td, p, uap->sig); - if (uap->sig == 0) + if (error != 0 || uap->sig == 0) ; else if (!_SIG_VALID(uap->sig)) error = EINVAL; diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c index 94050beee..29931499f 100644 --- a/sys/kern/kern_umtx.c +++ b/sys/kern/kern_umtx.c @@ -4289,8 +4289,7 @@ __umtx_op_sem2_wake(struct thread *td, struct _umtx_op_args *uap, #define USHM_OBJ_UMTX(o) \ ((struct umtx_shm_obj_list *)(&(o)->umtx_data)) -#define USHMF_REG_LINKED 0x0001 -#define USHMF_OBJ_LINKED 0x0002 +#define USHMF_LINKED 0x0001 struct umtx_shm_reg { TAILQ_ENTRY(umtx_shm_reg) ushm_reg_link; LIST_ENTRY(umtx_shm_reg) ushm_obj_link; @@ -4331,8 +4330,17 @@ umtx_shm_reg_delfree_tq(void *context __unused, int pending __unused) static struct task umtx_shm_reg_delfree_task = TASK_INITIALIZER(0, umtx_shm_reg_delfree_tq, NULL); -static struct umtx_shm_reg * -umtx_shm_find_reg_locked(const struct umtx_key *key) +/* + * Returns 0 if a SHM with the passed key is found in the registry, in which + * case it is returned through 'oreg'. Otherwise, returns an error among ESRCH + * (no corresponding SHM; ESRCH was chosen for compatibility, ENOENT would have + * been preferable) or EOVERFLOW (there is a corresponding SHM, but reference + * count would overflow, so can't return it), in which case '*oreg' is left + * unchanged. + */ +static int +umtx_shm_find_reg_locked(const struct umtx_key *key, + struct umtx_shm_reg **const oreg) { struct umtx_shm_reg *reg; struct umtx_shm_reg_head *reg_head; @@ -4348,26 +4356,38 @@ umtx_shm_find_reg_locked(const struct umtx_key *key) reg->ushm_key.info.shared.offset == key->info.shared.offset) { KASSERT(reg->ushm_key.type == TYPE_SHM, ("TYPE_USHM")); - KASSERT(reg->ushm_refcnt > 0, + KASSERT(reg->ushm_refcnt != 0, ("reg %p refcnt 0 onlist", reg)); - KASSERT((reg->ushm_flags & USHMF_REG_LINKED) != 0, + KASSERT((reg->ushm_flags & USHMF_LINKED) != 0, ("reg %p not linked", reg)); + /* + * Don't let overflow happen, just deny a new reference + * (this is additional protection against some reference + * count leak, which is known not to be the case at the + * time of this writing). + */ + if (__predict_false(reg->ushm_refcnt == UINT_MAX)) + return (EOVERFLOW); reg->ushm_refcnt++; - return (reg); + *oreg = reg; + return (0); } } - return (NULL); + return (ESRCH); } -static struct umtx_shm_reg * -umtx_shm_find_reg(const struct umtx_key *key) +/* + * Calls umtx_shm_find_reg_unlocked() under the 'umtx_shm_lock'. + */ +static int +umtx_shm_find_reg(const struct umtx_key *key, struct umtx_shm_reg **const oreg) { - struct umtx_shm_reg *reg; + int error; mtx_lock(&umtx_shm_lock); - reg = umtx_shm_find_reg_locked(key); + error = umtx_shm_find_reg_locked(key, oreg); mtx_unlock(&umtx_shm_lock); - return (reg); + return (error); } static void @@ -4381,42 +4401,49 @@ umtx_shm_free_reg(struct umtx_shm_reg *reg) } static bool -umtx_shm_unref_reg_locked(struct umtx_shm_reg *reg, bool force) +umtx_shm_unref_reg_locked(struct umtx_shm_reg *reg, bool linked_ref) { - bool res; - mtx_assert(&umtx_shm_lock, MA_OWNED); - KASSERT(reg->ushm_refcnt > 0, ("ushm_reg %p refcnt 0", reg)); - reg->ushm_refcnt--; - res = reg->ushm_refcnt == 0; - if (res || force) { - if ((reg->ushm_flags & USHMF_REG_LINKED) != 0) { - TAILQ_REMOVE(&umtx_shm_registry[reg->ushm_key.hash], - reg, ushm_reg_link); - reg->ushm_flags &= ~USHMF_REG_LINKED; - } - if ((reg->ushm_flags & USHMF_OBJ_LINKED) != 0) { - LIST_REMOVE(reg, ushm_obj_link); - reg->ushm_flags &= ~USHMF_OBJ_LINKED; - } + KASSERT(reg->ushm_refcnt != 0, ("ushm_reg %p refcnt 0", reg)); + + if (linked_ref) { + if ((reg->ushm_flags & USHMF_LINKED) == 0) + /* + * The reference tied to USHMF_LINKED has already been + * released concurrently. + */ + return (false); + + TAILQ_REMOVE(&umtx_shm_registry[reg->ushm_key.hash], reg, + ushm_reg_link); + LIST_REMOVE(reg, ushm_obj_link); + reg->ushm_flags &= ~USHMF_LINKED; } - return (res); + + reg->ushm_refcnt--; + return (reg->ushm_refcnt == 0); } static void -umtx_shm_unref_reg(struct umtx_shm_reg *reg, bool force) +umtx_shm_unref_reg(struct umtx_shm_reg *reg, bool linked_ref) { vm_object_t object; bool dofree; - if (force) { + if (linked_ref) { + /* + * Note: This may be executed multiple times on the same + * shared-memory VM object in presence of concurrent callers + * because 'umtx_shm_lock' is not held all along in umtx_shm() + * and here. + */ object = reg->ushm_obj->shm_object; VM_OBJECT_WLOCK(object); vm_object_set_flag(object, OBJ_UMTXDEAD); VM_OBJECT_WUNLOCK(object); } mtx_lock(&umtx_shm_lock); - dofree = umtx_shm_unref_reg_locked(reg, force); + dofree = umtx_shm_unref_reg_locked(reg, linked_ref); mtx_unlock(&umtx_shm_lock); if (dofree) umtx_shm_free_reg(reg); @@ -4460,16 +4487,22 @@ umtx_shm_create_reg(struct thread *td, const struct umtx_key *key, struct ucred *cred; int error; - reg = umtx_shm_find_reg(key); - if (reg != NULL) { - *res = reg; - return (0); + error = umtx_shm_find_reg(key, res); + if (error != ESRCH) { + /* + * Either no error occured, and '*res' was filled, or EOVERFLOW + * was returned, indicating a reference count limit, and we + * won't create a duplicate registration. In both cases, we are + * done. + */ + return (error); } + /* No entry, we will create one. */ + cred = td->td_ucred; if (!chgumtxcnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_UMTXP))) return (ENOMEM); reg = uma_zalloc(umtx_shm_reg_zone, M_WAITOK | M_ZERO); - reg->ushm_refcnt = 1; bcopy(key, ®->ushm_key, sizeof(*key)); reg->ushm_obj = shm_alloc(td->td_ucred, O_RDWR, false); reg->ushm_cred = crhold(cred); @@ -4479,18 +4512,32 @@ umtx_shm_create_reg(struct thread *td, const struct umtx_key *key, return (error); } mtx_lock(&umtx_shm_lock); - reg1 = umtx_shm_find_reg_locked(key); - if (reg1 != NULL) { + /* Re-lookup as 'umtx_shm_lock' has been temporarily released. */ + error = umtx_shm_find_reg_locked(key, ®1); + switch (error) { + case 0: mtx_unlock(&umtx_shm_lock); umtx_shm_free_reg(reg); *res = reg1; return (0); + case ESRCH: + break; + default: + mtx_unlock(&umtx_shm_lock); + umtx_shm_free_reg(reg); + return (error); } - reg->ushm_refcnt++; TAILQ_INSERT_TAIL(&umtx_shm_registry[key->hash], reg, ushm_reg_link); LIST_INSERT_HEAD(USHM_OBJ_UMTX(key->info.shared.object), reg, ushm_obj_link); - reg->ushm_flags = USHMF_REG_LINKED | USHMF_OBJ_LINKED; + reg->ushm_flags = USHMF_LINKED; + /* + * This is one reference for the registry and the list of shared + * mutexes referenced by the VM object containing the lock pointer, and + * another for the caller, which it will free after use. So, one of + * these is tied to the presence of USHMF_LINKED. + */ + reg->ushm_refcnt = 2; mtx_unlock(&umtx_shm_lock); *res = reg; return (0); @@ -4549,13 +4596,9 @@ umtx_shm(struct thread *td, void *addr, u_int flags) if (error != 0) return (error); KASSERT(key.shared == 1, ("non-shared key")); - if ((flags & UMTX_SHM_CREAT) != 0) { - error = umtx_shm_create_reg(td, &key, ®); - } else { - reg = umtx_shm_find_reg(&key); - if (reg == NULL) - error = ESRCH; - } + error = (flags & UMTX_SHM_CREAT) != 0 ? + umtx_shm_create_reg(td, &key, ®) : + umtx_shm_find_reg(&key, ®); umtx_key_release(&key); if (error != 0) return (error); diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c index 307aea407..ccf9b2402 100644 --- a/sys/kern/sys_procdesc.c +++ b/sys/kern/sys_procdesc.c @@ -270,6 +270,7 @@ procdesc_free(struct procdesc *pd) KASSERT((pd->pd_flags & PDF_CLOSED), ("procdesc_free: !PDF_CLOSED")); + seldrain(&pd->pd_selinfo); knlist_destroy(&pd->pd_selinfo.si_note); PROCDESC_LOCK_DESTROY(pd); free(pd, M_PROCDESC); @@ -312,10 +313,7 @@ procdesc_exit(struct proc *p) procdesc_free(pd); return (1); } - if (pd->pd_flags & PDF_SELECTED) { - pd->pd_flags &= ~PDF_SELECTED; - selwakeup(&pd->pd_selinfo); - } + selwakeup(&pd->pd_selinfo); KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT); PROCDESC_UNLOCK(pd); return (0); @@ -430,10 +428,8 @@ procdesc_poll(struct file *fp, int events, struct ucred *active_cred, PROCDESC_LOCK(pd); if (pd->pd_flags & PDF_EXITED) revents |= POLLHUP; - if (revents == 0) { + else selrecord(td, &pd->pd_selinfo); - pd->pd_flags |= PDF_SELECTED; - } PROCDESC_UNLOCK(pd); return (revents); } diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c index 381c1ac4f..59237e945 100644 --- a/sys/kern/sysv_sem.c +++ b/sys/kern/sysv_sem.c @@ -108,7 +108,7 @@ int semop(struct thread *td, struct semop_args *uap); static struct sem_undo *semu_alloc(struct thread *td); static int semundo_adjust(struct thread *td, struct sem_undo **supptr, - int semid, int semseq, int semnum, int adjval); + int semid, uint64_t semseq, int semnum, int adjval); static void semundo_clear(int semid, int semnum); static struct mtx sem_mtx; /* semaphore global lock */ @@ -116,6 +116,7 @@ static struct mtx sem_undo_mtx; static int semtot = 0; static struct semid_kernel *sema; /* semaphore id pool */ static struct mtx *sema_mtx; /* semaphore id pool mutexes*/ +static uint64_t *sema_seq; /* semaphore id sequence numbers */ static struct sem *sem; /* semaphore pool */ LIST_HEAD(, sem_undo) semu_list; /* list of active undo structures */ LIST_HEAD(, sem_undo) semu_free_list; /* list of free undo structures */ @@ -146,7 +147,7 @@ struct sem_undo { short un_adjval; /* adjust on exit values */ short un_num; /* semaphore # */ int un_id; /* semid */ - unsigned short un_seq; + uint64_t un_seq; } un_ent[1]; /* undo entries */ }; @@ -282,6 +283,8 @@ seminit(void) M_WAITOK | M_ZERO); sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM, M_WAITOK | M_ZERO); + sema_seq = malloc(sizeof(uint64_t) * seminfo.semmni, M_SEM, + M_WAITOK | M_ZERO); seminfo.semusz = SEMUSZ(seminfo.semume); semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK); @@ -367,6 +370,7 @@ semunload(void) for (i = 0; i < seminfo.semmni; i++) mtx_destroy(&sema_mtx[i]); free(sema_mtx, M_SEM); + free(sema_seq, M_SEM); mtx_destroy(&sem_mtx); mtx_destroy(&sem_undo_mtx); return (0); @@ -441,7 +445,7 @@ semu_try_free(struct sem_undo *suptr) static int semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid, - int semseq, int semnum, int adjval) + uint64_t semseq, int semnum, int adjval) { struct proc *p = td->td_proc; struct sem_undo *suptr; @@ -698,6 +702,7 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, struct semid_ds *sbuf; struct semid_kernel *semakptr; struct mtx *sema_mtxp; + uint64_t seq; u_short usval, count; int semidx; @@ -849,25 +854,23 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, * won't work for SETALL since we can't copyin() more * data than the user specified as we may return a * spurious EFAULT. - * - * Note that the number of semaphores in a set is - * fixed for the life of that set. The only way that - * the 'count' could change while are blocked in - * malloc() is if this semaphore set were destroyed - * and a new one created with the same index. - * However, semvalid() will catch that due to the - * sequence number unless exactly 0x8000 (or a - * multiple thereof) semaphore sets for the same index - * are created and destroyed while we are in malloc! - * */ + if ((error = semvalid(semid, rpr, semakptr)) != 0) + goto done2; count = semakptr->u.sem_nsems; + seq = sema_seq[semidx]; mtx_unlock(sema_mtxp); array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK); mtx_lock(sema_mtxp); if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; - KASSERT(count == semakptr->u.sem_nsems, ("nsems changed")); + if (seq != sema_seq[semidx]) { + error = EAGAIN; + goto done2; + } + KASSERT(count == semakptr->u.sem_nsems, + ("sem_nsems changed from %d to %d", + count, semakptr->u.sem_nsems)); if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; for (i = 0; i < semakptr->u.sem_nsems; i++) @@ -910,11 +913,10 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, break; case SETALL: - /* - * See comment on GETALL for why 'count' shouldn't change - * and why we require a userland buffer. - */ + if ((error = semvalid(semid, rpr, semakptr)) != 0) + goto done2; count = semakptr->u.sem_nsems; + seq = sema_seq[semidx]; mtx_unlock(sema_mtxp); array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK); error = copyin(arg->array, array, count * sizeof(*array)); @@ -923,7 +925,10 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, break; if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; - KASSERT(count == semakptr->u.sem_nsems, ("nsems changed")); + if (seq != sema_seq[semidx]) { + error = EAGAIN; + goto done2; + } if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W))) goto done2; for (i = 0; i < semakptr->u.sem_nsems; i++) { @@ -1058,8 +1063,8 @@ sys_semget(struct thread *td, struct semget_args *uap) sema[semid].u.sem_perm.gid = cred->cr_gid; sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC; sema[semid].cred = crhold(cred); - sema[semid].u.sem_perm.seq = - (sema[semid].u.sem_perm.seq + 1) & 0x7fff; + sema_seq[semid]++; + sema[semid].u.sem_perm.seq = sema_seq[semid] & 0x7fff; sema[semid].u.sem_nsems = nsems; sema[semid].u.sem_otime = 0; sema[semid].u.sem_ctime = time_second; @@ -1115,10 +1120,10 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, struct sem_undo *suptr; struct mtx *sema_mtxp; sbintime_t sbt, precision; - size_t i, j, k; + uint64_t seq; + size_t i, j, k, perms; int error; - int do_wakeup, do_undos; - unsigned short seq; + bool do_wakeup, do_undos; #ifdef SEM_DEBUG sops = NULL; @@ -1190,20 +1195,18 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, error = EINVAL; goto done2; } - seq = semakptr->u.sem_perm.seq; - if (seq != IPCID_TO_SEQ(usemid)) { + if (semvalid(usemid, rpr, semakptr) != 0) { error = EINVAL; goto done2; } - if ((error = sem_prison_cansee(rpr, semakptr)) != 0) - goto done2; + /* * Initial pass through sops to see what permissions are needed. * Also perform any checks that don't need repeating on each * attempt to satisfy the request vector. */ - j = 0; /* permission needed */ - do_undos = 0; + perms = 0; + do_undos = false; for (i = 0; i < nsops; i++) { sopptr = &sops[i]; if (sopptr->sem_num >= semakptr->u.sem_nsems) { @@ -1211,16 +1214,16 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, goto done2; } if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0) - do_undos = 1; - j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A; + do_undos = true; + perms |= (sopptr->sem_op == 0) ? SEM_R : SEM_A; } - if ((error = ipcperm(td, &semakptr->u.sem_perm, j))) { + if ((error = ipcperm(td, &semakptr->u.sem_perm, perms))) { DPRINTF(("error = %d from ipaccess\n", error)); goto done2; } #ifdef MAC - error = mac_sysvsem_check_semop(td->td_ucred, semakptr, j); + error = mac_sysvsem_check_semop(td->td_ucred, semakptr, perms); if (error != 0) goto done2; #endif @@ -1235,8 +1238,9 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, * of requests is atomic (never partially satisfied). */ for (;;) { - do_wakeup = 0; + do_wakeup = false; error = 0; /* error return if necessary */ + seq = sema_seq[semid]; for (i = 0; i < nsops; i++) { sopptr = &sops[i]; @@ -1258,7 +1262,7 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, semptr->semval += sopptr->sem_op; if (semptr->semval == 0 && semptr->semzcnt > 0) - do_wakeup = 1; + do_wakeup = true; } } else if (sopptr->sem_op == 0) { if (semptr->semval != 0) { @@ -1271,7 +1275,7 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, break; } else { if (semptr->semncnt > 0) - do_wakeup = 1; + do_wakeup = true; semptr->semval += sopptr->sem_op; } } @@ -1315,11 +1319,12 @@ kern_semop(struct thread *td, int usemid, struct sembuf *usops, /* return code is checked below, after sem[nz]cnt-- */ /* - * Make sure that the semaphore still exists + * Make sure that the semaphore still exists. The embedded + * sequence number check isn't sufficient to detect reallocation + * since it's too narrow. */ - seq = semakptr->u.sem_perm.seq; - if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || - seq != IPCID_TO_SEQ(usemid)) { + if (semvalid(usemid, rpr, semakptr) != 0 || + seq != sema_seq[semid]) { error = EIDRM; goto done2; } @@ -1445,8 +1450,8 @@ semexit_myhook(void *arg, struct proc *p) struct sem_undo *suptr; struct semid_kernel *semakptr; struct mtx *sema_mtxp; + uint64_t seq; int semid, semnum, adjval, ix; - unsigned short seq; /* * Go through the chain of undo vectors looking for one @@ -1483,12 +1488,11 @@ semexit_myhook(void *arg, struct proc *p) mtx_lock(sema_mtxp); if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || - (semakptr->u.sem_perm.seq != seq)) { + sema_seq[semid] != seq || + semakptr->u.sem_nsems <= semnum) { mtx_unlock(sema_mtxp); continue; } - if (semnum >= semakptr->u.sem_nsems) - panic("semexit - semnum out of range"); DPRINTF(( "semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n", diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 8ed59fd16..c5f8d7c50 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1260,6 +1260,10 @@ tty_drop_ctty(struct tty *tp, struct proc *p) session->s_ttydp = NULL; SESS_UNLOCK(session); + if (tp->t_session == session) { + tp->t_session = NULL; + tp->t_pgrp = NULL; + } tp->t_sessioncnt--; p->p_flag &= ~P_CONTROLT; PROC_UNLOCK(p); @@ -1845,7 +1849,12 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, /* XXX: This looks awful. */ tty_unlock(tp); sx_xlock(&proctree_lock); - tty_lock(tp); + error = ttydev_enter(tp); + if (error != 0) { + sx_xunlock(&proctree_lock); + tty_lock(tp); + return (error); + } if (!SESS_LEADER(p)) { /* Only the session leader may do this. */ @@ -1909,7 +1918,12 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, tty_lock(tp); return (EPERM); } - tty_lock(tp); + error = ttydev_enter(tp); + if (error != 0) { + sx_sunlock(&proctree_lock); + tty_lock(tp); + return (error); + } /* * Determine if this TTY is the controlling TTY after diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 3d977cba4..ce6b3b4dc 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -956,11 +956,13 @@ update_rtm_from_info(struct rt_addrinfo *info, struct rt_msghdr **prtm, { struct rt_msghdr *rtm, *orig_rtm = NULL; struct walkarg w; - int len; + int error, len; rtm = *prtm; /* Check if we need to realloc storage */ - rtsock_msg_buffer(rtm->rtm_type, info, NULL, &len); + error = rtsock_msg_buffer(rtm->rtm_type, info, NULL, &len); + if (error != 0) + return (error); if (len > alloc_len) { struct rt_msghdr *tmp_rtm; @@ -980,7 +982,12 @@ update_rtm_from_info(struct rt_addrinfo *info, struct rt_msghdr **prtm, w.w_tmem = (caddr_t)rtm; w.w_tmemsize = alloc_len; - rtsock_msg_buffer(rtm->rtm_type, info, &w, &len); + error = rtsock_msg_buffer(rtm->rtm_type, info, &w, &len); + if (error != 0) { + if (orig_rtm != NULL) + free(rtm, M_TEMP); + return (error); + } rtm->rtm_addrs = info->rti_addrs; if (orig_rtm != NULL) @@ -1877,6 +1884,8 @@ rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int * if ((sa = rtinfo->rti_info[i]) == NULL) continue; + if (sa->sa_len > sizeof(ss)) + return (EINVAL); rtinfo->rti_addrs |= (1 << i); #ifdef COMPAT_FREEBSD32 if (compat32) @@ -1885,8 +1894,6 @@ rtsock_msg_buffer(int type, struct rt_addrinfo *rtinfo, struct walkarg *w, int * #endif dlen = SA_SIZE(sa); if (cp != NULL && buflen >= dlen) { - KASSERT(dlen <= sizeof(ss), - ("%s: sockaddr size overflow", __func__)); bzero(&ss, sizeof(ss)); bcopy(sa, &ss, sa->sa_len); sa = (struct sockaddr *)&ss; diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h index ca26d65d5..9a41e5522 100644 --- a/sys/sys/procdesc.h +++ b/sys/sys/procdesc.h @@ -86,7 +86,6 @@ struct procdesc { * Flags for the pd_flags field. */ #define PDF_CLOSED 0x00000001 /* Descriptor has closed. */ -#define PDF_SELECTED 0x00000002 /* Issue selwakeup(). */ #define PDF_EXITED 0x00000004 /* Process exited. */ #define PDF_DAEMON 0x00000008 /* Don't exit when procdesc closes. */ diff --git a/zfs/module/nvpair/nvpair.c b/zfs/module/nvpair/nvpair.c index 887f7d3..5dcba96 100644 --- a/zfs/module/nvpair/nvpair.c +++ b/zfs/module/nvpair/nvpair.c @@ -134,7 +134,8 @@ #define NVP_SIZE_CALC(name_len, data_len) \ (NV_ALIGN((sizeof (nvpair_t)) + name_len) + NV_ALIGN(data_len)) -static int i_get_value_size(data_type_t type, const void *data, uint_t nelem); +static int i_get_value_size(data_type_t type, const void *data, uint_t nelem, + size_t max_size); static int nvlist_add_common(nvlist_t *nvl, const char *name, data_type_t type, uint_t nelem, const void *data); @@ -809,8 +810,13 @@ i_validate_nvpair(nvpair_t *nvp) * verify nvp_type, nvp_value_elem, and also possibly * verify string values and get the value size. */ - size2 = i_get_value_size(type, NVP_VALUE(nvp), NVP_NELEM(nvp)); size1 = nvp->nvp_size - NVP_VALOFF(nvp); + /* A zero helper limit means unbounded, not an empty string buffer. */ + if (type == DATA_TYPE_STRING && size1 == 0) + return (EFAULT); + size2 = i_get_value_size(type, NVP_VALUE(nvp), NVP_NELEM(nvp), + size1); + if (size2 < 0 || size1 != NV_ALIGN(size2)) return (EFAULT); @@ -1001,12 +1007,21 @@ nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp) * DATA_TYPE_STRING and * DATA_TYPE_STRING_ARRAY * Is data == NULL then the size of the string(s) is excluded. + * + * If 'max_size' is non-zero, then don't look beyond 'max_size' number of + * bytes when calculating a value size. Note that 'max_size' should include + * the NULL terminator byte when calculating string size. If 'max_size' is 0, + * it is ignored. */ static int -i_get_value_size(data_type_t type, const void *data, uint_t nelem) +i_get_value_size(data_type_t type, const void *data, uint_t nelem, + size_t max_size) { uint64_t value_sz; + if (max_size == 0) + max_size = INT32_MAX; + if (i_validate_type_nelem(type, nelem) != 0) return (-1); @@ -1051,10 +1066,15 @@ i_get_value_size(data_type_t type, const void *data, uint_t nelem) break; #endif case DATA_TYPE_STRING: - if (data == NULL) + if (data == NULL) { value_sz = 0; - else - value_sz = strlen(data) + 1; + } else { + value_sz = strnlen(data, max_size); + if (value_sz >= max_size) { + return (-1); /* string not terminated */ + } + value_sz += 1; + } break; case DATA_TYPE_BOOLEAN_ARRAY: value_sz = (uint64_t)nelem * sizeof (boolean_t); @@ -1088,16 +1108,27 @@ i_get_value_size(data_type_t type, const void *data, uint_t nelem) break; case DATA_TYPE_STRING_ARRAY: value_sz = (uint64_t)nelem * sizeof (uint64_t); - if (data != NULL) { char *const *strs = data; uint_t i; + size_t newsize; + + if (value_sz > max_size) + return (-1); + max_size -= value_sz; /* no alignment requirement for strings */ for (i = 0; i < nelem; i++) { if (strs[i] == NULL) return (-1); - value_sz += strlen(strs[i]) + 1; + + newsize = strnlen(strs[i], max_size); + + if (newsize == max_size) + return (-1); /* not terminated */ + + value_sz += newsize + 1; /* +1 for NULL */ + max_size -= newsize + 1; } } break; @@ -1162,7 +1193,7 @@ nvlist_add_common(nvlist_t *nvl, const char *name, * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) included. */ - if ((value_sz = i_get_value_size(type, data, nelem)) < 0) + if ((value_sz = i_get_value_size(type, data, nelem, 0)) < 0) return (EINVAL); if (i_validate_nvpair_value(type, nelem, data) != 0) @@ -1587,7 +1618,7 @@ nvpair_value_common(const nvpair_t *nvp, data_type_t type, uint_t *nelem, #endif if (data == NULL) return (EINVAL); - if ((value_sz = i_get_value_size(type, NULL, 1)) < 0) + if ((value_sz = i_get_value_size(type, NULL, 1, 0)) < 0) return (EINVAL); memcpy(data, NVP_VALUE(nvp), (size_t)value_sz); if (nelem != NULL) @@ -2954,7 +2985,7 @@ nvpair_native_embedded_array(nvstream_t *nvs, nvpair_t *nvp) return (nvs_embedded_nvl_array(nvs, nvp, NULL)); } -static void +static int nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) { switch (nvs->nvs_op) { @@ -2973,15 +3004,22 @@ nvpair_native_string_array(nvstream_t *nvs, nvpair_t *nvp) case NVS_OP_DECODE: { char **strp = (void *)NVP_VALUE(nvp); char *buf = ((char *)strp + NVP_NELEM(nvp) * sizeof (uint64_t)); + size_t remaining = (char *)nvp + nvp->nvp_size - buf; int i; for (i = 0; i < NVP_NELEM(nvp); i++) { + size_t len = strnlen(buf, remaining); + + if (len == remaining) + return (EFAULT); strp[i] = buf; - buf += strlen(buf) + 1; + buf += len + 1; + remaining -= len + 1; } break; } } + return (0); } static int @@ -3017,7 +3055,8 @@ nvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp) * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) excluded. */ - if ((value_sz = i_get_value_size(type, NULL, NVP_NELEM(nvp))) < 0) + if ((value_sz = i_get_value_size(type, NULL, NVP_NELEM(nvp), + NVP_SIZE(nvp))) < 0) return (EFAULT); if (NVP_SIZE_CALC(nvp->nvp_name_sz, value_sz) > nvp->nvp_size) @@ -3031,7 +3070,7 @@ nvs_native_nvp_op(nvstream_t *nvs, nvpair_t *nvp) ret = nvpair_native_embedded_array(nvs, nvp); break; case DATA_TYPE_STRING_ARRAY: - nvpair_native_string_array(nvs, nvp); + ret = nvpair_native_string_array(nvs, nvp); break; default: break; @@ -3332,7 +3371,7 @@ nvs_xdr_nvp_op(nvstream_t *nvs, nvpair_t *nvp) * In case of data types DATA_TYPE_STRING and DATA_TYPE_STRING_ARRAY * is the size of the string(s) excluded. */ - if ((value_sz = i_get_value_size(type, NULL, nelem)) < 0) + if ((value_sz = i_get_value_size(type, NULL, nelem, NVP_SIZE(nvp))) < 0) return (EFAULT); /* if there is no data to extract then return */ diff --git a/zfs/module/zfs/dmu_recv.c b/zfs/module/zfs/dmu_recv.c index 9f1c25f..e9e4315 100644 --- a/zfs/module/zfs/dmu_recv.c +++ b/zfs/module/zfs/dmu_recv.c @@ -2824,16 +2824,20 @@ receive_read_record(dmu_recv_cookie_t *drc) { struct drr_object *drro = &drc->drc_rrd->header.drr_u.drr_object; - uint32_t size = DRR_OBJECT_PAYLOAD_SIZE(drro); + uint32_t size; void *buf = NULL; dmu_object_info_t doi; + size = DRR_OBJECT_PAYLOAD_SIZE(drro); + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + if (size != 0) - buf = kmem_zalloc(size, KM_SLEEP); + buf = vmem_zalloc(size, KM_SLEEP); err = receive_read_payload_and_next_header(drc, size, buf); if (err != 0) { - kmem_free(buf, size); + vmem_free(buf, size); return (err); } err = dmu_object_info(drc->drc_os, drro->drr_object, &doi); @@ -2857,7 +2861,11 @@ receive_read_record(dmu_recv_cookie_t *drc) case DRR_WRITE: { struct drr_write *drrw = &drc->drc_rrd->header.drr_u.drr_write; - int size = DRR_WRITE_PAYLOAD_SIZE(drrw); + uint64_t size = DRR_WRITE_PAYLOAD_SIZE(drrw); + + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + abd_t *abd = abd_alloc_linear(size, B_FALSE); err = receive_read_payload_and_next_header(drc, size, abd_to_buf(abd)); @@ -2874,12 +2882,18 @@ receive_read_record(dmu_recv_cookie_t *drc) { struct drr_write_embedded *drrwe = &drc->drc_rrd->header.drr_u.drr_write_embedded; - uint32_t size = P2ROUNDUP(drrwe->drr_psize, 8); - void *buf = kmem_zalloc(size, KM_SLEEP); + uint32_t size; + void *buf; + + size = P2ROUNDUP(drrwe->drr_psize, 8); + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + + buf = vmem_zalloc(size, KM_SLEEP); err = receive_read_payload_and_next_header(drc, size, buf); if (err != 0) { - kmem_free(buf, size); + vmem_free(buf, size); return (err); } @@ -2908,7 +2922,11 @@ receive_read_record(dmu_recv_cookie_t *drc) case DRR_SPILL: { struct drr_spill *drrs = &drc->drc_rrd->header.drr_u.drr_spill; - int size = DRR_SPILL_PAYLOAD_SIZE(drrs); + uint64_t size = DRR_SPILL_PAYLOAD_SIZE(drrs); + + if (size > SPA_MAXBLOCKSIZE) + return (SET_ERROR(ERANGE)); + abd_t *abd = abd_alloc_linear(size, B_FALSE); err = receive_read_payload_and_next_header(drc, size, abd_to_buf(abd)); @@ -3059,7 +3077,7 @@ receive_process_record(struct receive_writer_arg *rwa, abd_free(rrd->abd); rrd->abd = NULL; } else if (rrd->payload != NULL) { - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; } return (0); @@ -3073,7 +3091,7 @@ receive_process_record(struct receive_writer_arg *rwa, rrd->abd = NULL; rrd->payload = NULL; } else if (rrd->payload != NULL) { - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; } @@ -3086,7 +3104,7 @@ receive_process_record(struct receive_writer_arg *rwa, { struct drr_object *drro = &rrd->header.drr_u.drr_object; err = receive_object(rwa, drro, rrd->payload); - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; break; } @@ -3124,7 +3142,7 @@ receive_process_record(struct receive_writer_arg *rwa, struct drr_write_embedded *drrwe = &rrd->header.drr_u.drr_write_embedded; err = receive_write_embedded(rwa, drrwe, rrd->payload); - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; break; } @@ -3193,7 +3211,7 @@ receive_writer_thread(void *arg) rrd->abd = NULL; rrd->payload = NULL; } else if (rrd->payload != NULL) { - kmem_free(rrd->payload, rrd->payload_size); + vmem_free(rrd->payload, rrd->payload_size); rrd->payload = NULL; } /* diff --git a/zfs/module/zfs/vdev_label.c b/zfs/module/zfs/vdev_label.c index 5c0e750..85d3619 100644 --- a/zfs/module/zfs/vdev_label.c +++ b/zfs/module/zfs/vdev_label.c @@ -1360,6 +1360,7 @@ vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv) VB_NVLIST); break; } + vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0'; fnvlist_add_string(bootenv, FREEBSD_BOOTONCE, buf); } diff --git a/zfs/module/zfs/zfs_ioctl.c b/zfs/module/zfs/zfs_ioctl.c index eee6ad6..c0cc06c 100644 --- a/zfs/module/zfs/zfs_ioctl.c +++ b/zfs/module/zfs/zfs_ioctl.c @@ -913,6 +913,23 @@ zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) ZFS_DELEG_PERM_CREATE, cr)); } +/* + * Policy for dataset set property operations. Individual properties checked by + * zfs_check_settable(), additionally require zfs_secpolicy_recv() when setting + * properties as part of a receive. + */ +static int +zfs_secpolicy_setprops(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) +{ + boolean_t received = zc->zc_cookie; + int error; + + if (received && (error = zfs_secpolicy_recv(zc, innvl, cr))) + return (error); + + return (zfs_secpolicy_read(zc, innvl, cr)); +} + int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr) { @@ -3658,7 +3675,6 @@ static int zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) { (void) unused, (void) outnvl; - const char *message; char *poolname; spa_t *spa; int error; @@ -3679,7 +3695,7 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) if (error != 0) return (error); - message = fnvlist_lookup_string(innvl, "message"); + const char *message = fnvlist_lookup_string(innvl, "message"); if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { spa_close(spa, FTAG); @@ -6010,21 +6026,27 @@ zfs_ioc_userspace_one(zfs_cmd_t *zc) * outputs: * zc_nvlist_dst[_size] data buffer (array of zfs_useracct_t) * zc_cookie zap cursor + * + * The zc_nvlist_dst output array is limited to 1000 entries. */ static int zfs_ioc_userspace_many(zfs_cmd_t *zc) { + const size_t batch_limit = 1000 * sizeof (zfs_useracct_t); + uint64_t bufsize = MIN(zc->zc_nvlist_dst_size, batch_limit); zfsvfs_t *zfsvfs; - int bufsize = zc->zc_nvlist_dst_size; - if (bufsize <= 0) + if (bufsize < sizeof (zfs_useracct_t)) { + zc->zc_nvlist_dst_size = sizeof (zfs_useracct_t); return (SET_ERROR(ENOMEM)); + } int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE); if (error != 0) return (error); void *buf = vmem_alloc(bufsize, KM_SLEEP); + zc->zc_nvlist_dst_size = bufsize; error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie, buf, &zc->zc_nvlist_dst_size); @@ -6515,7 +6537,7 @@ zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) dsl_pool_t *dp; dsl_dataset_t *new, *old; const char *firstsnap; - uint64_t used, comp, uncomp; + uint64_t used = 0, comp = 0, uncomp = 0; firstsnap = fnvlist_lookup_string(innvl, "firstsnap"); @@ -7390,7 +7412,7 @@ zfs_ioctl_init(void) zfs_ioc_send, zfs_secpolicy_send); zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, - zfs_secpolicy_none); + zfs_secpolicy_setprops); zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, zfs_secpolicy_destroy); zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename, diff --git a/zfs/module/zfs/zfs_quota.c b/zfs/module/zfs/zfs_quota.c index 9b351ee..c5ffd60 100644 --- a/zfs/module/zfs/zfs_quota.c +++ b/zfs/module/zfs/zfs_quota.c @@ -85,10 +85,14 @@ zpl_get_file_info(dmu_object_type_t bonustype, const void *data, sa.sa_layout_info = BSWAP_16(sa.sa_layout_info); swap = B_TRUE; } - VERIFY3U(sa.sa_magic, ==, SA_MAGIC); + + if (unlikely(sa.sa_magic != SA_MAGIC)) + return (SET_ERROR(EINVAL)); int hdrsize = sa_hdrsize(&sa); - VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t)); + + if (unlikely(hdrsize < sizeof (sa_hdr_phys_t))) + return (SET_ERROR(EINVAL)); uintptr_t data_after_hdr = (uintptr_t)data + hdrsize; zoi->zfi_user = *((uint64_t *)(data_after_hdr + SA_UID_OFFSET));