Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 2 Feb 2016 15:23:22 +0200
From:      Konstantin Belousov <kib@freebsd.org>
To:        Mateusz Guzik <mjguzik@gmail.com>
Cc:        freebsd-hackers@freebsd.org, Mateusz Guzik <mjg@freebsd.org>
Subject:   Re: [PATCH 2/2] fork: plug a use after free of the returned process pointer
Message-ID:  <20160202132322.GU91220@kib.kiev.ua>
In-Reply-To: <1454386069-29657-3-git-send-email-mjguzik@gmail.com>
References:  <20160201103632.GL91220@kib.kiev.ua> <1454386069-29657-1-git-send-email-mjguzik@gmail.com> <1454386069-29657-3-git-send-email-mjguzik@gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--YToU2i3Vx8H2dn7O
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Tue, Feb 02, 2016 at 05:07:49AM +0100, Mateusz Guzik wrote:
> From: Mateusz Guzik <mjg@freebsd.org>
>=20
> fork1 required its callers to pass a pointer to struct proc * which would
> be set to the new process (if any). procdesc and racct manipulation also
> used said pointer.
>=20
> However, the process could have exited prior to do_fork return and be
> automatically reaped, thus making this a use-after-free.
>=20
> Fix the problem by letting callers indicate whether they want the pid or
> the struct proc, return the process in stopped state for the latter case.
Patch looks fine, some style notes and one question is below.

