aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_gsm.c
diff options
context:
space:
mode:
authorRuss Gorby <russ.gorby@intel.com>2012-08-13 08:44:59 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-08-16 15:03:30 -0400
commitb4338e1efc339986cf6c0a3652906e914a86e2d3 (patch)
treeb7cad6cedd3a74c6c9f01ed74720999d2a16637e /drivers/tty/n_gsm.c
parent5e44708f75b0f8712da715d6babb0c21089b2317 (diff)
n_gsm: avoid accessing freed memory during CMD_FCOFF condition
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 <russ.gorby@intel.com> Tested-by: Yin, Fengwei <fengwei.yin@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Cc: Riding School <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/n_gsm.c')
-rw-r--r--drivers/tty/n_gsm.c40
1 files changed, 13 insertions, 27 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 0d93e51cb23d..6f4f8d3654d2 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -108,7 +108,7 @@ struct gsm_mux_net {
108 */ 108 */
109 109
110struct gsm_msg { 110struct gsm_msg {
111 struct gsm_msg *next; 111 struct list_head list;
112 u8 addr; /* DLCI address + flags */ 112 u8 addr; /* DLCI address + flags */
113 u8 ctrl; /* Control byte + flags */ 113 u8 ctrl; /* Control byte + flags */
114 unsigned int len; /* Length of data block (can be zero) */ 114 unsigned int len; /* Length of data block (can be zero) */
@@ -245,8 +245,7 @@ struct gsm_mux {
245 unsigned int tx_bytes; /* TX data outstanding */ 245 unsigned int tx_bytes; /* TX data outstanding */
246#define TX_THRESH_HI 8192 246#define TX_THRESH_HI 8192
247#define TX_THRESH_LO 2048 247#define TX_THRESH_LO 2048
248 struct gsm_msg *tx_head; /* Pending data packets */ 248 struct list_head tx_list; /* Pending data packets */
249 struct gsm_msg *tx_tail;
250 249
251 /* Control messages */ 250 /* Control messages */
252 struct timer_list t2_timer; /* Retransmit timer for commands */ 251 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,
663 m->len = len; 662 m->len = len;
664 m->addr = addr; 663 m->addr = addr;
665 m->ctrl = ctrl; 664 m->ctrl = ctrl;
666 m->next = NULL; 665 INIT_LIST_HEAD(&m->list);
667 return m; 666 return m;
668} 667}
669 668
@@ -681,16 +680,13 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
681 680
682static void gsm_data_kick(struct gsm_mux *gsm) 681static void gsm_data_kick(struct gsm_mux *gsm)
683{ 682{
684 struct gsm_msg *msg = gsm->tx_head; 683 struct gsm_msg *msg, *nmsg;
685 struct gsm_msg *free_msg;
686 int len; 684 int len;
687 int skip_sof = 0; 685 int skip_sof = 0;
688 686
689 while (msg) { 687 list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
690 if (gsm->constipated && msg->addr) { 688 if (gsm->constipated && msg->addr)
691 msg = msg->next;
692 continue; 689 continue;
693 }
694 if (gsm->encoding != 0) { 690 if (gsm->encoding != 0) {
695 gsm->txframe[0] = GSM1_SOF; 691 gsm->txframe[0] = GSM1_SOF;
696 len = gsm_stuff_frame(msg->data, 692 len = gsm_stuff_frame(msg->data,
@@ -718,14 +714,9 @@ static void gsm_data_kick(struct gsm_mux *gsm)
718 burst */ 714 burst */
719 skip_sof = 1; 715 skip_sof = 1;
720 716
721 if (gsm->tx_head == msg) 717 list_del(&msg->list);
722 gsm->tx_head = msg->next; 718 kfree(msg);
723 free_msg = msg;
724 msg = msg->next;
725 kfree(free_msg);
726 } 719 }
727 if (!gsm->tx_head)
728 gsm->tx_tail = NULL;
729} 720}
730 721
731/** 722/**
@@ -774,11 +765,7 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
774 msg->data = dp; 765 msg->data = dp;
775 766
776 /* Add to the actual output queue */ 767 /* Add to the actual output queue */
777 if (gsm->tx_tail) 768 list_add_tail(&msg->list, &gsm->tx_list);
778 gsm->tx_tail->next = msg;
779 else
780 gsm->tx_head = msg;
781 gsm->tx_tail = msg;
782 gsm->tx_bytes += msg->len; 769 gsm->tx_bytes += msg->len;
783 gsm_data_kick(gsm); 770 gsm_data_kick(gsm);
784} 771}
@@ -2026,7 +2013,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
2026{ 2013{
2027 int i; 2014 int i;
2028 struct gsm_dlci *dlci = gsm->dlci[0]; 2015 struct gsm_dlci *dlci = gsm->dlci[0];
2029 struct gsm_msg *txq; 2016 struct gsm_msg *txq, *utxq;
2030 struct gsm_control *gc; 2017 struct gsm_control *gc;
2031 2018
2032 gsm->dead = 1; 2019 gsm->dead = 1;
@@ -2061,11 +2048,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
2061 if (gsm->dlci[i]) 2048 if (gsm->dlci[i])
2062 gsm_dlci_release(gsm->dlci[i]); 2049 gsm_dlci_release(gsm->dlci[i]);
2063 /* Now wipe the queues */ 2050 /* Now wipe the queues */
2064 for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { 2051 list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
2065 gsm->tx_head = txq->next;
2066 kfree(txq); 2052 kfree(txq);
2067 } 2053 INIT_LIST_HEAD(&gsm->tx_list);
2068 gsm->tx_tail = NULL;
2069} 2054}
2070EXPORT_SYMBOL_GPL(gsm_cleanup_mux); 2055EXPORT_SYMBOL_GPL(gsm_cleanup_mux);
2071 2056
@@ -2176,6 +2161,7 @@ struct gsm_mux *gsm_alloc_mux(void)
2176 } 2161 }
2177 spin_lock_init(&gsm->lock); 2162 spin_lock_init(&gsm->lock);
2178 kref_init(&gsm->ref); 2163 kref_init(&gsm->ref);
2164 INIT_LIST_HEAD(&gsm->tx_list);
2179 2165
2180 gsm->t1 = T1; 2166 gsm->t1 = T1;
2181 gsm->t2 = T2; 2167 gsm->t2 = T2;