Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753543Ab2JNOl1 (ORCPT ); Sun, 14 Oct 2012 10:41:27 -0400 Received: from shadbolt.e.decadent.org.uk ([88.96.1.126]:46001 "EHLO shadbolt.e.decadent.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753422Ab2JNOlY (ORCPT ); Sun, 14 Oct 2012 10:41:24 -0400 Message-Id: <20121014143535.407123363@decadent.org.uk> User-Agent: quilt/0.60-1 Date: Sun, 14 Oct 2012 15:35:45 +0100 From: Ben Hutchings To: linux-kernel@vger.kernel.org, stable@vger.kernel.org Cc: akpm@linux-foundation.org, alan@lxorguk.ukuu.org.uk, Russ Gorby , "Yin, Fengwei" , Alan Cox , Greg Kroah-Hartman Subject: [ 012/147] n_gsm: avoid accessing freed memory during CMD_FCOFF condition In-Reply-To: <20121014143533.742627615@decadent.org.uk> X-SA-Exim-Connect-IP: 77.75.106.1 X-SA-Exim-Mail-From: ben@decadent.org.uk X-SA-Exim-Scanned: No (on shadbolt.decadent.org.uk); SAEximRunCond expanded to false Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4281 Lines: 146 3.2-stable review patch. If anyone has any objections, please let me know. ------------------ From: Russ Gorby commit b4338e1efc339986cf6c0a3652906e914a86e2d3 upstream. gsm_data_kick was recently modified to allow messages on the tx queue bound for DLCI0 to flow even during FCOFF conditions. Unfortunately we introduced a bug discovered by code inspection where subsequent list traversers can access freed memory if the DLCI0 messages were not all at the head of the list. Replaced singly linked tx list w/ a list_head and used provided interfaces for traversing and deleting members. Signed-off-by: Russ Gorby Tested-by: Yin, Fengwei Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman Signed-off-by: Ben Hutchings --- drivers/tty/n_gsm.c | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 0d93e51..6f4f8d3 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -108,7 +108,7 @@ struct gsm_mux_net { */ struct gsm_msg { - struct gsm_msg *next; + struct list_head list; u8 addr; /* DLCI address + flags */ u8 ctrl; /* Control byte + flags */ unsigned int len; /* Length of data block (can be zero) */ @@ -245,8 +245,7 @@ struct gsm_mux { unsigned int tx_bytes; /* TX data outstanding */ #define TX_THRESH_HI 8192 #define TX_THRESH_LO 2048 - struct gsm_msg *tx_head; /* Pending data packets */ - struct gsm_msg *tx_tail; + struct list_head tx_list; /* Pending data packets */ /* Control messages */ struct timer_list t2_timer; /* Retransmit timer for commands */ @@ -663,7 +662,7 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, m->len = len; m->addr = addr; m->ctrl = ctrl; - m->next = NULL; + INIT_LIST_HEAD(&m->list); return m; } @@ -681,16 +680,13 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, static void gsm_data_kick(struct gsm_mux *gsm) { - struct gsm_msg *msg = gsm->tx_head; - struct gsm_msg *free_msg; + struct gsm_msg *msg, *nmsg; int len; int skip_sof = 0; - while (msg) { - if (gsm->constipated && msg->addr) { - msg = msg->next; + list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) { + if (gsm->constipated && msg->addr) continue; - } if (gsm->encoding != 0) { gsm->txframe[0] = GSM1_SOF; len = gsm_stuff_frame(msg->data, @@ -718,14 +714,9 @@ static void gsm_data_kick(struct gsm_mux *gsm) burst */ skip_sof = 1; - if (gsm->tx_head == msg) - gsm->tx_head = msg->next; - free_msg = msg; - msg = msg->next; - kfree(free_msg); + list_del(&msg->list); + kfree(msg); } - if (!gsm->tx_head) - gsm->tx_tail = NULL; } /** @@ -774,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) msg->data = dp; /* Add to the actual output queue */ - if (gsm->tx_tail) - gsm->tx_tail->next = msg; - else - gsm->tx_head = msg; - gsm->tx_tail = msg; + list_add_tail(&msg->list, &gsm->tx_list); gsm->tx_bytes += msg->len; gsm_data_kick(gsm); } @@ -2026,7 +2013,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) { int i; struct gsm_dlci *dlci = gsm->dlci[0]; - struct gsm_msg *txq; + struct gsm_msg *txq, *utxq; struct gsm_control *gc; gsm->dead = 1; @@ -2061,11 +2048,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); /* Now wipe the queues */ - for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { - gsm->tx_head = txq->next; + list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) kfree(txq); - } - gsm->tx_tail = NULL; + INIT_LIST_HEAD(&gsm->tx_list); } EXPORT_SYMBOL_GPL(gsm_cleanup_mux); @@ -2176,6 +2161,7 @@ struct gsm_mux *gsm_alloc_mux(void) } spin_lock_init(&gsm->lock); kref_init(&gsm->ref); + INIT_LIST_HEAD(&gsm->tx_list); gsm->t1 = T1; gsm->t2 = T2; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/