Commit 24def5ac authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA CVS update

ALSA<-OSS sequencer
rewrote snd_seq_oss_read/snd_seq_oss_write to fix various
buffer overflow/locking/nonstandard behaviour bugs
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
parent e3f481be
...@@ -149,50 +149,37 @@ snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev) ...@@ -149,50 +149,37 @@ snd_seq_oss_readq_put_event(seq_oss_readq_t *q, evrec_t *ev)
/* /*
* pop queue * pop queue
* caller must hold lock
*/ */
evrec_t * int
snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags) snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec)
{ {
evrec_t *p; if (q->qlen == 0)
return -EAGAIN;
spin_lock_irqsave(&q->lock, *rflags); memcpy(rec, &q->q[q->head], sizeof(*rec));
if (q->qlen == 0) { return 0;
if (blocking) {
spin_unlock(&q->lock);
interruptible_sleep_on_timeout(&q->midi_sleep,
q->pre_event_timeout);
spin_lock(&q->lock);
}
if (q->qlen == 0) {
spin_unlock_irqrestore(&q->lock, *rflags);
return NULL;
}
}
p = q->q + q->head;
return p;
} }
/* /*
* unlock queue * sleep until ready
*/ */
void void
snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags) snd_seq_oss_readq_wait(seq_oss_readq_t *q)
{ {
spin_unlock_irqrestore(&q->lock, flags); interruptible_sleep_on_timeout(&q->midi_sleep, q->pre_event_timeout);
} }
/* /*
* drain one record and unlock queue * drain one record
* caller must hold lock
*/ */
void void
snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags) snd_seq_oss_readq_free(seq_oss_readq_t *q)
{ {
if (q->qlen > 0) { if (q->qlen > 0) {
q->head = (q->head + 1) % q->maxlen; q->head = (q->head + 1) % q->maxlen;
q->qlen--; q->qlen--;
} }
spin_unlock_irqrestore(&q->lock, flags);
} }
/* /*
......
...@@ -46,8 +46,11 @@ unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, p ...@@ -46,8 +46,11 @@ unsigned int snd_seq_oss_readq_poll(seq_oss_readq_t *readq, struct file *file, p
int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len); int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len);
int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev); int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev);
int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode); int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode);
evrec_t *snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags); int snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec);
void snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags); void snd_seq_oss_readq_wait(seq_oss_readq_t *q);
void snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags); void snd_seq_oss_readq_free(seq_oss_readq_t *q);
#define snd_seq_oss_readq_lock(q, flags) spin_lock_irqsave(&(q)->lock, flags)
#define snd_seq_oss_readq_unlock(q, flags) spin_unlock_irqrestore(&(q)->lock, flags)
#endif #endif
...@@ -44,35 +44,47 @@ int ...@@ -44,35 +44,47 @@ int
snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count) snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count)
{ {
seq_oss_readq_t *readq = dp->readq; seq_oss_readq_t *readq = dp->readq;
int cnt, pos; int result = 0, err = 0;
evrec_t *q; int ev_len;
evrec_t rec;
unsigned long flags; unsigned long flags;
if (readq == NULL || ! is_read_mode(dp->file_mode)) if (readq == NULL || ! is_read_mode(dp->file_mode))
return -EIO; return -ENXIO;
/* copy queued events to read buffer */ while (count >= SHORT_EVENT_SIZE) {
cnt = count; snd_seq_oss_readq_lock(readq, flags);
pos = 0; err = snd_seq_oss_readq_pick(readq, &rec);
q = snd_seq_oss_readq_pick(readq, !is_nonblock_mode(dp->file_mode), &flags); if (err == -EAGAIN &&
if (q == NULL) !is_nonblock_mode(dp->file_mode) && result == 0) {
return 0; snd_seq_oss_readq_unlock(readq, flags);
do { snd_seq_oss_readq_wait(readq);
int ev_len; snd_seq_oss_readq_lock(readq, flags);
/* tansfer the data */ if (signal_pending(current))
ev_len = ev_length(q); err = -ERESTARTSYS;
if (copy_to_user(buf + pos, q, ev_len)) { else
err = snd_seq_oss_readq_pick(readq, &rec);
}
if (err < 0) {
snd_seq_oss_readq_unlock(readq, flags); snd_seq_oss_readq_unlock(readq, flags);
break; break;
} }
snd_seq_oss_readq_free(readq, flags); ev_len = ev_length(&rec);
pos += ev_len; if (ev_len < count) {
cnt -= ev_len; snd_seq_oss_readq_unlock(readq, flags);
if (cnt < ev_len)
break; break;
} while ((q = snd_seq_oss_readq_pick(readq, 0, &flags)) != NULL); }
snd_seq_oss_readq_free(readq);
return count - cnt; snd_seq_oss_readq_unlock(readq, flags);
if (copy_to_user(buf, &rec, ev_len)) {
err = -EFAULT;
break;
}
result += ev_len;
buf += ev_len;
count -= ev_len;
}
return result > 0 ? result : err;
} }
...@@ -83,56 +95,64 @@ snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count) ...@@ -83,56 +95,64 @@ snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count)
int int
snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt) snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt)
{ {
int rc, c, p, ev_size; int result = 0, err = 0;
int ev_size, fmt;
evrec_t rec; evrec_t rec;
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return -EIO; return -ENXIO;
c = count; while (count >= SHORT_EVENT_SIZE) {
p = 0; if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) {
while (c >= SHORT_EVENT_SIZE) { err = -EFAULT;
if (copy_from_user(rec.c, buf + p, SHORT_EVENT_SIZE))
break; break;
p += SHORT_EVENT_SIZE; }
if (rec.s.code == SEQ_FULLSIZE) { if (rec.s.code == SEQ_FULLSIZE) {
/* load patch */ /* load patch */
int fmt = (*(unsigned short *)rec.c) & 0xffff; if (result > 0) {
return snd_seq_oss_synth_load_patch(dp, rec.s.dev, fmt, buf, p, c); err = -EINVAL;
break;
}
fmt = (*(unsigned short *)rec.c) & 0xffff;
/* FIXME the return value isn't correct */
return snd_seq_oss_synth_load_patch(dp, rec.s.dev,
fmt, buf, 0, count);
} }
if (ev_is_long(&rec)) { if (ev_is_long(&rec)) {
/* extended code */ /* extended code */
if (rec.s.code == SEQ_EXTENDED && if (rec.s.code == SEQ_EXTENDED &&
dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
return -EINVAL; err = -EINVAL;
break;
}
ev_size = LONG_EVENT_SIZE; ev_size = LONG_EVENT_SIZE;
if (c < ev_size) if (count < ev_size)
break; break;
/* copy the reset 4 bytes */ /* copy the reset 4 bytes */
if (copy_from_user(rec.c + SHORT_EVENT_SIZE, buf + p, if (copy_from_user(rec.c + SHORT_EVENT_SIZE,
LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) buf + SHORT_EVENT_SIZE,
LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) {
err = -EFAULT;
break; break;
p += LONG_EVENT_SIZE - SHORT_EVENT_SIZE; }
} else { } else {
/* old-type code */ /* old-type code */
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
return -EINVAL; err = -EINVAL;
break;
}
ev_size = SHORT_EVENT_SIZE; ev_size = SHORT_EVENT_SIZE;
} }
/* insert queue */ /* insert queue */
if ((rc = insert_queue(dp, &rec, opt)) < 0) if ((err = insert_queue(dp, &rec, opt)) < 0)
break; break;
c -= ev_size; result += ev_size;
buf += ev_size;
count -= ev_size;
} }
return result > 0 ? result : err;
if (count == c && is_nonblock_mode(dp->file_mode))
return -EAGAIN;
return count - c;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment