DF-2823 / fix.diff
--- a/sys/kern/kern_lockf.c +++ b/sys/kern/kern_lockf.c @@ -91,7 +91,7 @@ static int lf_getlock(struct flock *, struct lockf *, struct proc *, int, int, off_t, off_t); -static int lf_count_change(struct proc *, int); +static int lf_count_change(struct proc *, int, int); /* * Return TRUE (non-zero) if the type and posix flags match. @@ -153,8 +153,17 @@ } } +/* + * Change the POSIX lock accounting for the given process. + * + * enforce - when non-zero, enforce the RLIMIT_POSIXLOCKS / + * kern.maxposixlocksperuid limit for positive diffs. Callers servicing + * an F_UNLCK must pass 0: POSIX.1-2008 2.9.7 requires releasing one's + * own locks to succeed; a clipping unlock may transiently charge +1 + * headroom (a split), which is settled at the end of lf_setlock(). + */ static int -lf_count_change(struct proc *owner, int diff) +lf_count_change(struct proc *owner, int diff, int enforce) { struct uidinfo *uip; int max, ret; @@ -168,7 +177,7 @@ max = MIN(owner->p_rlimit[RLIMIT_POSIXLOCKS].rlim_cur, maxposixlocksperuid); - if (diff > 0 && owner->p_ucred->cr_uid != 0 && max != -1 && + if (enforce && diff > 0 && owner->p_ucred->cr_uid != 0 && max != -1 && uip->ui_posixlocks >= max ) { ret = 1; } else { @@ -457,7 +466,7 @@ if (type == F_UNLCK) goto do_wakeup; if (flags & F_POSIX) { - if (lf_count_change(owner, 1)) { + if (lf_count_change(owner, 1, 1)) { error = ENOLCK; goto do_cleanup; } @@ -517,7 +526,8 @@ else count = -1; } - if (count && lf_count_change(owner, -count)) { + if (count && + lf_count_change(owner, -count, type != F_UNLCK)) { error = ENOLCK; goto do_cleanup; } @@ -748,7 +758,7 @@ KKASSERT(count <= 0); if (count < 0) - lf_count_change(owner, count); + lf_count_change(owner, count, 0); do_wakeup: lf_print_lock(lock); if (wakeup_needed) |