Commit c7e74ca2 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix hvc console sleep in spinlock bug

From: Jeremy Kerr <jk@ozlabs.org>

This patch fixes the sleep in spinlock hvc bug in hvc_write().

The code is a little longer, but protects against large amounts of memory
being kmalloc()ed by userspace, and minimises calls to copy_from_user().
parent 824315d9
...@@ -131,31 +131,65 @@ static int hvc_write(struct tty_struct *tty, int from_user, ...@@ -131,31 +131,65 @@ static int hvc_write(struct tty_struct *tty, int from_user,
const unsigned char *buf, int count) const unsigned char *buf, int count)
{ {
struct hvc_struct *hp = tty->driver_data; struct hvc_struct *hp = tty->driver_data;
char *p; char *tbuf, *p;
int todo, written = 0; int tbsize, rsize, written = 0;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&hp->lock, flags);
while (count > 0 && (todo = N_OUTBUF - hp->n_outbuf) > 0) {
if (todo > count)
todo = count;
p = hp->outbuf + hp->n_outbuf;
if (from_user) { if (from_user) {
todo -= copy_from_user(p, buf, todo); tbsize = min(count, (int)PAGE_SIZE);
if (todo == 0) { if (!(tbuf = kmalloc(tbsize, GFP_KERNEL)))
return -ENOMEM;
while ((rsize = count - written) > 0) {
int wsize;
if (rsize > tbsize)
rsize = tbsize;
p = tbuf;
rsize -= copy_from_user(p, buf, rsize);
if (!rsize) {
if (written == 0) if (written == 0)
written = -EFAULT; written = -EFAULT;
break; break;
} }
} else buf += rsize;
memcpy(p, buf, todo); written += rsize;
count -= todo;
buf += todo; spin_lock_irqsave(&hp->lock, flags);
hp->n_outbuf += todo; for (wsize = N_OUTBUF - hp->n_outbuf; rsize && wsize;
written += todo; wsize = N_OUTBUF - hp->n_outbuf) {
if (wsize > rsize)
wsize = rsize;
memcpy(hp->outbuf + hp->n_outbuf, p, wsize);
hp->n_outbuf += wsize;
hvc_push(hp);
rsize -= wsize;
p += wsize;
}
spin_unlock_irqrestore(&hp->lock, flags);
if (rsize)
break;
if (count < tbsize)
tbsize = count;
}
kfree(tbuf);
} else {
spin_lock_irqsave(&hp->lock, flags);
while (count > 0 && (rsize = N_OUTBUF - hp->n_outbuf) > 0) {
if (rsize > count)
rsize = count;
memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);
count -= rsize;
buf += rsize;
hp->n_outbuf += rsize;
written += rsize;
hvc_push(hp); hvc_push(hp);
} }
spin_unlock_irqrestore(&hp->lock, flags); spin_unlock_irqrestore(&hp->lock, flags);
}
return written; return written;
} }
......
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