Hi Greg,
On Fri, Sep 03, 2010 at 03:24:12PM -0700, Greg KH wrote:
> On Thu, Sep 02, 2010 at 10:41:30AM +0200, Moritz Muehlenhoff wrote:
> > Hi Greg,
> > please merge e74d098c66543d0731de62eb747ccd5b636a6f4c for 2.6.32.x
> > stable. It fixes CVE-2010-2563:
> > http://www.openwall.com/lists/oss-security/2010/07/07/5
>
> Are you sure that a simple backport works here? Remember, the tty layer
> underwent a lot of changes recently.
>
> Have you tested this out? If so, I'd be glad to apply it, otherwise I'm
> a bit worried.
Sorry for the late reply, it fell through the cracks and I only just
noticed your mail:
The following two patches for CVE-2010-2563 have been in Debian's 2.6.32
since two months:
320718ee074acce5ffced6506cb51af1388942aa
e74d098c66543d0731de62eb747ccd5b636a6f4c
Cheers,
Moritz
commit 320718ee074acce5ffced6506cb51af1388942aa
Author: Anton Blanchard <[email protected]>
Date: Tue Apr 6 21:42:38 2010 +1000
hvc_console: Fix race between hvc_close and hvc_remove
I don't claim to understand the tty layer, but it seems like hvc_open and
hvc_close should be balanced in their kref reference counting.
Right now we get a kref every call to hvc_open:
if (hp->count++ > 0) {
tty_kref_get(tty); <----- here
spin_unlock_irqrestore(&hp->lock, flags);
hvc_kick();
return 0;
} /* else count == 0 */
tty->driver_data = hp;
hp->tty = tty_kref_get(tty); <------ or here if hp->count was 0
But hvc_close has:
tty_kref_get(tty);
if (--hp->count == 0) {
...
/* Put the ref obtained in hvc_open() */
tty_kref_put(tty);
...
}
tty_kref_put(tty);
Since the outside kref get/put balance we only do a single kref_put when
count reaches 0.
The patch below changes things to call tty_kref_put once for every
hvc_close call, and with that my machine boots fine.
Signed-off-by: Anton Blanchard <[email protected]>
Acked-by: Amit Shah <[email protected]>
Signed-off-by: Rusty Russell <[email protected]>
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index d3890e8..35cca4c 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -368,16 +368,12 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
hp = tty->driver_data;
spin_lock_irqsave(&hp->lock, flags);
- tty_kref_get(tty);
if (--hp->count == 0) {
/* We are done with the tty pointer now. */
hp->tty = NULL;
spin_unlock_irqrestore(&hp->lock, flags);
- /* Put the ref obtained in hvc_open() */
- tty_kref_put(tty);
-
if (hp->ops->notifier_del)
hp->ops->notifier_del(hp, hp->data);
commit e74d098c66543d0731de62eb747ccd5b636a6f4c
Author: Amit Shah <[email protected]>
Date: Fri Mar 12 11:53:15 2010 +0530
hvc_console: Fix race between hvc_close and hvc_remove
Alan pointed out a race in the code where hvc_remove is invoked. The
recent virtio_console work is the first user of hvc_remove().
Alan describes it thus:
The hvc_console assumes that a close and remove call can't occur at the
same time.
In addition tty_hangup(tty) is problematic as tty_hangup is asynchronous
itself....
So this can happen
hvc_close hvc_remove
hung up ? - no
lock
tty = hp->tty
unlock
lock
hp->tty = NULL
unlock
notify del
kref_put the hvc struct
close completes
tty is destroyed
tty_hangup dead tty
tty->ops will be NULL
NULL->...
This patch adds some tty krefs and also converts to using tty_vhangup().
Reported-by: Alan Cox <[email protected]>
Signed-off-by: Amit Shah <[email protected]>
CC: Alan Cox <[email protected]>
CC: [email protected]
CC: Rusty Russell <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 465185f..ba55bba 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -312,6 +312,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
spin_lock_irqsave(&hp->lock, flags);
/* Check and then increment for fast path open. */
if (hp->count++ > 0) {
+ tty_kref_get(tty);
spin_unlock_irqrestore(&hp->lock, flags);
hvc_kick();
return 0;
@@ -319,7 +320,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
tty->driver_data = hp;
- hp->tty = tty;
+ hp->tty = tty_kref_get(tty);
spin_unlock_irqrestore(&hp->lock, flags);
@@ -336,6 +337,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
spin_lock_irqsave(&hp->lock, flags);
hp->tty = NULL;
spin_unlock_irqrestore(&hp->lock, flags);
+ tty_kref_put(tty);
tty->driver_data = NULL;
kref_put(&hp->kref, destroy_hvc_struct);
printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
@@ -363,13 +365,18 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
return;
hp = tty->driver_data;
+
spin_lock_irqsave(&hp->lock, flags);
+ tty_kref_get(tty);
if (--hp->count == 0) {
/* We are done with the tty pointer now. */
hp->tty = NULL;
spin_unlock_irqrestore(&hp->lock, flags);
+ /* Put the ref obtained in hvc_open() */
+ tty_kref_put(tty);
+
if (hp->ops->notifier_del)
hp->ops->notifier_del(hp, hp->data);
@@ -389,6 +396,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
spin_unlock_irqrestore(&hp->lock, flags);
}
+ tty_kref_put(tty);
kref_put(&hp->kref, destroy_hvc_struct);
}
@@ -424,10 +432,11 @@ static void hvc_hangup(struct tty_struct *tty)
spin_unlock_irqrestore(&hp->lock, flags);
if (hp->ops->notifier_hangup)
- hp->ops->notifier_hangup(hp, hp->data);
+ hp->ops->notifier_hangup(hp, hp->data);
while(temp_open_count) {
--temp_open_count;
+ tty_kref_put(tty);
kref_put(&hp->kref, destroy_hvc_struct);
}
}
@@ -592,7 +601,7 @@ int hvc_poll(struct hvc_struct *hp)
}
/* No tty attached, just skip */
- tty = hp->tty;
+ tty = tty_kref_get(hp->tty);
if (tty == NULL)
goto bail;
@@ -672,6 +681,8 @@ int hvc_poll(struct hvc_struct *hp)
tty_flip_buffer_push(tty);
}
+ if (tty)
+ tty_kref_put(tty);
return poll_mask;
}
@@ -807,7 +818,7 @@ int hvc_remove(struct hvc_struct *hp)
struct tty_struct *tty;
spin_lock_irqsave(&hp->lock, flags);
- tty = hp->tty;
+ tty = tty_kref_get(hp->tty);
if (hp->index < MAX_NR_HVC_CONSOLES)
vtermnos[hp->index] = -1;
@@ -819,18 +830,18 @@ int hvc_remove(struct hvc_struct *hp)
/*
* We 'put' the instance that was grabbed when the kref instance
* was initialized using kref_init(). Let the last holder of this
- * kref cause it to be removed, which will probably be the tty_hangup
+ * kref cause it to be removed, which will probably be the tty_vhangup
* below.
*/
kref_put(&hp->kref, destroy_hvc_struct);
/*
- * This function call will auto chain call hvc_hangup. The tty should
- * always be valid at this time unless a simultaneous tty close already
- * cleaned up the hvc_struct.
+ * This function call will auto chain call hvc_hangup.
*/
- if (tty)
- tty_hangup(tty);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
return 0;
}
EXPORT_SYMBOL_GPL(hvc_remove);
_______________________________________________
stable mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/stable