All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Slaby <jslaby@suse.cz>
To: stable@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	Alexander Popov <alex.popov@linux.com>,
	Jiri Slaby <jslaby@suse.cz>
Subject: [PATCH 3.12 46/60] tty: n_hdlc: get rid of racy n_hdlc.tbuf
Date: Tue, 14 Mar 2017 14:15:37 +0100	[thread overview]
Message-ID: <63075fbddd5151d2e98fa7cf0608a2113e23607d.1489497268.git.jslaby@suse.cz> (raw)
In-Reply-To: <d93cf67053e241539a1ef7c30ee8583022bc0e89.1489497268.git.jslaby@suse.cz>
In-Reply-To: <cover.1489497268.git.jslaby@suse.cz>

From: Alexander Popov <alex.popov@linux.com>

3.12-stable review patch.  If anyone has any objections, please let me know.

===============

commit 82f2341c94d270421f383641b7cd670e474db56b upstream.

Currently N_HDLC line discipline uses a self-made singly linked list for
data buffers and has n_hdlc.tbuf pointer for buffer retransmitting after
an error.

The commit be10eb7589337e5defbe214dae038a53dd21add8
("tty: n_hdlc add buffer flushing") introduced racy access to n_hdlc.tbuf.
After tx error concurrent flush_tx_queue() and n_hdlc_send_frames() can put
one data buffer to tx_free_buf_list twice. That causes double free in
n_hdlc_release().

Let's use standard kernel linked list and get rid of n_hdlc.tbuf:
in case of tx error put current data buffer after the head of tx_buf_list.

Signed-off-by: Alexander Popov <alex.popov@linux.com>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
 drivers/tty/n_hdlc.c | 132 +++++++++++++++++++++++++++------------------------
 1 file changed, 69 insertions(+), 63 deletions(-)

diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index f26657c06870..66fb07684133 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -114,7 +114,7 @@
 #define DEFAULT_TX_BUF_COUNT 3
 
 struct n_hdlc_buf {
-	struct n_hdlc_buf *link;
+	struct list_head  list_item;
 	int		  count;
 	char		  buf[1];
 };
@@ -122,8 +122,7 @@ struct n_hdlc_buf {
 #define	N_HDLC_BUF_SIZE	(sizeof(struct n_hdlc_buf) + maxframe)
 
 struct n_hdlc_buf_list {
-	struct n_hdlc_buf *head;
-	struct n_hdlc_buf *tail;
+	struct list_head  list;
 	int		  count;
 	spinlock_t	  spinlock;
 };
@@ -136,7 +135,6 @@ struct n_hdlc_buf_list {
  * @backup_tty - TTY to use if tty gets closed
  * @tbusy - reentrancy flag for tx wakeup code
  * @woke_up - FIXME: describe this field
- * @tbuf - currently transmitting tx buffer
  * @tx_buf_list - list of pending transmit frame buffers
  * @rx_buf_list - list of received frame buffers
  * @tx_free_buf_list - list unused transmit frame buffers
@@ -149,7 +147,6 @@ struct n_hdlc {
 	struct tty_struct	*backup_tty;
 	int			tbusy;
 	int			woke_up;
-	struct n_hdlc_buf	*tbuf;
 	struct n_hdlc_buf_list	tx_buf_list;
 	struct n_hdlc_buf_list	rx_buf_list;
 	struct n_hdlc_buf_list	tx_free_buf_list;
@@ -159,6 +156,8 @@ struct n_hdlc {
 /*
  * HDLC buffer list manipulation functions
  */
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+						struct n_hdlc_buf *buf);
 static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
 			   struct n_hdlc_buf *buf);
 static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);
@@ -208,16 +207,9 @@ static void flush_tx_queue(struct tty_struct *tty)
 {
 	struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
 	struct n_hdlc_buf *buf;
-	unsigned long flags;
 
 	while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list)))
 		n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf);
