Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 9 Apr 2014 13:16:54 +0200
From:      Mateusz Guzik <mjguzik@gmail.com>
To:        Konstantin Belousov <kostikbel@gmail.com>
Cc:        freebsd-hackers@freebsd.org, Eduardo Morras <emorrasg@yahoo.es>
Subject:   Re: pipe() resource exhaustion
Message-ID:  <20140409111654.GA17650@dft-labs.eu>
In-Reply-To: <20140408132442.GZ21331@kib.kiev.ua>
References:  <lhu0jv$r6n$1@ger.gmane.org> <ab57e60fcc1c1438fcca500e3c594d35@mail.feld.me> <20140408130206.e75f3bf6c6df28b6e4839e70@yahoo.es> <20140408121222.GB30326@dft-labs.eu> <20140408123827.GW21331@kib.kiev.ua> <20140408130727.GA11363@dft-labs.eu> <20140408132442.GZ21331@kib.kiev.ua>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, Apr 08, 2014 at 04:24:42PM +0300, Konstantin Belousov wrote:
> On Tue, Apr 08, 2014 at 03:07:27PM +0200, Mateusz Guzik wrote:
> > On Tue, Apr 08, 2014 at 03:38:27PM +0300, Konstantin Belousov wrote:
> > > On Tue, Apr 08, 2014 at 02:12:22PM +0200, Mateusz Guzik wrote:
> > > > That said, supporting a reserve for this one sounds like a good idea and
> > > > not that hard to implement - one can either play with atomics and double
> > > > check or just place a mutex-protected check in pipespace_new (before
> > > > vm_map_find).
> > > > 
> > > ...
> > > 
> > > I think more reasonable behaviour there is to just fall back to the
> > > buffered pipe if the direct buffer allocation fails. Look at the
> > > pipespace_new() calls in the pipe_create(); probably ignoring the error
> > > would do the trick.
> > 
> > Yeah, should have checked the caller.
> > 
> > Interesting though how the error was made fatal in thiscase.
> > 
> > Anyhow, the following hack following your suggestion  indeed makes the
> > issue go away for me:
> > 
> > diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
> > index 6ba52e3..5930cf2 100644
> > --- a/sys/kern/sys_pipe.c
> > +++ b/sys/kern/sys_pipe.c
> > @@ -647,19 +647,21 @@ pipe_create(pipe, backing)
> >  	struct pipe *pipe;
> >  	int backing;
> >  {
> > -	int error;
> >  
> >  	if (backing) {
> > +		/*
> > +		 * Note that these functions can fail, but we ignore
> > +		 * the error as it is not fatal and could be provoked
> > +		 * by users.
> > +		 */
> >  		if (amountpipekva > maxpipekva / 2)
> > -			error = pipespace_new(pipe, SMALL_PIPE_SIZE);
> > +			(void)pipespace_new(pipe, SMALL_PIPE_SIZE);
> >  		else
> > -			error = pipespace_new(pipe, PIPE_SIZE);
> > -	} else {
> > -		/* If we're not backing this pipe, no need to do anything. */
> > -		error = 0;
> > +			(void)pipespace_new(pipe, PIPE_SIZE);
> >  	}
> > +
> >  	pipe->pipe_ino = -1;
> > -	return (error);
> > +	return (0);
> >  }
> >  
> 
> Yes, this looks right. I think it does not make sense to continue
> returning an error from the pipe_create() after the patch. The change
> would become bigger, but the code for pipe_create() and pipe_paircreate
> collapse. It seems that pipe_paircreate() can be changed to return void
> as well, but the benefits would be smaller.

I figured keeping pipe_create is beneficial in case one wants to play
with backing and does no harm.

That said how about the following:

diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c
index d3eb281..a3e8179 100644
--- a/sys/fs/fifofs/fifo_vnops.c
+++ b/sys/fs/fifofs/fifo_vnops.c
@@ -146,9 +146,7 @@ fifo_open(ap)
 	if (fp == NULL || (ap->a_mode & FEXEC) != 0)
 		return (EINVAL);
 	if ((fip = vp->v_fifoinfo) == NULL) {
-		error = pipe_named_ctor(&fpipe, td);
-		if (error != 0)
-			return (error);
+		pipe_named_ctor(&fpipe, td);
 		fip = malloc(sizeof(*fip), M_VNODE, M_WAITOK);
 		fip->fi_pipe = fpipe;
 		fpipe->pipe_wgen = fip->fi_readers = fip->fi_writers = 0;
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
index 6ba52e3..b0a1f0d 100644
--- a/sys/kern/sys_pipe.c
+++ b/sys/kern/sys_pipe.c
@@ -221,8 +221,8 @@ SYSCTL_INT(_kern_ipc, OID_AUTO, piperesizeallowed, CTLFLAG_RW,
 static void pipeinit(void *dummy __unused);
 static void pipeclose(struct pipe *cpipe);
 static void pipe_free_kmem(struct pipe *cpipe);
-static int pipe_create(struct pipe *pipe, int backing);
-static int pipe_paircreate(struct thread *td, struct pipepair **p_pp);
+static void pipe_create(struct pipe *pipe, int backing);
+static void pipe_paircreate(struct thread *td, struct pipepair **p_pp);
 static __inline int pipelock(struct pipe *cpipe, int catch);
 static __inline void pipeunlock(struct pipe *cpipe);
 #ifndef PIPE_NODIRECT
@@ -331,12 +331,11 @@ pipe_zone_fini(void *mem, int size)
 	mtx_destroy(&pp->pp_mtx);
 }
 
-static int
+static void
 pipe_paircreate(struct thread *td, struct pipepair **p_pp)
 {
 	struct pipepair *pp;
 	struct pipe *rpipe, *wpipe;
-	int error;
 
 	*p_pp = pp = uma_zalloc(pipe_zone, M_WAITOK);
 #ifdef MAC
@@ -355,30 +354,21 @@ pipe_paircreate(struct thread *td, struct pipepair **p_pp)
 	knlist_init_mtx(&wpipe->pipe_sel.si_note, PIPE_MTX(wpipe));
 
 	/* Only the forward direction pipe is backed by default */
-	if ((error = pipe_create(rpipe, 1)) != 0 ||
-	    (error = pipe_create(wpipe, 0)) != 0) {
-		pipeclose(rpipe);
-		pipeclose(wpipe);
-		return (error);
-	}
+	pipe_create(rpipe, 1);
+	pipe_create(wpipe, 0);
 
 	rpipe->pipe_state |= PIPE_DIRECTOK;
 	wpipe->pipe_state |= PIPE_DIRECTOK;
-	return (0);
 }
 
-int
+void
 pipe_named_ctor(struct pipe **ppipe, struct thread *td)
 {
 	struct pipepair *pp;
-	int error;
 
-	error = pipe_paircreate(td, &pp);
-	if (error != 0)
-		return (error);
+	pipe_paircreate(td, &pp);
 	pp->pp_rpipe.pipe_state |= PIPE_NAMED;
 	*ppipe = &pp->pp_rpipe;
-	return (0);
 }
 
 void
@@ -419,9 +409,7 @@ kern_pipe2(struct thread *td, int fildes[2], int flags)
 	int fd, fflags, error;
 
 	fdp = td->td_proc->p_fd;
-	error = pipe_paircreate(td, &pp);
-	if (error != 0)
-		return (error);
+	pipe_paircreate(td, &pp);
 	rpipe = &pp->pp_rpipe;
 	wpipe = &pp->pp_wpipe;
 	error = falloc(td, &rf, &fd, flags);
@@ -642,24 +630,25 @@ pipeselwakeup(cpipe)
  * Initialize and allocate VM and memory for pipe.  The structure
  * will start out zero'd from the ctor, so we just manage the kmem.
  */
-static int
+static void
 pipe_create(pipe, backing)
 	struct pipe *pipe;
 	int backing;
 {
-	int error;
 
 	if (backing) {
+		/*
+		 * Note that these functions can fail, but we ignore
+		 * the error as it is not fatal and could be provoked
+		 * by users.
+		 */
 		if (amountpipekva > maxpipekva / 2)
-			error = pipespace_new(pipe, SMALL_PIPE_SIZE);
+			(void)pipespace_new(pipe, SMALL_PIPE_SIZE);
 		else
-			error = pipespace_new(pipe, PIPE_SIZE);
-	} else {
-		/* If we're not backing this pipe, no need to do anything. */
-		error = 0;
+			(void)pipespace_new(pipe, PIPE_SIZE);
 	}
+
 	pipe->pipe_ino = -1;
-	return (error);
 }
 
 /* ARGSUSED */
diff --git a/sys/sys/pipe.h b/sys/sys/pipe.h
index dfe7f2f..ee7c128 100644
--- a/sys/sys/pipe.h
+++ b/sys/sys/pipe.h
@@ -142,6 +142,6 @@ struct pipepair {
 #define PIPE_LOCK_ASSERT(pipe, type)  mtx_assert(PIPE_MTX(pipe), (type))
 
 void	pipe_dtor(struct pipe *dpipe);
-int	pipe_named_ctor(struct pipe **ppipe, struct thread *td);
+void	pipe_named_ctor(struct pipe **ppipe, struct thread *td);
 void	pipeselwakeup(struct pipe *cpipe);
 #endif /* !_SYS_PIPE_H_ */



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