> ---
>  sys/compat/cloudabi/cloudabi_proc.c |   2 -
>  sys/kern/kern_fork.c                | 104 +++++++++++++++++++-----------=
------
>  sys/kern/kern_racct.c               |   3 +-
>  sys/sys/proc.h                      |   1 +
>  4 files changed, 58 insertions(+), 52 deletions(-)
>=20
> diff --git a/sys/compat/cloudabi/cloudabi_proc.c b/sys/compat/cloudabi/cl=
oudabi_proc.c
> index 010efca..2bc50ca 100644
> --- a/sys/compat/cloudabi/cloudabi_proc.c
> +++ b/sys/compat/cloudabi/cloudabi_proc.c
> @@ -77,12 +77,10 @@ cloudabi_sys_proc_fork(struct thread *td,
>  {
>  	struct fork_req fr =3D {};
>  	struct filecaps fcaps =3D {};
> -	struct proc *p2;
>  	int error, fd;
> =20
>  	cap_rights_init(&fcaps.fc_rights, CAP_FSTAT, CAP_EVENT);
>  	fr.fr_flags =3D RFFDG | RFPROC | RFPROCDESC;
> -	fr.fr_procp =3D &p2;
>  	fr.fr_pd_fd =3D &fd;
>  	fr.fr_pd_fcaps =3D &fcaps;
>  	error =3D fork1(td, &fr);
> diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
> index 3b51b7f..d0c3837 100644
> --- a/sys/kern/kern_fork.c
> +++ b/sys/kern/kern_fork.c
> @@ -102,14 +102,13 @@ int
>  sys_fork(struct thread *td, struct fork_args *uap)
>  {
>  	struct fork_req fr =3D {};
> -	int error;
> -	struct proc *p2;
> +	int error, pid;
> =20
>  	fr.fr_flags =3D RFFDG | RFPROC;
> -	fr.fr_procp =3D &p2;
> +	fr.fr_pidp =3D &pid;
>  	error =3D fork1(td, &fr);
>  	if (error =3D=3D 0) {
> -		td->td_retval[0] =3D p2->p_pid;
> +		td->td_retval[0] =3D pid;
>  		td->td_retval[1] =3D 0;
>  	}
>  	return (error);
> @@ -122,11 +121,10 @@ sys_pdfork(td, uap)
>  	struct pdfork_args *uap;
>  {
>  	struct fork_req fr =3D {};
> -	int error, fd;
> -	struct proc *p2;
> +	int error, fd, pid;
> =20
>  	fr.fr_flags =3D RFFDG | RFPROC | RFPROCDESC;
> -	fr.fr_procp =3D &p2;
> +	fr.fr_pidp =3D &pid;
>  	fr.fr_pd_fd =3D &fd;
>  	fr.fr_pd_flags =3D uap->flags;
>  	/*
> @@ -136,7 +134,7 @@ sys_pdfork(td, uap)
>  	 */
>  	error =3D fork1(td, &fr);
>  	if (error =3D=3D 0) {
> -		td->td_retval[0] =3D p2->p_pid;
> +		td->td_retval[0] =3D pid;
>  		td->td_retval[1] =3D 0;
>  		error =3D copyout(&fd, uap->fdp, sizeof(fd));
>  	}
> @@ -148,14 +146,13 @@ int
>  sys_vfork(struct thread *td, struct vfork_args *uap)
>  {
>  	struct fork_req fr =3D {};
> -	int error;
> -	struct proc *p2;
> +	int error, pid;
> =20
>  	fr.fr_flags =3D RFFDG | RFPROC | RFPPWAIT | RFMEM;
> -	fr.fr_procp =3D &p2;
> +	fr.fr_pidp =3D &pid;
>  	error =3D fork1(td, &fr);
>  	if (error =3D=3D 0) {
> -		td->td_retval[0] =3D p2->p_pid;
> +		td->td_retval[0] =3D pid;
>  		td->td_retval[1] =3D 0;
>  	}
>  	return (error);
> @@ -165,8 +162,7 @@ int
>  sys_rfork(struct thread *td, struct rfork_args *uap)
>  {
>  	struct fork_req fr =3D {};
> -	struct proc *p2;
> -	int error;
> +	int error, pid;
> =20
>  	/* Don't allow kernel-only flags. */
>  	if ((uap->flags & RFKERNELONLY) !=3D 0)
> @@ -174,10 +170,10 @@ sys_rfork(struct thread *td, struct rfork_args *uap)
> =20
>  	AUDIT_ARG_FFLAGS(uap->flags);
>  	fr.fr_flags =3D uap->flags;
> -	fr.fr_procp =3D &p2;
> +	fr.fr_pidp =3D &pid;
>  	error =3D fork1(td, &fr);
>  	if (error =3D=3D 0) {
> -		td->td_retval[0] =3D p2 ? p2->p_pid : 0;
> +		td->td_retval[0] =3D pid;
>  		td->td_retval[1] =3D 0;
>  	}
>  	return (error);
> @@ -378,20 +374,21 @@ fail:
>  }
> =20
>  static void
> -do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td=
2,
> -    struct vmspace *vm2, int pdflags)
> +do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct =
thread *td2,
> +    struct vmspace *vm2, struct file *fp_procdesc)
Style, line is too long.

>  {
>  	struct proc *p1, *pptr;
> -	int p2_held, trypid;
> +	int trypid;
>  	struct filedesc *fd;
>  	struct filedesc_to_leader *fdtol;
>  	struct sigacts *newsigacts;
> +	int flags;
> =20
>  	sx_assert(&proctree_lock, SX_SLOCKED);
>  	sx_assert(&allproc_lock, SX_XLOCKED);
> =20
> -	p2_held =3D 0;
>  	p1 =3D td->td_proc;
> +	flags =3D fr->fr_flags;
Why not use fr->fr_flags directly ?  It is slightly more churn, but IMO
it is worth it.

> =20
>  	trypid =3D fork_findpid(flags);
> =20
> @@ -690,7 +687,7 @@ do_fork(struct thread *td, int flags, struct proc *p2=
, struct thread *td2,
>  	 * However, don't do this until after fork(2) can no longer fail.
>  	 */
>  	if (flags & RFPROCDESC)
> -		procdesc_new(p2, pdflags);
> +		procdesc_new(p2, fr->fr_pd_flags);
> =20
>  	/*
>  	 * Both processes are set up, now check if any loadable modules want
> @@ -718,6 +715,11 @@ do_fork(struct thread *td, int flags, struct proc *p=
2, struct thread *td2,
>  	if ((flags & RFMEM) =3D=3D 0 && dtrace_fasttrap_fork)
>  		dtrace_fasttrap_fork(p1, p2);
>  #endif
> +	/*
> +	 * Hold the process so that it cannot exit after we make it runnable,
> +	 * but before we wait for the debugger.
Is this possible ?  The forked child must execute through fork_return(),
and there we do ptracestop() before the child has a chance to ever return
to usermode.

Do you mean a scenario where the debugger detaches before child executes
fork_return() and TDP_STOPATFORK is cleared in advance ?

> +	 */
> +	_PHOLD(p2);
>  	if ((p1->p_flag & (P_TRACED | P_FOLLOWFORK)) =3D=3D (P_TRACED |
>  	    P_FOLLOWFORK)) {
>  		/*
> @@ -730,24 +732,12 @@ do_fork(struct thread *td, int flags, struct proc *=
p2, struct thread *td2,
>  		td->td_dbgflags |=3D TDB_FORK;
>  		td->td_dbg_forked =3D p2->p_pid;
>  		td2->td_dbgflags |=3D TDB_STOPATFORK;
> -		_PHOLD(p2);
> -		p2_held =3D 1;
>  	}
>  	if (flags & RFPPWAIT) {
>  		td->td_pflags |=3D TDP_RFPPWAIT;
>  		td->td_rfppwait_p =3D p2;
>  	}
>  	PROC_UNLOCK(p2);
> -	if ((flags & RFSTOPPED) =3D=3D 0) {
> -		/*
> -		 * If RFSTOPPED not requested, make child runnable and
> -		 * add to run queue.
> -		 */
> -		thread_lock(td2);
> -		TD_SET_CAN_RUN(td2);
> -		sched_add(td2, SRQ_BORING);
> -		thread_unlock(td2);
> -	}
> =20
>  	/*
>  	 * Now can be swapped.
> @@ -761,14 +751,34 @@ do_fork(struct thread *td, int flags, struct proc *=
p2, struct thread *td2,
>  	knote_fork(&p1->p_klist, p2->p_pid);
>  	SDT_PROBE3(proc, , , create, p2, p1, flags);
> =20
> +	if (flags & RFPROCDESC) {
> +		procdesc_finit(p2->p_procdesc, fp_procdesc);
> +		fdrop(fp_procdesc, td);
> +	}
> +
> +	if ((flags & RFSTOPPED) =3D=3D 0) {
> +		/*
> +		 * If RFSTOPPED not requested, make child runnable and
> +		 * add to run queue.
> +		 */
> +		thread_lock(td2);
> +		TD_SET_CAN_RUN(td2);
> +		sched_add(td2, SRQ_BORING);
> +		thread_unlock(td2);
> +		if (fr->fr_pidp !=3D NULL)
> +			*fr->fr_pidp =3D p2->p_pid;
> +	} else {
> +		*fr->fr_procp =3D p2;
> +	}
> +
> +	PROC_LOCK(p2);
>  	/*
>  	 * Wait until debugger is attached to child.
>  	 */
> -	PROC_LOCK(p2);
>  	while ((td2->td_dbgflags & TDB_STOPATFORK) !=3D 0)
>  		cv_wait(&p2->p_dbgwait, &p2->p_mtx);
> -	if (p2_held)
> -		_PRELE(p2);
> +	_PRELE(p2);
> +	racct_proc_fork_done(p2);
>  	PROC_UNLOCK(p2);
>  }
> =20
> @@ -788,6 +798,11 @@ fork1(struct thread *td, struct fork_req *fr)
>  	flags =3D fr->fr_flags;
>  	pages =3D fr->fr_pages;
> =20
> +	if ((flags & RFSTOPPED) !=3D 0)
> +		MPASS(fr->fr_procp !=3D NULL && fr->fr_pidp =3D=3D NULL);
> +	else
> +		MPASS(fr->fr_procp =3D=3D NULL);
> +
>  	/* Check for the undefined or unimplemented flags. */
>  	if ((flags & ~(RFFLAGS | RFTSIGFLAGS(RFTSIGMASK))) !=3D 0)
>  		return (EINVAL);
> @@ -821,7 +836,10 @@ fork1(struct thread *td, struct fork_req *fr)
>  	 * certain parts of a process from itself.
>  	 */
>  	if ((flags & RFPROC) =3D=3D 0) {
> -		*fr->fr_procp =3D NULL;
> +		if (fr->fr_procp !=3D NULL)
> +			*fr->fr_procp =3D NULL;
> +		else if (fr->fr_pidp !=3D NULL)
> +			*fr->fr_pidp =3D 0;
>  		return (fork_norfproc(td, flags));
>  	}
> =20
> @@ -949,17 +967,7 @@ fork1(struct thread *td, struct fork_req *fr)
>  		    lim_cur(td, RLIMIT_NPROC));
>  	}
>  	if (ok) {
> -		do_fork(td, flags, newproc, td2, vm2, fr->fr_pd_flags);
> -
> -		/*
> -		 * Return child proc pointer to parent.
> -		 */
> -		*fr->fr_procp =3D newproc;
> -		if (flags & RFPROCDESC) {
> -			procdesc_finit(newproc->p_procdesc, fp_procdesc);
> -			fdrop(fp_procdesc, td);
> -		}
> -		racct_proc_fork_done(newproc);
> +		do_fork(td, fr, newproc, td2, vm2, fp_procdesc);
>  		return (0);
>  	}
> =20
> diff --git a/sys/kern/kern_racct.c b/sys/kern/kern_racct.c
> index 0c7c0c4..ce7e2a4 100644
> --- a/sys/kern/kern_racct.c
> +++ b/sys/kern/kern_racct.c
> @@ -957,16 +957,15 @@ void
>  racct_proc_fork_done(struct proc *child)
>  {
> =20
> +	PROC_LOCK_ASSERT(child, MA_OWNED);
>  #ifdef RCTL
>  	if (!racct_enable)
>  		return;
> =20
> -	PROC_LOCK(child);
>  	mtx_lock(&racct_lock);
>  	rctl_enforce(child, RACCT_NPROC, 0);
>  	rctl_enforce(child, RACCT_NTHR, 0);
>  	mtx_unlock(&racct_lock);
> -	PROC_UNLOCK(child);
>  #endif
>  }
> =20
> diff --git a/sys/sys/proc.h b/sys/sys/proc.h
> index ac96566..039fd39 100644
> --- a/sys/sys/proc.h
> +++ b/sys/sys/proc.h
> @@ -910,6 +910,7 @@ struct	proc *zpfind(pid_t);		/* Find zombie process b=
y id. */
>  struct	fork_req {
>  	int		fr_flags;
>  	int		fr_pages;
> +	int 		*fr_pidp;
>  	struct proc 	**fr_procp;
>  	int 		*fr_pd_fd;
>  	int 		fr_pd_flags;
> --=20
> 2.7.0

--YToU2i3Vx8H2dn7O
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJWsK3KAAoJEJDCuSvBvK1Bs2cP/2+e9oBdarmZxy4bGjzF0xbY
CHWjs3Hpt5x/vrMxMXzEwB0ggEK2vA3HBbtvjLn5mDUX0UBode3OnUh2aGBqqwRG
XdS9fQdbjIxl0qA5GdEp27jxvugDi2JxZ7Xom4JErS/bKJBeHKcutBo89n3KEMCA
a6OEivYcdN8qJPd5TfOYDZVJDUHf98IwhZgKZ0O94toZybIWEFLnMZJBlHG0Y92V
hKe469h3R6uK2SPpHKYZ8bkbrUCubqm+ROB404sRBiuahTYv6Iea726v7kEU5yjC
nxPp+Jhly++lWCtVqh9ymZXGP1bIaZV+Qv/IsvtExDgFL/NSK6rvQQ52cRrxoaS4
CILlISeuV+2KjQh3Z9ceKyqn1hhO3xyb7dv56y+ASI0a0tT5hMHZYYZmPVR0uR3J
QYEz3jR9VK9JyCrDRLY3Ny/JtyawJfKl92Od98OdLMRrh6UePdCNy1D2/wWUR7Gt
KVWbTGhhy9UDp/yfQVMrdXc1GUrtL9CfpN/aGiJboEOMAaHEc81hLzDPMIRr5sGS
vSX6T5b2LOmRUEqzTWqys3h1UIpWN2Bs9tYGZv1C1yIOXmZWlhv2pgc5xoRfC2YK
0LXK9n1onsX6EgJzyhfrmw6JBpW2OXIyXQZI8Y6a2RorKp4CemGQdWCfvoE1ITQB
FOUAUKHyO0Y5hdEIBIKV
=vt4+
-----END PGP SIGNATURE-----

--YToU2i3Vx8H2dn7O--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20160202132322.GU91220>