- 	spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
-	if (n_hdlc->tbuf) {
-		n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf);
-		n_hdlc->tbuf = NULL;
-	}
-	spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
 }
 
 static struct tty_ldisc_ops n_hdlc_ldisc = {
@@ -283,7 +275,6 @@ static void n_hdlc_release(struct n_hdlc *n_hdlc)
 		} else
 			break;
 	}
-	kfree(n_hdlc->tbuf);
 	kfree(n_hdlc);
 	
 }	/* end of n_hdlc_release() */
@@ -402,13 +393,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
 	n_hdlc->woke_up = 0;
 	spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
 
-	/* get current transmit buffer or get new transmit */
-	/* buffer from list of pending transmit buffers */
-		
-	tbuf = n_hdlc->tbuf;
-	if (!tbuf)
-		tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
-		
+	tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
 	while (tbuf) {
 		if (debuglevel >= DEBUG_LEVEL_INFO)	
 			printk("%s(%d)sending frame %p, count=%d\n",
@@ -420,7 +405,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
 
 		/* rollback was possible and has been done */
 		if (actual == -ERESTARTSYS) {
-			n_hdlc->tbuf = tbuf;
+			n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
 			break;
 		}
 		/* if transmit error, throw frame away by */
@@ -435,10 +420,7 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
 					
 			/* free current transmit buffer */
 			n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);
-			
-			/* this tx buffer is done */
-			n_hdlc->tbuf = NULL;
-			
+
 			/* wait up sleeping writers */
 			wake_up_interruptible(&tty->write_wait);
 	
@@ -448,10 +430,12 @@ static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty)
 			if (debuglevel >= DEBUG_LEVEL_INFO)	
 				printk("%s(%d)frame %p pending\n",
 					__FILE__,__LINE__,tbuf);
-					
-			/* buffer not accepted by driver */
-			/* set this buffer as pending buffer */
-			n_hdlc->tbuf = tbuf;
+
+			/*
+			 * the buffer was not accepted by driver,
+			 * return it back into tx queue
+			 */
+			n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
 			break;
 		}
 	}
@@ -749,7 +733,8 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
 	int error = 0;
 	int count;
 	unsigned long flags;
-	
+	struct n_hdlc_buf *buf = NULL;
+
 	if (debuglevel >= DEBUG_LEVEL_INFO)	
 		printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
 			__FILE__,__LINE__,cmd);
@@ -763,8 +748,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
 		/* report count of read data available */
 		/* in next available frame (if any) */
 		spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
-		if (n_hdlc->rx_buf_list.head)
-			count = n_hdlc->rx_buf_list.head->count;
+		buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
+						struct n_hdlc_buf, list_item);
+		if (buf)
+			count = buf->count;
 		else
 			count = 0;
 		spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
@@ -776,8 +763,10 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file,
 		count = tty_chars_in_buffer(tty);
 		/* add size of next output frame in queue */
 		spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
-		if (n_hdlc->tx_buf_list.head)
-			count += n_hdlc->tx_buf_list.head->count;
+		buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
+						struct n_hdlc_buf, list_item);
+		if (buf)
+			count += buf->count;
 		spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);
 		error = put_user(count, (int __user *)arg);
 		break;
@@ -825,14 +814,14 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp,
 		poll_wait(filp, &tty->write_wait, wait);
 
 		/* set bits for operations that won't block */
-		if (n_hdlc->rx_buf_list.head)
+		if (!list_empty(&n_hdlc->rx_buf_list.list))
 			mask |= POLLIN | POLLRDNORM;	/* readable */
 		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
 			mask |= POLLHUP;
 		if (tty_hung_up_p(filp))
 			mask |= POLLHUP;
 		if (!tty_is_writelocked(tty) &&
-				n_hdlc->tx_free_buf_list.head)
+				!list_empty(&n_hdlc->tx_free_buf_list.list))
 			mask |= POLLOUT | POLLWRNORM;	/* writable */
 	}
 	return mask;
@@ -858,7 +847,12 @@ static struct n_hdlc *n_hdlc_alloc(void)
 	spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock);
 	spin_lock_init(&n_hdlc->rx_buf_list.spinlock);
 	spin_lock_init(&n_hdlc->tx_buf_list.spinlock);
