aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libprocstat/msdosfs.c1
-rw-r--r--lib/libprocstat/smbfs.c2
-rw-r--r--lib/libprocstat/udf.c2
-rw-r--r--lib/libprocstat/zfs.c1
-rw-r--r--sys/fs/unionfs/union_subr.c6
-rw-r--r--sys/kern/kern_exit.c10
-rw-r--r--sys/kern/kern_fork.c2
-rw-r--r--sys/kern/kern_prot.c81
-rw-r--r--sys/sys/ucred.h6
9 files changed, 78 insertions, 33 deletions
diff --git a/lib/libprocstat/msdosfs.c b/lib/libprocstat/msdosfs.c
index 2af34f856e50..7e73360757bc 100644
--- a/lib/libprocstat/msdosfs.c
+++ b/lib/libprocstat/msdosfs.c
@@ -35,6 +35,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <stdbool.h>
#include <sys/cdefs.h>
#include <sys/param.h>
diff --git a/lib/libprocstat/smbfs.c b/lib/libprocstat/smbfs.c
index f705ccd207dd..854fd3eb986c 100644
--- a/lib/libprocstat/smbfs.c
+++ b/lib/libprocstat/smbfs.c
@@ -25,6 +25,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <stdbool.h>
+
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
diff --git a/lib/libprocstat/udf.c b/lib/libprocstat/udf.c
index 43e79c39a62d..dc6cadeeea6d 100644
--- a/lib/libprocstat/udf.c
+++ b/lib/libprocstat/udf.c
@@ -25,6 +25,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <stdbool.h>
+
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
diff --git a/lib/libprocstat/zfs.c b/lib/libprocstat/zfs.c
index 9ca43d6f6331..a84083a054db 100644
--- a/lib/libprocstat/zfs.c
+++ b/lib/libprocstat/zfs.c
@@ -25,6 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <stdbool.h>
#include <sys/param.h>
#define _KERNEL
diff --git a/sys/fs/unionfs/union_subr.c b/sys/fs/unionfs/union_subr.c
index 22c8ffe88bde..56c16fc9ed6e 100644
--- a/sys/fs/unionfs/union_subr.c
+++ b/sys/fs/unionfs/union_subr.c
@@ -775,11 +775,6 @@ unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
/* Authority change to root */
rootinfo = uifind((uid_t)0);
cred = crdup(cnp->cn_cred);
- /*
- * The calls to chgproccnt() are needed to compensate for change_ruid()
- * calling chgproccnt().
- */
- chgproccnt(cred->cr_ruidinfo, 1, 0);
change_euid(cred, rootinfo);
change_ruid(cred, rootinfo);
change_svuid(cred, (uid_t)0);
@@ -831,7 +826,6 @@ unionfs_mkshadowdir_free_out:
unionfs_mkshadowdir_abort:
cnp->cn_cred = credbk;
- chgproccnt(cred->cr_ruidinfo, -1, 0);
crfree(cred);
return (error);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index bd820aae8197..64fef905daa4 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -977,11 +977,6 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options)
PROC_UNLOCK(q);
/*
- * Decrement the count of procs running with this uid.
- */
- (void)chgproccnt(p->p_ucred->cr_ruidinfo, -1, 0);
-
- /*
* Destroy resource accounting information associated with the process.
*/
#ifdef RACCT
@@ -994,9 +989,10 @@ proc_reap(struct thread *td, struct proc *p, int *status, int options)
racct_proc_exit(p);
/*
- * Free credentials, arguments, and sigacts.
+ * Free credentials, arguments, and sigacts, and decrement the count of
+ * processes running with this uid.
*/
- proc_unset_cred(p);
+ proc_unset_cred(p, true);
pargs_drop(p->p_args);
p->p_args = NULL;
sigacts_free(p->p_sigacts);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 413453f9bf8f..29b3e5d8d682 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -1087,7 +1087,7 @@ fail0:
#endif
racct_proc_exit(newproc);
fail1:
- proc_unset_cred(newproc);
+ proc_unset_cred(newproc, false);
fail2:
if (vm2 != NULL)
vmspace_free(vm2);
diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c
index 755bdca4fd73..655ee776b6a7 100644
--- a/sys/kern/kern_prot.c
+++ b/sys/kern/kern_prot.c
@@ -564,7 +564,7 @@ sys_setuid(struct thread *td, struct setuid_args *uap)
#endif
{
/*
- * Set the real uid and transfer proc count to new user.
+ * Set the real uid.
*/
if (uid != oldcred->cr_ruid) {
change_ruid(newcred, uip);
@@ -590,6 +590,9 @@ sys_setuid(struct thread *td, struct setuid_args *uap)
change_euid(newcred, uip);
setsugid(p);
}
+ /*
+ * This also transfers the proc count to the new user.
+ */
proc_set_cred(p, newcred);
#ifdef RACCT
racct_proc_ucred_changed(p, oldcred, newcred);
@@ -2276,31 +2279,76 @@ cru2xt(struct thread *td, struct xucred *xcr)
/*
* Change process credentials.
+ *
* Callers are responsible for providing the reference for passed credentials
- * and for freeing old ones.
+ * and for freeing old ones. Calls chgproccnt() to correctly account the
+ * current process to the proper real UID, if the latter has changed. Returns
+ * whether the operation was successful. Failure can happen only on
+ * 'enforce_proc_lim' being true and if no new process can be accounted to the
+ * new real UID because of the current limit (see the inner comment for more
+ * details) and the caller does not have privilege (PRIV_PROC_LIMIT) to override
+ * that.
*/
-void
-proc_set_cred(struct proc *p, struct ucred *newcred)
+static bool
+_proc_set_cred(struct proc *p, struct ucred *newcred, bool enforce_proc_lim)
{
- struct ucred *cr;
+ struct ucred *const oldcred = p->p_ucred;
- cr = p->p_ucred;
- MPASS(cr != NULL);
+ MPASS(oldcred != NULL);
PROC_LOCK_ASSERT(p, MA_OWNED);
KASSERT(newcred->cr_users == 0, ("%s: users %d not 0 on cred %p",
__func__, newcred->cr_users, newcred));
- mtx_lock(&cr->cr_mtx);
- KASSERT(cr->cr_users > 0, ("%s: users %d not > 0 on cred %p",
- __func__, cr->cr_users, cr));
- cr->cr_users--;
- mtx_unlock(&cr->cr_mtx);
+ KASSERT(newcred->cr_ref == 1, ("%s: ref %ld not 1 on cred %p",
+ __func__, newcred->cr_ref, newcred));
+
+ if (newcred->cr_ruidinfo != oldcred->cr_ruidinfo) {
+ /*
+ * XXXOC: This check is flawed but nonetheless the best we can
+ * currently do as we don't really track limits per UID contrary
+ * to what we pretend in setrlimit(2). Until this is reworked,
+ * we just check here that the number of processes for our new
+ * real UID doesn't exceed this process' process number limit
+ * (which is meant to be associated with the current real UID).
+ */
+ const int proccnt_changed = chgproccnt(newcred->cr_ruidinfo, 1,
+ enforce_proc_lim ? lim_cur_proc(p, RLIMIT_NPROC) : 0);
+
+ if (!proccnt_changed) {
+ if (priv_check_cred(oldcred, PRIV_PROC_LIMIT) != 0)
+ return (false);
+ (void)chgproccnt(newcred->cr_ruidinfo, 1, 0);
+ }
+ }
+
+ mtx_lock(&oldcred->cr_mtx);
+ KASSERT(oldcred->cr_users > 0, ("%s: users %d not > 0 on cred %p",
+ __func__, oldcred->cr_users, oldcred));
+ oldcred->cr_users--;
+ mtx_unlock(&oldcred->cr_mtx);
p->p_ucred = newcred;
newcred->cr_users = 1;
PROC_UPDATE_COW(p);
+ if (newcred->cr_ruidinfo != oldcred->cr_ruidinfo)
+ (void)chgproccnt(oldcred->cr_ruidinfo, -1, 0);
+ return (true);
+}
+
+void
+proc_set_cred(struct proc *p, struct ucred *newcred)
+{
+ bool success = _proc_set_cred(p, newcred, false);
+
+ MPASS(success);
+}
+
+bool
+proc_set_cred_enforce_proc_lim(struct proc *p, struct ucred *newcred)
+{
+ return (_proc_set_cred(p, newcred, true));
}
void
-proc_unset_cred(struct proc *p)
+proc_unset_cred(struct proc *p, bool decrement_proc_count)
{
struct ucred *cr;
@@ -2315,6 +2363,8 @@ proc_unset_cred(struct proc *p)
KASSERT(cr->cr_ref > 0, ("%s: ref %d not > 0 on cred %p",
__func__, cr->cr_ref, cr));
mtx_unlock(&cr->cr_mtx);
+ if (decrement_proc_count)
+ (void)chgproccnt(cr->cr_ruidinfo, -1, 0);
crfree(cr);
}
@@ -2599,8 +2649,7 @@ change_egid(struct ucred *newcred, gid_t egid)
/*-
* Change a process's real uid.
* Side effects: newcred->cr_ruid will be updated, newcred->cr_ruidinfo
- * will be updated, and the old and new cr_ruidinfo proc
- * counts will be updated.
+ * will be updated.
* References: newcred must be an exclusive credential reference for the
* duration of the call.
*/
@@ -2608,12 +2657,10 @@ void
change_ruid(struct ucred *newcred, struct uidinfo *ruip)
{
- (void)chgproccnt(newcred->cr_ruidinfo, -1, 0);
newcred->cr_ruid = ruip->ui_uid;
uihold(ruip);
uifree(newcred->cr_ruidinfo);
newcred->cr_ruidinfo = ruip;
- (void)chgproccnt(newcred->cr_ruidinfo, 1, 0);
}
/*-
diff --git a/sys/sys/ucred.h b/sys/sys/ucred.h
index f5e2757f7417..d8729b49c2dd 100644
--- a/sys/sys/ucred.h
+++ b/sys/sys/ucred.h
@@ -34,6 +34,7 @@
#ifndef _SYS_UCRED_H_
#define _SYS_UCRED_H_
+#include <sys/types.h>
#if defined(_KERNEL) || defined(_WANT_UCRED)
#include <sys/_lock.h>
#include <sys/_mutex.h>
@@ -158,8 +159,9 @@ void crcopy(struct ucred *dest, struct ucred *src);
struct ucred *crcopysafe(struct proc *p, struct ucred *cr);
struct ucred *crdup(struct ucred *cr);
void crextend(struct ucred *cr, int n);
-void proc_set_cred(struct proc *p, struct ucred *cr);
-void proc_unset_cred(struct proc *p);
+void proc_set_cred(struct proc *p, struct ucred *newcred);
+bool proc_set_cred_enforce_proc_lim(struct proc *p, struct ucred *newcred);
+void proc_unset_cred(struct proc *p, bool decrement_proc_count);
void crfree(struct ucred *cr);
struct ucred *crcowsync(void);
struct ucred *crget(void);