diff options
author | Matthew Dillon <dillon@FreeBSD.org> | 2000-11-18 21:01:04 +0000 |
---|---|---|
committer | Matthew Dillon <dillon@FreeBSD.org> | 2000-11-18 21:01:04 +0000 |
commit | 279d7226048c39b48b788e5ea5607c0dbc042acd (patch) | |
tree | 8d32488eeb453793eb19bb380ffcc26e97ca77fd /sys/kern/vfs_aio.c | |
parent | d641426e5acce54fd775c3be86ddb9c2e78e8540 (diff) | |
download | src-279d7226048c39b48b788e5ea5607c0dbc042acd.tar.gz src-279d7226048c39b48b788e5ea5607c0dbc042acd.zip |
This patchset fixes a large number of file descriptor race conditions.
Pre-rfork code assumed inherent locking of a process's file descriptor
array. However, with the advent of rfork() the file descriptor table
could be shared between processes. This patch closes over a dozen
serious race conditions related to one thread manipulating the table
(e.g. closing or dup()ing a descriptor) while another is blocked in
an open(), close(), fcntl(), read(), write(), etc...
PR: kern/11629
Discussed with: Alexander Viro <viro@math.psu.edu>
Notes
Notes:
svn path=/head/; revision=68883
Diffstat (limited to 'sys/kern/vfs_aio.c')
-rw-r--r-- | sys/kern/vfs_aio.c | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/sys/kern/vfs_aio.c b/sys/kern/vfs_aio.c index a56488613ef3..d4a226c75e4b 100644 --- a/sys/kern/vfs_aio.c +++ b/sys/kern/vfs_aio.c @@ -589,6 +589,11 @@ aio_process(struct aiocblist *aiocbe) inblock_st = mycp->p_stats->p_ru.ru_inblock; oublock_st = mycp->p_stats->p_ru.ru_oublock; + /* + * Temporarily bump the ref count while reading to avoid the + * descriptor being ripped out from under us. + */ + fhold(fp); if (cb->aio_lio_opcode == LIO_READ) { auio.uio_rw = UIO_READ; error = fo_read(fp, &auio, fp->f_cred, FOF_OFFSET, mycp); @@ -596,6 +601,7 @@ aio_process(struct aiocblist *aiocbe) auio.uio_rw = UIO_WRITE; error = fo_write(fp, &auio, fp->f_cred, FOF_OFFSET, mycp); } + fdrop(fp, mycp); inblock_end = mycp->p_stats->p_ru.ru_inblock; oublock_end = mycp->p_stats->p_ru.ru_oublock; @@ -986,6 +992,8 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) if (ki->kaio_buffer_count >= ki->kaio_ballowed_count) return (-1); + fhold(fp); + ki->kaio_buffer_count++; lj = aiocbe->lio; @@ -1074,6 +1082,7 @@ aio_qphysio(struct proc *p, struct aiocblist *aiocbe) splx(s); if (notify) KNOTE(&aiocbe->klist, 0); + fdrop(fp, p); return 0; doerror: @@ -1082,6 +1091,7 @@ doerror: lj->lioj_buffer_count--; aiocbe->bp = NULL; relpbuf(bp, NULL); + fdrop(fp, p); return error; } @@ -1291,6 +1301,8 @@ _aio_aqueue(struct proc *p, struct aiocb *job, struct aio_liojob *lj, int type) return EINVAL; } + fhold(fp); + /* * XXX * Figure out how to do this properly. This currently won't @@ -1326,7 +1338,7 @@ aqueue_fail: TAILQ_INSERT_HEAD(&aio_freejobs, aiocbe, list); if (type == 0) suword(&job->_aiocb_private.error, error); - return (error); + goto done; } no_kqueue: } @@ -1363,18 +1375,19 @@ no_kqueue: ki->kaio_queue_count++; num_queue_count++; splx(s); - return 0; + error = 0; + goto done; } splx(s); } if ((error = aio_qphysio(p, aiocbe)) == 0) - return 0; - else if (error > 0) { + goto done; + if (error > 0) { suword(&job->_aiocb_private.status, 0); aiocbe->uaiocb._aiocb_private.error = error; suword(&job->_aiocb_private.error, error); - return error; + goto done; } /* No buffer for daemon I/O. */ @@ -1418,6 +1431,8 @@ retryproc: num_aio_resv_start--; } splx(s); +done: + fdrop(fp, p); return error; } @@ -1907,7 +1922,13 @@ aio_read(struct proc *p, struct aio_read_args *uap) auio.uio_procp = p; cnt = iocb.aio_nbytes; + /* + * Temporarily bump the ref count while reading to avoid the + * descriptor being ripped out from under us. + */ + fhold(fp); error = fo_read(fp, &auio, fp->f_cred, FOF_OFFSET, p); + fdrop(fp, p); if (error && (auio.uio_resid != cnt) && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; @@ -1974,7 +1995,13 @@ aio_write(struct proc *p, struct aio_write_args *uap) auio.uio_procp = p; cnt = iocb.aio_nbytes; + /* + * Temporarily bump the ref count while writing to avoid the + * descriptor being ripped out from under us. + */ + fhold(fp); error = fo_write(fp, &auio, fp->f_cred, FOF_OFFSET, p); + fdrop(fp, p); if (error) { if (auio.uio_resid != cnt) { if (error == ERESTART || error == EINTR || error == |