-	
+
+	INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list);
+	INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list);
+	INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list);
+	INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list);
+
 	/* allocate free rx buffer list */
 	for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {
 		buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
@@ -886,53 +880,65 @@ static struct n_hdlc *n_hdlc_alloc(void)
 }	/* end of n_hdlc_alloc() */
 
 /**
+ * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list
+ * @buf_list - pointer to the buffer list
+ * @buf - pointer to the buffer
+ */
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+						struct n_hdlc_buf *buf)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&buf_list->spinlock, flags);
+
+	list_add(&buf->list_item, &buf_list->list);
+	buf_list->count++;
+
+	spin_unlock_irqrestore(&buf_list->spinlock, flags);
+}
+
+/**
  * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list
- * @list - pointer to buffer list
+ * @buf_list - pointer to buffer list
  * @buf	- pointer to buffer
  */
-static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
+static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
 			   struct n_hdlc_buf *buf)
 {
 	unsigned long flags;
-	spin_lock_irqsave(&list->spinlock,flags);
-	
-	buf->link=NULL;
-	if (list->tail)
-		list->tail->link = buf;
-	else
-		list->head = buf;
-	list->tail = buf;
-	(list->count)++;
-	
-	spin_unlock_irqrestore(&list->spinlock,flags);
-	
+
+	spin_lock_irqsave(&buf_list->spinlock, flags);
+
+	list_add_tail(&buf->list_item, &buf_list->list);
+	buf_list->count++;
+
+	spin_unlock_irqrestore(&buf_list->spinlock, flags);
 }	/* end of n_hdlc_buf_put() */
 
 /**
  * n_hdlc_buf_get - remove and return an HDLC buffer from list
- * @list - pointer to HDLC buffer list
+ * @buf_list - pointer to HDLC buffer list
  * 
  * Remove and return an HDLC buffer from the head of the specified HDLC buffer
  * list.
  * Returns a pointer to HDLC buffer if available, otherwise %NULL.
  */
-static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list)
+static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
 {
 	unsigned long flags;
 	struct n_hdlc_buf *buf;
-	spin_lock_irqsave(&list->spinlock,flags);
-	
-	buf = list->head;
+
+	spin_lock_irqsave(&buf_list->spinlock, flags);
+
+	buf = list_first_entry_or_null(&buf_list->list,
+						struct n_hdlc_buf, list_item);
 	if (buf) {
-		list->head = buf->link;
-		(list->count)--;
+		list_del(&buf->list_item);
+		buf_list->count--;
 	}
-	if (!list->head)
-		list->tail = NULL;
-	
-	spin_unlock_irqrestore(&list->spinlock,flags);
+
+	spin_unlock_irqrestore(&buf_list->spinlock, flags);
 	return buf;
-	
 }	/* end of n_hdlc_buf_get() */
 
 static char hdlc_banner[] __initdata =
-- 
2.12.0

  parent reply	other threads:[~2017-03-14 13:22 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-03-14 13:15 [PATCH 3.12 00/60] 3.12.72-stable review Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 01/60] md linear: fix a race between linear_add() and linear_congested() Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 02/60] sctp: deny peeloff operation on asocs with threads sleeping on it Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 03/60] net/sched: em_meta: Fix 'meta vlan' to correctly recognize zero VID frames Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 04/60] perf trace: Use the syscall raw_syscalls:sys_enter timestamp Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 05/60] MIPS: Fix special case in 64 bit IP checksumming Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 06/60] MIPS: OCTEON: Fix copy_from_user fault handling for large buffers Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 07/60] MIPS: Clear ISA bit correctly in get_frame_info() Jiri Slaby
