diff options
author | mattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2008-04-19 21:54:03 +0000 |
---|---|---|
committer | mattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2008-04-19 21:54:03 +0000 |
commit | 7d263bb9b55130b4b58a99ccc2ab092c63136e3b (patch) | |
tree | 0d87134854857448f93c05a2a320ed47276ac5e7 /kernel/zaptel-base.c | |
parent | 0a591d86a1cc9bdddac044b646f3d5a45222dba3 (diff) |
Partial fix for #9379. Fixes potential race condition in open and close routines for zaptel devices
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@4183 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'kernel/zaptel-base.c')
-rw-r--r-- | kernel/zaptel-base.c | 26 |
1 files changed, 16 insertions, 10 deletions
diff --git a/kernel/zaptel-base.c b/kernel/zaptel-base.c index 326a7b1..8e2f41f 100644 --- a/kernel/zaptel-base.c +++ b/kernel/zaptel-base.c @@ -2461,18 +2461,20 @@ static int zt_specchan_open(struct inode *inode, struct file *file, int unit, in if (chans[unit] && chans[unit]->sig) { /* Make sure we're not already open, a net device, or a slave device */ - if (chans[unit]->flags & ZT_FLAG_OPEN) - res = -EBUSY; - else if (chans[unit]->flags & ZT_FLAG_NETDEV) + if (chans[unit]->flags & ZT_FLAG_NETDEV) res = -EBUSY; else if (chans[unit]->master != chans[unit]) res = -EBUSY; else if ((chans[unit]->sig & __ZT_SIG_DACS) == __ZT_SIG_DACS) res = -EBUSY; - else { + else if (!test_and_set_bit(ZT_FLAGBIT_OPEN, &chans[unit]->flags)) { unsigned long flags; - /* Assume everything is going to be okay */ res = initialize_channel(chans[unit]); + if (res) { + /* Reallocbufs must have failed */ + clear_bit(ZT_FLAGBIT_OPEN, &chans[unit]->flags); + return res; + } spin_lock_irqsave(&chans[unit]->lock, flags); if (chans[unit]->flags & ZT_FLAG_PSEUDO) chans[unit]->flags |= ZT_FLAG_AUDIO; @@ -2485,13 +2487,14 @@ static int zt_specchan_open(struct inode *inode, struct file *file, int unit, in if (inc) MOD_INC_USE_COUNT; #endif - chans[unit]->flags |= ZT_FLAG_OPEN; spin_unlock_irqrestore(&chans[unit]->lock, flags); } else { spin_unlock_irqrestore(&chans[unit]->lock, flags); close_channel(chans[unit]); + clear_bit(ZT_FLAGBIT_OPEN, &chans[unit]->flags); } - } + } else + res = -EBUSY; } else res = -ENXIO; return res; @@ -2500,15 +2503,18 @@ static int zt_specchan_open(struct inode *inode, struct file *file, int unit, in static int zt_specchan_release(struct inode *node, struct file *file, int unit) { int res=0; + unsigned long flags; + if (chans[unit]) { - unsigned long flags; + /* Chan lock protects contents against potentially non atomic accesses. + * So if the pointer setting is not atomic, we should protect */ spin_lock_irqsave(&chans[unit]->lock, flags); - chans[unit]->flags &= ~ZT_FLAG_OPEN; - spin_unlock_irqrestore(&chans[unit]->lock, flags); chans[unit]->file = NULL; + spin_unlock_irqrestore(&chans[unit]->lock, flags); close_channel(chans[unit]); if (chans[unit]->span && chans[unit]->span->close) res = chans[unit]->span->close(chans[unit]); + clear_bit(ZT_FLAGBIT_OPEN, &chans[unit]->flags); } else res = -ENXIO; #ifndef LINUX26 |