2017-03-14 13:14 ` [PATCH 3.12 08/60] MIPS: Prevent unaligned accesses during stack unwinding Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 09/60] MIPS: Fix get_frame_info() handling of microMIPS function size Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 10/60] MIPS: Fix is_jump_ins() handling of 16b microMIPS instructions Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 11/60] MIPS: Calculate microMIPS ra properly when unwinding the stack Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 12/60] MIPS: Handle microMIPS jumps in the same way as MIPS32/MIPS64 jumps Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 13/60] uvcvideo: Fix a wrong macro Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 14/60] ALSA: hda - fix Lewisburg audio issue Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 15/60] ALSA: timer: Reject user params with too small ticks Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 16/60] ALSA: seq: Fix link corruption by event error handling Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 17/60] staging: rtl: fix possible NULL pointer dereference Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 18/60] mm: vmpressure: fix sending wrong events on underflow Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 19/60] ipc/shm: Fix shmat mmap nil-page protection Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 20/60] scsi: storvsc: use tagged SRB requests if supported by the device Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 21/60] scsi: storvsc: properly handle SRB_ERROR when sense message is present Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 22/60] scsi: storvsc: properly set residual data length on errors Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 23/60] scsi: aacraid: Reorder Adapter status check Jiri Slaby
2017-03-14 13:15   ` Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 24/60] sd: get disk reference in sd_check_events() Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 25/60] jbd2: don't leak modified metadata buffers on an aborted journal Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 26/60] ext4: trim allocation requests to group size Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 27/60] ext4: preserve the needs_recovery flag when the journal is aborted Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 28/60] ext4: return EROFS if device is r/o and journal replay is needed Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 29/60] samples/seccomp: fix 64-bit comparison macros Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 30/60] ath5k: drop bogus warning on drv_set_key with unsupported cipher Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 31/60] ath9k: use correct OTP register offsets for the AR9340 and AR9550 Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 32/60] fuse: add missing FR_FORCE Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 33/60] can: usb_8dev: Fix memory leak of priv->cmd_msg_buffer Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 34/60] hv: allocate synic pages for all present CPUs Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 35/60] RDMA/core: Fix incorrect structure packing for booleans Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 36/60] rdma_cm: fail iwarp accepts w/o connection params Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 37/60] NFSv4: Fix memory and state leak in _nfs4_open_and_get_state Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 38/60] NFSv4: fix getacl head length estimation Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 39/60] NFSv4: fix getacl ERANGE for some ACL buffer sizes Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 40/60] bcma: use (get|put)_device when probing/removing device driver Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 41/60] powerpc/xmon: Fix data-breakpoint Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 42/60] MIPS: IP22: Reformat inline assembler code to modern standards Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 43/60] MIPS: IP22: Fix build error due to binutils 2.25 uselessnes Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 44/60] scsi: lpfc: Correct WQ creation for pagesize Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 45/60] TTY: n_hdlc, fix lockdep false positive Jiri Slaby
2017-03-14 13:15 ` Jiri Slaby [this message]
2017-03-14 13:15 ` [PATCH 3.12 47/60] serial: 8250_pci: Add MKS Tenta SCOM-0800 and SCOM-0801 cards Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 48/60] KVM: VMX: use correct vmcs_read/write for guest segment selector/base Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 49/60] Bluetooth: Add another AR3012 04ca:3018 device Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 50/60] s390/qdio: clear DSCI prior to scanning multiple input queues Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 51/60] s390: TASK_SIZE for kernel threads Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 52/60] IB/ipoib: Fix deadlock between rmmod and set_mode Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 53/60] ktest: Fix child exit code processing Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 54/60] nlm: Ensure callback code also checks that the files match Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 55/60] xtensa: move parse_tag_fdt out of #ifdef CONFIG_BLK_DEV_INITRD Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 56/60] mac80211: flush delayed work when entering suspend Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 57/60] drm/ast: Fix test for VGA enabled Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 58/60] drm/ttm: Make sure BOs being swapped out are cacheable Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 59/60] fat: fix using uninitialized fields of fat_inode/fsinfo_inode Jiri Slaby
2017-03-14 13:15 ` [PATCH 3.12 60/60] drivers: hv: Turn off write permission on the hypercall page Jiri Slaby
2017-03-14 13:24 ` [PATCH 3.12 00/60] 3.12.72-stable review Guenter Roeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=63075fbddd5151d2e98fa7cf0608a2113e23607d.1489497268.git.jslaby@suse.cz \
    --to=jslaby@suse.cz \
    --cc=alex.popov@linux.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.