All of lore.kernel.org
 help / color / mirror / Atom feed
* [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection
@ 2016-10-29 16:19 David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 1/5] fs: add overflow protection to struct fs_struct.users David Windsor
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: David Windsor @ 2016-10-29 16:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: keescook, elena.reshetova, ishkamiel, takahiro.akashi, colin, dwindsor

Expand HARDENED_ATOMIC overflow protection to cover more kernel reference
counters.

The original HARDENED_ATOMIC series adds overflow protection to existing kernel
users of atomic_t.  This series creates 8 new users of atomic_t:

 * struct fs_struct.users
 * struct tty_port.count
 * struct tty_ldisc_ops.refcount
 * struct pipe_inode_info.{readers|writers|files|waiting_writers}
 * struct kmem_cache.refcount

This series changes the type of these variables to atomic_t, thus affording them
the overflow protection provided by HARDENED_ATOMIC.

This is based upon work done by the PaX Team [1].

[1] https://forums.grsecurity.net/viewtopic.php?f=7&t=4173 

David Windsor (5):
  fs: add overflow protection to struct fs_struct.users
  tty: add overflow protection to struct tty_port.count
  tty: add overflow protection to struct tty_ldisc_ops.refcount
  fs: add overflow protection to struct
    pipe_inode_info.{readers|writers|files|waiting_writers}
  mm: add overflow protection to struct kmem_cache.refcount

 arch/um/drivers/line.c                 |  2 +-
 drivers/char/pcmcia/synclink_cs.c      | 16 ++++-----
 drivers/isdn/gigaset/interface.c       |  8 ++---
 drivers/isdn/i4l/isdn_tty.c            | 22 ++++++-------
 drivers/net/usb/hso.c                  | 22 ++++++-------
 drivers/s390/char/tty3270.c            |  2 +-
 drivers/staging/gdm724x/gdm_tty.c      |  2 +-
 drivers/tty/amiserial.c                |  4 +--
 drivers/tty/bfin_jtag_comm.c           |  4 +--
 drivers/tty/cyclades.c                 |  8 ++---
 drivers/tty/hvc/hvc_console.c          | 14 ++++----
 drivers/tty/hvc/hvcs.c                 | 20 ++++++------
 drivers/tty/hvc/hvsi.c                 | 10 +++---
 drivers/tty/ipwireless/tty.c           | 26 +++++++--------
 drivers/tty/moxa.c                     |  2 +-
 drivers/tty/n_gsm.c                    |  2 +-
 drivers/tty/n_tty.c                    |  3 +-
 drivers/tty/rocket.c                   |  8 ++---
 drivers/tty/serial/crisv10.c           | 34 ++++++++++----------
 drivers/tty/serial/serial_core.c       |  4 +--
 drivers/tty/synclink.c                 | 32 +++++++++---------
 drivers/tty/synclink_gt.c              | 28 ++++++++--------
 drivers/tty/synclinkmp.c               | 34 ++++++++++----------
 drivers/tty/tty_ldisc.c                |  8 ++---
 drivers/tty/tty_port.c                 | 22 ++++++-------
 drivers/usb/gadget/function/u_serial.c | 22 ++++++-------
 drivers/usb/serial/console.c           |  6 ++--
 fs/coredump.c                          | 10 +++---
 fs/exec.c                              |  2 +-
 fs/fs_struct.c                         |  8 ++---
 fs/namespace.c                         |  2 +-
 fs/pipe.c                              | 59 +++++++++++++++++-----------------
 fs/proc/task_nommu.c                   |  2 +-
 fs/splice.c                            | 36 ++++++++++-----------
 include/linux/fs_struct.h              |  2 +-
 include/linux/pipe_fs_i.h              |  8 ++---
 include/linux/slab_def.h               |  2 +-
 include/linux/slub_def.h               |  2 +-
 include/linux/tty.h                    |  4 +--
 include/linux/tty_ldisc.h              |  2 +-
 kernel/fork.c                          |  6 ++--
 kernel/user_namespace.c                |  2 +-
 mm/slab.c                              |  2 +-
 mm/slab.h                              |  2 +-
 mm/slab_common.c                       | 12 +++----
 mm/slub.c                              | 10 +++---
 net/bluetooth/rfcomm/tty.c             |  4 +--
 net/irda/ircomm/ircomm_tty.c           | 18 +++++------
 48 files changed, 281 insertions(+), 279 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [kernel-hardening][RFC PATCH 1/5] fs: add overflow protection to struct fs_struct.users
  2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
@ 2016-10-29 16:19 ` David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 2/5] tty: add overflow protection to struct tty_port.count David Windsor
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2016-10-29 16:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: keescook, elena.reshetova, ishkamiel, takahiro.akashi, colin, dwindsor

Change type of struct fs_struct.users to atomic_t.  This enables overflow
protection: when CONFIG_HARDENED_ATOMIC is enabled, atomic_t variables
cannot be overflowed.

The copyright for the original PAX_REFCOUNT code:
  - all REFCOUNT code in general: PaX Team <pageexec@freemail.hu>
  - various false positive fixes: Mathias Krause <minipli@googlemail.com>
---
 fs/exec.c                 | 2 +-
 fs/fs_struct.c            | 8 ++++----
 fs/namespace.c            | 2 +-
 fs/proc/task_nommu.c      | 2 +-
 include/linux/fs_struct.h | 2 +-
 kernel/fork.c             | 6 +++---
 kernel/user_namespace.c   | 2 +-
 7 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 4e497b9..ad79491 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1429,7 +1429,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
 	}
 	rcu_read_unlock();
 
-	if (p->fs->users > n_fs)
+	if (atomic_read(&p->fs->users) > n_fs)
 		bprm->unsafe |= LSM_UNSAFE_SHARE;
 	else
 		p->fs->in_exec = 1;
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 7dca743..697c96e 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -99,7 +99,7 @@ void exit_fs(struct task_struct *tsk)
 		task_lock(tsk);
 		spin_lock(&fs->lock);
 		tsk->fs = NULL;
-		kill = !--fs->users;
+		kill = !atomic_dec_return(&fs->users);
 		spin_unlock(&fs->lock);
 		task_unlock(tsk);
 		if (kill)
@@ -112,7 +112,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
 	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
 	/* We don't need to lock fs - think why ;-) */
 	if (fs) {
-		fs->users = 1;
+		atomic_set(&fs->users, 1);
 		fs->in_exec = 0;
 		spin_lock_init(&fs->lock);
 		seqcount_init(&fs->seq);
@@ -139,7 +139,7 @@ int unshare_fs_struct(void)
 
 	task_lock(current);
 	spin_lock(&fs->lock);
-	kill = !--fs->users;
+	kill = !atomic_dec_return(&fs->users);
 	current->fs = new_fs;
 	spin_unlock(&fs->lock);
 	task_unlock(current);
@@ -159,7 +159,7 @@ EXPORT_SYMBOL(current_umask);
 
 /* to be mentioned only in INIT_TASK */
 struct fs_struct init_fs = {
-	.users		= 1,
+	.users		= ATOMIC_INIT(1),
 	.lock		= __SPIN_LOCK_UNLOCKED(init_fs.lock),
 	.seq		= SEQCNT_ZERO(init_fs.seq),
 	.umask		= 0022,
diff --git a/fs/namespace.c b/fs/namespace.c
index 5d205f9..66a0b99 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -3394,7 +3394,7 @@ static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns)
 	    !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
 		return -EPERM;
 
-	if (fs->users != 1)
+	if (atomic_read(&fs->users) != 1)
 		return -EINVAL;
 
 	get_mnt_ns(mnt_ns);
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 3717562..b318930 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -51,7 +51,7 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
 	else
 		bytes += kobjsize(mm);
 	
-	if (current->fs && current->fs->users > 1)
+	if (current->fs && atomic_read(&current->fs->users) > 1)
 		sbytes += kobjsize(current->fs);
 	else
 		bytes += kobjsize(current->fs);
diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h
index 0efc3e6..e0e1e5f 100644
--- a/include/linux/fs_struct.h
+++ b/include/linux/fs_struct.h
@@ -6,7 +6,7 @@
 #include <linux/seqlock.h>
 
 struct fs_struct {
-	int users;
+	atomic_t users;
 	spinlock_t lock;
 	seqcount_t seq;
 	int umask;
diff --git a/kernel/fork.c b/kernel/fork.c
index 623259f..f06f356 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1203,7 +1203,7 @@ static int copy_fs(unsigned long clone_flags, struct task_struct *tsk)
 			spin_unlock(&fs->lock);
 			return -EAGAIN;
 		}
-		fs->users++;
+		atomic_inc(&fs->users);
 		spin_unlock(&fs->lock);
 		return 0;
 	}
@@ -2129,7 +2129,7 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
 		return 0;
 
 	/* don't need lock here; in the worst case we'll do useless copy */
-	if (fs->users == 1)
+	if (atomic_read(&fs->users) == 1)
 		return 0;
 
 	*new_fsp = copy_fs_struct(fs);
@@ -2242,7 +2242,7 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
 			fs = current->fs;
 			spin_lock(&fs->lock);
 			current->fs = new_fs;
-			if (--fs->users)
+			if (atomic_dec_return(&fs->users))
 				new_fs = NULL;
 			else
 				new_fs = fs;
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 86b7854..8fbc98b 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1034,7 +1034,7 @@ static int userns_install(struct nsproxy *nsproxy, struct ns_common *ns)
 	if (!thread_group_empty(current))
 		return -EINVAL;
 
-	if (current->fs->users != 1)
+	if (atomic_read(&current->fs->users) != 1)
 		return -EINVAL;
 
 	if (!ns_capable(user_ns, CAP_SYS_ADMIN))
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kernel-hardening][RFC PATCH 2/5] tty: add overflow protection to struct tty_port.count
  2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 1/5] fs: add overflow protection to struct fs_struct.users David Windsor
@ 2016-10-29 16:19 ` David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 3/5] tty: add overflow protection to struct tty_ldisc_ops.refcount David Windsor
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2016-10-29 16:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: keescook, elena.reshetova, ishkamiel, takahiro.akashi, colin, dwindsor

Change type of struct struct tty_port.count to atomic_t.  This enables
overflow protection: when CONFIG_HARDENED_ATOMIC is enabled, atomic_t
variables cannot be overflowed.

The copyright for the original PAX_REFCOUNT code:
  - all REFCOUNT code in general: PaX Team <pageexec@freemail.hu>
  - various false positive fixes: Mathias Krause <minipli@googlemail.com>
---
 arch/um/drivers/line.c                 |  2 +-
 drivers/char/pcmcia/synclink_cs.c      | 16 ++++++++--------
 drivers/isdn/gigaset/interface.c       |  8 ++++----
 drivers/isdn/i4l/isdn_tty.c            | 22 +++++++++++-----------
 drivers/net/usb/hso.c                  | 22 +++++++++++-----------
 drivers/s390/char/tty3270.c            |  2 +-
 drivers/staging/gdm724x/gdm_tty.c      |  2 +-
 drivers/tty/amiserial.c                |  4 ++--
 drivers/tty/bfin_jtag_comm.c           |  4 ++--
 drivers/tty/cyclades.c                 |  8 ++++----
 drivers/tty/hvc/hvc_console.c          | 14 +++++++-------
 drivers/tty/hvc/hvcs.c                 | 20 ++++++++++----------
 drivers/tty/hvc/hvsi.c                 | 10 +++++-----
 drivers/tty/ipwireless/tty.c           | 26 +++++++++++++-------------
 drivers/tty/moxa.c                     |  2 +-
 drivers/tty/n_gsm.c                    |  2 +-
 drivers/tty/rocket.c                   |  8 ++++----
 drivers/tty/serial/crisv10.c           | 34 +++++++++++++++++-----------------
 drivers/tty/serial/serial_core.c       |  4 ++--
 drivers/tty/synclink.c                 | 32 ++++++++++++++++----------------
 drivers/tty/synclink_gt.c              | 28 ++++++++++++++--------------
 drivers/tty/synclinkmp.c               | 34 +++++++++++++++++-----------------
 drivers/tty/tty_port.c                 | 22 +++++++++++-----------
 drivers/usb/gadget/function/u_serial.c | 22 +++++++++++-----------
 drivers/usb/serial/console.c           |  6 +++---
 include/linux/tty.h                    |  4 ++--
 net/bluetooth/rfcomm/tty.c             |  4 ++--
 net/irda/ircomm/ircomm_tty.c           | 18 +++++++++---------
 28 files changed, 190 insertions(+), 190 deletions(-)

diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 6208702..00292c8 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -377,7 +377,7 @@ int setup_one_line(struct line *lines, int n, char *init,
 	struct tty_driver *driver = line->driver->driver;
 	int err = -EINVAL;
 
-	if (line->port.count) {
+	if (atomic_read(&line->port.count)) {
 		*error_out = "Device is already open";
 		goto out;
 	}
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index d28922d..3c343d6 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -2333,7 +2333,7 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp)
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgslpc_close(%s) entry, count=%d\n",
-			 __FILE__, __LINE__, info->device_name, port->count);
+			 __FILE__, __LINE__, info->device_name, atomic_read(&port->count));
 
 	if (tty_port_close_start(port, tty, filp) == 0)
 		goto cleanup;
@@ -2351,7 +2351,7 @@ static void mgslpc_close(struct tty_struct *tty, struct file * filp)
 cleanup:
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgslpc_close(%s) exit, count=%d\n", __FILE__, __LINE__,
-			tty->driver->name, port->count);
+			tty->driver->name, atomic_read(&port->count));
 }
 
 /* Wait until the transmitter is empty.
@@ -2493,7 +2493,7 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp)
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgslpc_open(%s), old ref count = %d\n",
-			 __FILE__, __LINE__, tty->driver->name, port->count);
+			 __FILE__, __LINE__, tty->driver->name, atomic_read(&port->count));
 
 	port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
@@ -2504,11 +2504,11 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp)
 		goto cleanup;
 	}
 	spin_lock(&port->lock);
-	port->count++;
+	atomic_inc(&port->count);
 	spin_unlock(&port->lock);
 	spin_unlock_irqrestore(&info->netlock, flags);
 
-	if (port->count == 1) {
+	if (atomic_read(&port->count) == 1) {
 		/* 1st open on this device, init hardware */
 		retval = startup(info, tty);
 		if (retval < 0)
@@ -3897,7 +3897,7 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
 	unsigned short new_crctype;
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	switch (encoding)
@@ -4001,7 +4001,7 @@ static int hdlcdev_open(struct net_device *dev)
 
 	/* arbitrate between network and tty opens */
 	spin_lock_irqsave(&info->netlock, flags);
-	if (info->port.count != 0 || info->netcount != 0) {
+	if (atomic_read(&info->port.count) != 0 || info->netcount != 0) {
 		printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
 		spin_unlock_irqrestore(&info->netlock, flags);
 		return -EBUSY;
@@ -4091,7 +4091,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 		printk("%s:hdlcdev_ioctl(%s)\n", __FILE__, dev->name);
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	if (cmd != SIOCWANDEV)
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index 600c79b..3752bab 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -130,9 +130,9 @@ static int if_open(struct tty_struct *tty, struct file *filp)
 	}
 	tty->driver_data = cs;
 
-	++cs->port.count;
+	atomic_inc(&cs->port.count);
 
-	if (cs->port.count == 1) {
+	if (atomic_read(&cs->port.count) == 1) {
 		tty_port_tty_set(&cs->port, tty);
 		cs->port.low_latency = 1;
 	}
@@ -156,9 +156,9 @@ static void if_close(struct tty_struct *tty, struct file *filp)
 
 	if (!cs->connected)
 		gig_dbg(DEBUG_IF, "not connected");	/* nothing to do */
-	else if (!cs->port.count)
+	else if (!atomic_read(&cs->port.count))
 		dev_warn(cs->dev, "%s: device not opened\n", __func__);
-	else if (!--cs->port.count)
+	else if (!atomic_dec_return(&cs->port.count))
 		tty_port_tty_set(&cs->port, NULL);
 
 	mutex_unlock(&cs->mutex);
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
index 63eaa0a..00a663c 100644
--- a/drivers/isdn/i4l/isdn_tty.c
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -1499,9 +1499,9 @@ isdn_tty_open(struct tty_struct *tty, struct file *filp)
 
 #ifdef ISDN_DEBUG_MODEM_OPEN
 	printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name,
-	       port->count);
+	       atomic_read(&port->count));
 #endif
-	port->count++;
+	atomic_inc(&port->count);
 	port->tty = tty;
 	/*
 	 * Start up serial port
@@ -1545,7 +1545,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
 #endif
 		return;
 	}
-	if ((tty->count == 1) && (port->count != 1)) {
+	if ((tty->count == 1) && (atomic_read(&port->count) != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
 		 * structure will be freed.  Info->count should always
@@ -1554,15 +1554,15 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp)
 		 * serial port won't be shutdown.
 		 */
 		printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
-		       "info->count is %d\n", port->count);
-		port->count = 1;
+		       "info->count is %d\n", atomic_read(&port->count));
+		atomic_set(&port->count, 1);
 	}
-	if (--port->count < 0) {
+	if (atomic_dec_return(&port->count) < 0) {
 		printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
-		       info->line, port->count);
-		port->count = 0;
+		       info->line, atomic_read(&port->count));
+		atomic_set(&port->count, 0);
 	}
-	if (port->count) {
+	if (atomic_read(&port->count)) {
 #ifdef ISDN_DEBUG_MODEM_OPEN
 		printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
 #endif
@@ -1617,7 +1617,7 @@ isdn_tty_hangup(struct tty_struct *tty)
 	if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup"))
 		return;
 	isdn_tty_shutdown(info);
-	port->count = 0;
+	atomic_set(&port->count, 0);
 	tty_port_set_active(port, 0);
 	port->tty = NULL;
 	wake_up_interruptible(&port->open_wait);
@@ -1962,7 +1962,7 @@ isdn_tty_find_icall(int di, int ch, setup_parm *setup)
 	for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
 		modem_info *info = &dev->mdm.info[i];
 
-		if (info->port.count == 0)
+		if (atomic_read(&info->port.count) == 0)
 			continue;
 		if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) &&  /* SI1 is matching */
 		    (info->emu.mdmreg[REG_SI2] == si2))	{         /* SI2 is matching */
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index e7b5163..25e095a 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -1175,7 +1175,7 @@ static void put_rxbuf_data_and_resubmit_ctrl_urb(struct hso_serial *serial)
 	struct urb *urb;
 
 	urb = serial->rx_urb[0];
-	if (serial->port.count > 0) {
+	if (atomic_read(&serial->port.count) > 0) {
 		count = put_rxbuf_data(urb, serial);
 		if (count == -1)
 			return;
@@ -1213,7 +1213,7 @@ static void hso_std_serial_read_bulk_callback(struct urb *urb)
 	DUMP1(urb->transfer_buffer, urb->actual_length);
 
 	/* Anyone listening? */
-	if (serial->port.count == 0)
+	if (atomic_read(&serial->port.count) == 0)
 		return;
 
 	if (serial->parent->port_spec & HSO_INFO_CRC_BUG)
@@ -1274,8 +1274,8 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
 	tty_port_tty_set(&serial->port, tty);
 
 	/* check for port already opened, if not set the termios */
-	serial->port.count++;
-	if (serial->port.count == 1) {
+	atomic_inc(&serial->port.count);
+	if (atomic_read(&serial->port.count) == 1) {
 		serial->rx_state = RX_IDLE;
 		/* Force default termio settings */
 		_hso_serial_set_termios(tty, NULL);
@@ -1285,7 +1285,7 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
 		result = hso_start_serial_device(serial->parent, GFP_KERNEL);
 		if (result) {
 			hso_stop_serial_device(serial->parent);
-			serial->port.count--;
+			atomic_dec(&serial->port.count);
 		} else {
 			kref_get(&serial->parent->ref);
 		}
@@ -1323,10 +1323,10 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
 
 	/* reset the rts and dtr */
 	/* do the actual close */
-	serial->port.count--;
+	atomic_dec(&serial->port.count);
 
-	if (serial->port.count <= 0) {
-		serial->port.count = 0;
+	if (atomic_read(&serial->port.count) <= 0) {
+		atomic_read(&serial->port.count) = 0;
 		tty_port_tty_set(&serial->port, NULL);
 		if (!usb_gone)
 			hso_stop_serial_device(serial->parent);
@@ -1409,7 +1409,7 @@ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
 
 	/* the actual setup */
 	spin_lock_irqsave(&serial->serial_lock, flags);
-	if (serial->port.count)
+	if (atomic_read(&serial->port.count))
 		_hso_serial_set_termios(tty, old);
 	else
 		tty->termios = *old;
@@ -1884,7 +1884,7 @@ static void intr_callback(struct urb *urb)
 					i);
 				spin_lock(&serial->serial_lock);
 				if (serial->rx_state == RX_IDLE &&
-					serial->port.count > 0) {
+					atomic_read(&serial->port.count) > 0) {
 					/* Setup and send a ctrl req read on
 					 * port i */
 					if (!serial->rx_urb_filled[0]) {
@@ -3041,7 +3041,7 @@ static int hso_resume(struct usb_interface *iface)
 	/* Start all serial ports */
 	for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
 		if (serial_table[i] && (serial_table[i]->interface == iface)) {
-			if (dev2ser(serial_table[i])->port.count) {
+			if (atomic_read(&dev2ser(serial_table[i])->port.count)) {
 				result =
 				    hso_start_serial_device(serial_table[i], GFP_NOIO);
 				hso_kick_transmit(dev2ser(serial_table[i]));
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 272cb6c..43eaab9 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -1035,7 +1035,7 @@ tty3270_open(struct tty_struct *tty, struct file *filp)
 	struct tty3270 *tp = tty->driver_data;
 	struct tty_port *port = &tp->port;
 
-	port->count++;
+	atomic_inc(&port->count);
 	tty_port_tty_set(port, tty);
 	return 0;
 }
diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c
index ae39663..a288273 100644
--- a/drivers/staging/gdm724x/gdm_tty.c
+++ b/drivers/staging/gdm724x/gdm_tty.c
@@ -44,7 +44,7 @@
 #define gdm_tty_send_control(n, r, v, d, l) (\
 	n->tty_dev->send_control(n->tty_dev->priv_dev, r, v, d, l))
 
-#define GDM_TTY_READY(gdm) (gdm && gdm->tty_dev && gdm->port.count)
+#define GDM_TTY_READY(gdm) (gdm && gdm->tty_dev && atomic_read(&gdm->port.count))
 
 static struct tty_driver *gdm_driver[TTY_MAX_COUNT];
 static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR];
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index 208f573..32c03b7 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -1489,7 +1489,7 @@ static void rs_hangup(struct tty_struct *tty)
 
 	rs_flush_buffer(tty);
 	shutdown(tty, info);
-	info->tport.count = 0;
+	atomic_set(&info->tport.count, 0);
 	tty_port_set_active(&info->tport, 0);
 	info->tport.tty = NULL;
 	wake_up_interruptible(&info->tport.open_wait);
@@ -1507,7 +1507,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
 	struct tty_port *port = &info->tport;
 	int retval;
 
-	port->count++;
+	atomic_inc(&port->count);
 	port->tty = tty;
 	tty->driver_data = info;
 	tty->port = port;
diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c
index ce24182..5c0069e 100644
--- a/drivers/tty/bfin_jtag_comm.c
+++ b/drivers/tty/bfin_jtag_comm.c
@@ -143,7 +143,7 @@ bfin_jc_open(struct tty_struct *tty, struct file *filp)
 	unsigned long flags;
 
 	spin_lock_irqsave(&port.lock, flags);
-	port.count++;
+	atomic_inc(&port.count);
 	spin_unlock_irqrestore(&port.lock, flags);
 	tty_port_tty_set(&port, tty);
 	wake_up_process(bfin_jc_kthread);
@@ -157,7 +157,7 @@ bfin_jc_close(struct tty_struct *tty, struct file *filp)
 	bool last;
 
 	spin_lock_irqsave(&port.lock, flags);
-	last = --port.count == 0;
+	last = atomic_dec_return(&port.count) == 0;
 	spin_unlock_irqrestore(&port.lock, flags);
 	if (last)
 		tty_port_tty_set(&port, NULL);
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
index 5e4fa92..4a5b883 100644
--- a/drivers/tty/cyclades.c
+++ b/drivers/tty/cyclades.c
@@ -1566,12 +1566,12 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
 
 #ifdef CY_DEBUG_OPEN
 	printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line,
-			info->port.count);
+			atomic_read(&info->port.count));
 #endif
-	info->port.count++;
+	atomic_inc(&info->port.count);
 #ifdef CY_DEBUG_COUNT
 	printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n",
-		current->pid, info->port.count);
+		current->pid, atomic_read(&info->port.count));
 #endif
 
 	/*
@@ -3947,7 +3947,7 @@ static int cyclades_proc_show(struct seq_file *m, void *v)
 		for (j = 0; j < cy_card[i].nports; j++) {
 			info = &cy_card[i].ports[j];
 
-			if (info->port.count) {
+			if (atomic_read(&info->port.count)) {
 				/* XXX is the ldisc num worth this? */
 				struct tty_struct *tty;
 				struct tty_ldisc *ld;
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index ce86487..b5d085f 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -343,7 +343,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
 
 	spin_lock_irqsave(&hp->port.lock, flags);
 	/* Check and then increment for fast path open. */
-	if (hp->port.count++ > 0) {
+	if (atomic_inc_return(&hp->port.count) > 0) {
 		spin_unlock_irqrestore(&hp->port.lock, flags);
 		hvc_kick();
 		return 0;
@@ -398,7 +398,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
 
 	spin_lock_irqsave(&hp->port.lock, flags);
 
-	if (--hp->port.count == 0) {
+	if (atomic_dec_return(&hp->port.count) == 0) {
 		spin_unlock_irqrestore(&hp->port.lock, flags);
 		/* We are done with the tty pointer now. */
 		tty_port_tty_set(&hp->port, NULL);
@@ -420,9 +420,9 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
 		 */
 		tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
 	} else {
-		if (hp->port.count < 0)
+		if (atomic_read(&hp->port.count) < 0)
 			printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
-				hp->vtermno, hp->port.count);
+				hp->vtermno, atomic_read(&hp->port.count));
 		spin_unlock_irqrestore(&hp->port.lock, flags);
 	}
 }
@@ -452,12 +452,12 @@ static void hvc_hangup(struct tty_struct *tty)
 	 * open->hangup case this can be called after the final close so prevent
 	 * that from happening for now.
 	 */
-	if (hp->port.count <= 0) {
+	if (atomic_read(&hp->port.count) <= 0) {
 		spin_unlock_irqrestore(&hp->port.lock, flags);
 		return;
 	}
 
-	hp->port.count = 0;
+	atomic_set(&hp->port.count, 0);
 	spin_unlock_irqrestore(&hp->port.lock, flags);
 	tty_port_tty_set(&hp->port, NULL);
 
@@ -505,7 +505,7 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count
 		return -EPIPE;
 
 	/* FIXME what's this (unprotected) check for? */
-	if (hp->port.count <= 0)
+	if (atomic_read(&hp->port.count) <= 0)
 		return -EIO;
 
 	spin_lock_irqsave(&hp->lock, flags);
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index 3c4d7c2..5249438 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -416,7 +416,7 @@ static ssize_t hvcs_vterm_state_store(struct device *dev, struct device_attribut
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
 
-	if (hvcsd->port.count > 0) {
+	if (atomic_read(&hvcsd->port.count) > 0) {
 		spin_unlock_irqrestore(&hvcsd->lock, flags);
 		printk(KERN_INFO "HVCS: vterm state unchanged.  "
 				"The hvcs device node is still in use.\n");
@@ -1127,7 +1127,7 @@ static int hvcs_install(struct tty_driver *driver, struct tty_struct *tty)
 		}
 	}
 
-	hvcsd->port.count = 0;
+	atomic_set(&hvcsd->port.count, 0);
 	hvcsd->port.tty = tty;
 	tty->driver_data = hvcsd;
 
@@ -1180,7 +1180,7 @@ static int hvcs_open(struct tty_struct *tty, struct file *filp)
 	unsigned long flags;
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
-	hvcsd->port.count++;
+	atomic_inc(&hvcsd->port.count);
 	hvcsd->todo_mask |= HVCS_SCHED_READ;
 	spin_unlock_irqrestore(&hvcsd->lock, flags);
 
@@ -1216,7 +1216,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
 	hvcsd = tty->driver_data;
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
-	if (--hvcsd->port.count == 0) {
+	if (atomic_dec_and_test(&hvcsd->port.count)) {
 
 		vio_disable_interrupts(hvcsd->vdev);
 
@@ -1241,10 +1241,10 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
 
 		free_irq(irq, hvcsd);
 		return;
-	} else if (hvcsd->port.count < 0) {
+	} else if (atomic_read(&hvcsd->port.count) < 0) {
 		printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
 				" is missmanaged.\n",
-		hvcsd->vdev->unit_address, hvcsd->port.count);
+		hvcsd->vdev->unit_address, atomic_read(&hvcsd->port.count));
 	}
 
 	spin_unlock_irqrestore(&hvcsd->lock, flags);
@@ -1266,7 +1266,7 @@ static void hvcs_hangup(struct tty_struct * tty)
 
 	spin_lock_irqsave(&hvcsd->lock, flags);
 	/* Preserve this so that we know how many kref refs to put */
-	temp_open_count = hvcsd->port.count;
+	temp_open_count = atomic_read(&hvcsd->port.count);
 
 	/*
 	 * Don't kref put inside the spinlock because the destruction
@@ -1281,7 +1281,7 @@ static void hvcs_hangup(struct tty_struct * tty)
 	tty->driver_data = NULL;
 	hvcsd->port.tty = NULL;
 
-	hvcsd->port.count = 0;
+	atomic_set(&hvcsd->port.count, 0);
 
 	/* This will drop any buffered data on the floor which is OK in a hangup
 	 * scenario. */
@@ -1352,7 +1352,7 @@ static int hvcs_write(struct tty_struct *tty,
 	 * the middle of a write operation?  This is a crummy place to do this
 	 * but we want to keep it all in the spinlock.
 	 */
-	if (hvcsd->port.count <= 0) {
+	if (atomic_read(&hvcsd->port.count) <= 0) {
 		spin_unlock_irqrestore(&hvcsd->lock, flags);
 		return -ENODEV;
 	}
@@ -1426,7 +1426,7 @@ static int hvcs_write_room(struct tty_struct *tty)
 {
 	struct hvcs_struct *hvcsd = tty->driver_data;
 
-	if (!hvcsd || hvcsd->port.count <= 0)
+	if (!hvcsd || atomic_read(&hvcsd->port.count) <= 0)
 		return 0;
 
 	return HVCS_BUFF_LEN - hvcsd->chars_in_buffer;
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index c05b32e..670bdf9 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -727,7 +727,7 @@ static int hvsi_open(struct tty_struct *tty, struct file *filp)
 
 	tty_port_tty_set(&hp->port, tty);
 	spin_lock_irqsave(&hp->lock, flags);
-	hp->port.count++;
+	atomic_inc(&hp->port.count);
 	atomic_set(&hp->seqno, 0);
 	h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE);
 	spin_unlock_irqrestore(&hp->lock, flags);
@@ -784,7 +784,7 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp)
 
 	spin_lock_irqsave(&hp->lock, flags);
 
-	if (--hp->port.count == 0) {
+	if (atomic_dec_and_return(&hp->port.count) == 0) {
 		tty_port_tty_set(&hp->port, NULL);
 		hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */
 
@@ -817,9 +817,9 @@ static void hvsi_close(struct tty_struct *tty, struct file *filp)
 
 			spin_lock_irqsave(&hp->lock, flags);
 		}
-	} else if (hp->port.count < 0)
+	} else if (atomic_read(&hp->port.count) < 0)
 		printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n",
-		       hp - hvsi_ports, hp->port.count);
+		       hp - hvsi_ports, atomic_read(&hp->port.count));
 
 	spin_unlock_irqrestore(&hp->lock, flags);
 }
@@ -834,7 +834,7 @@ static void hvsi_hangup(struct tty_struct *tty)
 	tty_port_tty_set(&hp->port, NULL);
 
 	spin_lock_irqsave(&hp->lock, flags);
-	hp->port.count = 0;
+	atomic_sec(&hp->port.count, 0);
 	hp->n_outbuf = 0;
 	spin_unlock_irqrestore(&hp->lock, flags);
 }
diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c
index 2685d59..469d71d 100644
--- a/drivers/tty/ipwireless/tty.c
+++ b/drivers/tty/ipwireless/tty.c
@@ -93,10 +93,10 @@ static int ipw_open(struct tty_struct *linux_tty, struct file *filp)
 		return -ENODEV;
 
 	mutex_lock(&tty->ipw_tty_mutex);
-	if (tty->port.count == 0)
+	if (atomic_read(&tty->port.count) == 0)
 		tty->tx_bytes_queued = 0;
 
-	tty->port.count++;
+	atomic_inc(&tty->port.count);
 
 	tty->port.tty = linux_tty;
 	linux_tty->driver_data = tty;
@@ -112,9 +112,9 @@ static int ipw_open(struct tty_struct *linux_tty, struct file *filp)
 
 static void do_ipw_close(struct ipw_tty *tty)
 {
-	tty->port.count--;
+	atomic_dec(&tty->port.count);
 
-	if (tty->port.count == 0) {
+	if (atomic_read(&tty->port.count) == 0) {
 		struct tty_struct *linux_tty = tty->port.tty;
 
 		if (linux_tty != NULL) {
@@ -135,7 +135,7 @@ static void ipw_hangup(struct tty_struct *linux_tty)
 		return;
 
 	mutex_lock(&tty->ipw_tty_mutex);
-	if (tty->port.count == 0) {
+	if (atomic_read(&tty->port.count) == 0) {
 		mutex_unlock(&tty->ipw_tty_mutex);
 		return;
 	}
@@ -158,7 +158,7 @@ void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data,
 
 	mutex_lock(&tty->ipw_tty_mutex);
 
-	if (!tty->port.count) {
+	if (!atomic_read(&tty->port.count)) {
 		mutex_unlock(&tty->ipw_tty_mutex);
 		return;
 	}
@@ -197,7 +197,7 @@ static int ipw_write(struct tty_struct *linux_tty,
 		return -ENODEV;
 
 	mutex_lock(&tty->ipw_tty_mutex);
-	if (!tty->port.count) {
+	if (!atomic_read(&tty->port.count)) {
 		mutex_unlock(&tty->ipw_tty_mutex);
 		return -EINVAL;
 	}
@@ -237,7 +237,7 @@ static int ipw_write_room(struct tty_struct *linux_tty)
 	if (!tty)
 		return -ENODEV;
 
-	if (!tty->port.count)
+	if (!atomic_read(&tty->port.count))
 		return -EINVAL;
 
 	room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
@@ -270,7 +270,7 @@ static int ipw_chars_in_buffer(struct tty_struct *linux_tty)
 	if (!tty)
 		return 0;
 
-	if (!tty->port.count)
+	if (!atomic_read(&tty->port.count))
 		return 0;
 
 	return tty->tx_bytes_queued;
@@ -351,7 +351,7 @@ static int ipw_tiocmget(struct tty_struct *linux_tty)
 	if (!tty)
 		return -ENODEV;
 
-	if (!tty->port.count)
+	if (!atomic_read(&tty->port.count))
 		return -EINVAL;
 
 	return get_control_lines(tty);
@@ -367,7 +367,7 @@ ipw_tiocmset(struct tty_struct *linux_tty,
 	if (!tty)
 		return -ENODEV;
 
-	if (!tty->port.count)
+	if (!atomic_read(&tty->port.count))
 		return -EINVAL;
 
 	return set_control_lines(tty, set, clear);
@@ -381,7 +381,7 @@ static int ipw_ioctl(struct tty_struct *linux_tty,
 	if (!tty)
 		return -ENODEV;
 
-	if (!tty->port.count)
+	if (!atomic_read(&tty->port.count))
 		return -EINVAL;
 
 	/* FIXME: Exactly how is the tty object locked here .. */
@@ -537,7 +537,7 @@ void ipwireless_tty_free(struct ipw_tty *tty)
 				 * are gone */
 				mutex_lock(&ttyj->ipw_tty_mutex);
 			}
-			while (ttyj->port.count)
+			while (atomic_read(&ttyj->port.count))
 				do_ipw_close(ttyj);
 			ipwireless_disassociate_network_ttys(network,
 							     ttyj->channel_idx);
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
index 60d37b2..3d222ca 100644
--- a/drivers/tty/moxa.c
+++ b/drivers/tty/moxa.c
@@ -1188,7 +1188,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
 	}
 
 	ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
-	ch->port.count++;
+	atomic_inc(&ch->port.count);
 	tty->driver_data = ch;
 	tty_port_tty_set(&ch->port, tty);
 	mutex_lock(&ch->port.mutex);
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 54cab59..83560c9 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2943,7 +2943,7 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
 	struct gsm_dlci *dlci = tty->driver_data;
 	struct tty_port *port = &dlci->port;
 
-	port->count++;
+	atomic_inc(&port->count);
 	tty_port_tty_set(port, tty);
 
 	dlci->modem_rx = 0;
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
index b0cc47c..daeb183 100644
--- a/drivers/tty/rocket.c
+++ b/drivers/tty/rocket.c
@@ -906,7 +906,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
 	tty->driver_data = info;
 	tty_port_tty_set(port, tty);
 
-	if (port->count++ == 0) {
+	if (atomic_inc_return(&port->count) == 1) {
 		atomic_inc(&rp_num_ports_open);
 
 #ifdef ROCKET_DEBUG_OPEN
@@ -915,7 +915,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp)
 #endif
 	}
 #ifdef ROCKET_DEBUG_OPEN
-	printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, info->port.count);
+	printk(KERN_INFO "rp_open ttyR%d, count=%d\n", info->line, atomic_read(&info->port.count));
 #endif
 
 	/*
@@ -992,7 +992,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp)
 		return;
 
 #ifdef ROCKET_DEBUG_OPEN
-	printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, info->port.count);
+	printk(KERN_INFO "rp_close ttyR%d, count = %d\n", info->line, atomic_read(&info->port.count));
 #endif
 
 	if (tty_port_close_start(port, tty, filp) == 0)
@@ -1500,7 +1500,7 @@ static void rp_hangup(struct tty_struct *tty)
 #endif
 	rp_flush_buffer(tty);
 	spin_lock_irqsave(&info->port.lock, flags);
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		atomic_dec(&rp_num_ports_open);
 	clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
 	spin_unlock_irqrestore(&info->port.lock, flags);
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
index 315c849..3053ec3 100644
--- a/drivers/tty/serial/crisv10.c
+++ b/drivers/tty/serial/crisv10.c
@@ -3255,7 +3255,7 @@ set_serial_info(struct e100_serial *info,
 		goto check_and_exit;
 	}
 
-	if (info->port.count > 1)
+	if (atomic_read(&info->port.count) > 1)
 		return -EBUSY;
 
 	/*
@@ -3588,7 +3588,7 @@ rs_close(struct tty_struct *tty, struct file * filp)
 	printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
 	       info->line, info->count);
 #endif
-	if ((tty->count == 1) && (info->port.count != 1)) {
+	if ((tty->count == 1) && (atomic_read(&info->port.count) != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
 		 * structure will be freed.  Info->count should always
@@ -3598,15 +3598,15 @@ rs_close(struct tty_struct *tty, struct file * filp)
 		 */
 		printk(KERN_ERR
 		       "rs_close: bad serial port count; tty->count is 1, "
-		       "info->count is %d\n", info->port.count);
-		info->port.count = 1;
+		       "info->count is %d\n", atomic_read(&info->port.count));
+		atomic_set(&info->port.count, 1);
 	}
-	if (--info->port.count < 0) {
+	if (atomic_dec_return(&info->port.count) < 0) {
 		printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n",
-		       info->line, info->port.count);
-		info->port.count = 0;
+		       info->line, atomic_read(&info->port.count));
+		atomic_set(&info->port.count, 0);
 	}
-	if (info->port.count) {
+	if (atomic_read(&info->port.count)) {
 		local_irq_restore(flags);
 		return;
 	}
@@ -3731,7 +3731,7 @@ rs_hangup(struct tty_struct *tty)
 	rs_flush_buffer(tty);
 	shutdown(info);
 	info->event = 0;
-	info->port.count = 0;
+	atomic_set(&info->port.count, 0);
 	tty_port_set_active(&info->port, 0);
 	info->port.tty = NULL;
 	wake_up_interruptible(&info->port.open_wait);
@@ -3774,10 +3774,10 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 	add_wait_queue(&info->port.open_wait, &wait);
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready before block: ttyS%d, count = %d\n",
-	       info->line, info->port.count);
+	       info->line, atomic_read(&info->port.count));
 #endif
 	local_irq_save(flags);
-	info->port.count--;
+	atomic_dec(&info->port.count);
 	local_irq_restore(flags);
 	info->port.blocked_open++;
 	while (1) {
@@ -3807,7 +3807,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 		}
 #ifdef SERIAL_DEBUG_OPEN
 		printk("block_til_ready blocking: ttyS%d, count = %d\n",
-		       info->line, info->port.count);
+		       info->line, atomic_read(&info->port.count));
 #endif
 		tty_unlock(tty);
 		schedule();
@@ -3816,11 +3816,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->port.open_wait, &wait);
 	if (!tty_hung_up_p(filp))
-		info->port.count++;
+		atomic_inc(&info->port.count);
 	info->port.blocked_open--;
 #ifdef SERIAL_DEBUG_OPEN
 	printk("block_til_ready after blocking: ttyS%d, count = %d\n",
-	       info->line, info->port.count);
+	       info->line, atomic_inc(&info->port.count));
 #endif
 	if (retval)
 		return retval;
@@ -3858,10 +3858,10 @@ rs_open(struct tty_struct *tty, struct file * filp)
 
 #ifdef SERIAL_DEBUG_OPEN
         printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name,
- 	       info->port.count);
+ 	       atomic_read(&info->port.count));
 #endif
 
-	info->port.count++;
+	atomic_inc(&info->port.count);
 	tty->driver_data = info;
 	info->port.tty = tty;
 
@@ -3870,7 +3870,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
 	/*
 	 * If DMA is enabled try to allocate the irq's.
 	 */
-	if (info->port.count == 1) {
+	if (atomic_read(&info->port.count) == 1) {
 		allocated_resources = 1;
 		if (info->dma_in_enabled) {
 			if (request_irq(info->dma_in_irq_nbr,
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 6e4f636..2f70dc0 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1468,7 +1468,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 		state = drv->state + tty->index;
 		port = &state->port;
 		spin_lock_irq(&port->lock);
-		--port->count;
+		atomic_dec(&port->count);
 		spin_unlock_irq(&port->lock);
 		return;
 	}
@@ -1589,7 +1589,7 @@ static void uart_hangup(struct tty_struct *tty)
 		uart_flush_buffer(tty);
 		uart_shutdown(tty, state);
 		spin_lock_irqsave(&port->lock, flags);
-		port->count = 0;
+		atomic_set(&port->count, 0);
 		spin_unlock_irqrestore(&port->lock, flags);
 		tty_port_set_active(port, 0);
 		tty_port_tty_set(port, NULL);
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
index c13e27e..d61d064 100644
--- a/drivers/tty/synclink.c
+++ b/drivers/tty/synclink.c
@@ -3075,7 +3075,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
 	
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_close(%s) entry, count=%d\n",
-			 __FILE__,__LINE__, info->device_name, info->port.count);
+			 __FILE__,__LINE__, info->device_name, atomic_read(&info->port.count));
 
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 		goto cleanup;
@@ -3093,7 +3093,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp)
 cleanup:			
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__,
-			tty->driver->name, info->port.count);
+			tty->driver->name, atomic_read(&info->port.count));
 			
 }	/* end of mgsl_close() */
 
@@ -3193,7 +3193,7 @@ static void mgsl_hangup(struct tty_struct *tty)
 	mgsl_flush_buffer(tty);
 	shutdown(info);
 	
-	info->port.count = 0;	
+	atomic_set(&info->port.count, 0);	
 	tty_port_set_active(&info->port, 0);
 	info->port.tty = NULL;
 
@@ -3281,10 +3281,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):block_til_ready before block on %s count=%d\n",
-			 __FILE__,__LINE__, tty->driver->name, port->count );
+			 __FILE__,__LINE__, tty->driver->name, atomic_read(&port->count));
 
 	spin_lock_irqsave(&info->irq_spinlock, flags);
-	port->count--;
+	atomic_dec(&port->count);
 	spin_unlock_irqrestore(&info->irq_spinlock, flags);
 	port->blocked_open++;
 
@@ -3311,7 +3311,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 		
 		if (debug_level >= DEBUG_LEVEL_INFO)
 			printk("%s(%d):block_til_ready blocking on %s count=%d\n",
-				 __FILE__,__LINE__, tty->driver->name, port->count );
+				 __FILE__,__LINE__, tty->driver->name, atomic_read(&port->count));
 				 
 		tty_unlock(tty);
 		schedule();
@@ -3323,12 +3323,12 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 	
 	/* FIXME: Racy on hangup during close wait */
 	if (!tty_hung_up_p(filp))
-		port->count++;
+		atomic_inc(&port->count);
 	port->blocked_open--;
 	
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):block_til_ready after blocking on %s count=%d\n",
-			 __FILE__,__LINE__, tty->driver->name, port->count );
+			 __FILE__,__LINE__, tty->driver->name, atomic_read(&port->count));
 			 
 	if (!retval)
 		tty_port_set_active(port, 1);
@@ -3380,7 +3380,7 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp)
 		
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):mgsl_open(%s), old ref count = %d\n",
-			 __FILE__,__LINE__,tty->driver->name, info->port.count);
+			 __FILE__,__LINE__,tty->driver->name, atomic_read(&info->port.count));
 
 	info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
@@ -3390,10 +3390,10 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp)
 		spin_unlock_irqrestore(&info->netlock, flags);
 		goto cleanup;
 	}
-	info->port.count++;
+	atomic_inc(&info->port.count);
 	spin_unlock_irqrestore(&info->netlock, flags);
 
-	if (info->port.count == 1) {
+	if (atomic_read(&info->port.count) == 1) {
 		/* 1st open on this device, init hardware */
 		retval = startup(info);
 		if (retval < 0)
@@ -3417,8 +3417,8 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp)
 	if (retval) {
 		if (tty->count == 1)
 			info->port.tty = NULL; /* tty layer will release tty struct */
-		if(info->port.count)
-			info->port.count--;
+		if(atomic_read(&info->port.count))
+			atomic_dec(&info->port.count);
 	}
 	
 	return retval;
@@ -7637,7 +7637,7 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
 	unsigned short new_crctype;
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	switch (encoding)
@@ -7733,7 +7733,7 @@ static int hdlcdev_open(struct net_device *dev)
 
 	/* arbitrate between network and tty opens */
 	spin_lock_irqsave(&info->netlock, flags);
-	if (info->port.count != 0 || info->netcount != 0) {
+	if (atomic_read(&info->port.count) != 0 || info->netcount != 0) {
 		printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
 		spin_unlock_irqrestore(&info->netlock, flags);
 		return -EBUSY;
@@ -7819,7 +7819,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 		printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	if (cmd != SIOCWANDEV)
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index 7aca2d4..45a7121 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -670,7 +670,7 @@ static int open(struct tty_struct *tty, struct file *filp)
 	tty->driver_data = info;
 	info->port.tty = tty;
 
-	DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count));
+	DBGINFO(("%s open, old ref count = %d\n", info->device_name, atomic_read(&info->port.count)));
 
 	mutex_lock(&info->port.mutex);
 	info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
@@ -682,10 +682,10 @@ static int open(struct tty_struct *tty, struct file *filp)
 		mutex_unlock(&info->port.mutex);
 		goto cleanup;
 	}
-	info->port.count++;
+	atomic_inc(&info->port.count);
 	spin_unlock_irqrestore(&info->netlock, flags);
 
-	if (info->port.count == 1) {
+	if (atomic_read(&info->port.count) == 1) {
 		/* 1st open on this device, init hardware */
 		retval = startup(info);
 		if (retval < 0) {
@@ -706,8 +706,8 @@ static int open(struct tty_struct *tty, struct file *filp)
 	if (retval) {
 		if (tty->count == 1)
 			info->port.tty = NULL; /* tty layer will release tty struct */
-		if(info->port.count)
-			info->port.count--;
+		if(atomic_read(&info->port.count))
+			atomic_dec(&info->port.count);
 	}
 
 	DBGINFO(("%s open rc=%d\n", info->device_name, retval));
@@ -720,7 +720,7 @@ static void close(struct tty_struct *tty, struct file *filp)
 
 	if (sanity_check(info, tty->name, "close"))
 		return;
-	DBGINFO(("%s close entry, count=%d\n", info->device_name, info->port.count));
+	DBGINFO(("%s close entry, count=%d\n", info->device_name, atomic_read(&info->port.count)));
 
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 		goto cleanup;
@@ -737,7 +737,7 @@ static void close(struct tty_struct *tty, struct file *filp)
 	tty_port_close_end(&info->port, tty);
 	info->port.tty = NULL;
 cleanup:
-	DBGINFO(("%s close exit, count=%d\n", tty->driver->name, info->port.count));
+	DBGINFO(("%s close exit, count=%d\n", tty->driver->name, atomic_read(&info->port.count)));
 }
 
 static void hangup(struct tty_struct *tty)
@@ -755,7 +755,7 @@ static void hangup(struct tty_struct *tty)
 	shutdown(info);
 
 	spin_lock_irqsave(&info->port.lock, flags);
-	info->port.count = 0;
+	atomic_set(&info->port.count, 0);
 	info->port.tty = NULL;
 	spin_unlock_irqrestore(&info->port.lock, flags);
 	tty_port_set_active(&info->port, 0);
@@ -1435,7 +1435,7 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
 	unsigned short new_crctype;
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	DBGINFO(("%s hdlcdev_attach\n", info->device_name));
@@ -1531,7 +1531,7 @@ static int hdlcdev_open(struct net_device *dev)
 
 	/* arbitrate between network and tty opens */
 	spin_lock_irqsave(&info->netlock, flags);
-	if (info->port.count != 0 || info->netcount != 0) {
+	if (atomic_read(&info->port.count) != 0 || info->netcount != 0) {
 		DBGINFO(("%s hdlc_open busy\n", dev->name));
 		spin_unlock_irqrestore(&info->netlock, flags);
 		return -EBUSY;
@@ -1616,7 +1616,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 	DBGINFO(("%s hdlcdev_ioctl\n", dev->name));
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	if (cmd != SIOCWANDEV)
@@ -2403,7 +2403,7 @@ static irqreturn_t slgt_interrupt(int dummy, void *dev_id)
 		if (port == NULL)
 			continue;
 		spin_lock(&port->lock);
-		if ((port->port.count || port->netcount) &&
+		if ((atomic_read(&port->port.count) || port->netcount) &&
 		    port->pending_bh && !port->bh_running &&
 		    !port->bh_requested) {
 			DBGISR(("%s bh queued\n", port->device_name));
@@ -3282,7 +3282,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 	add_wait_queue(&port->open_wait, &wait);
 
 	spin_lock_irqsave(&info->lock, flags);
-	port->count--;
+	atomic_dec(&port->count);
 	spin_unlock_irqrestore(&info->lock, flags);
 	port->blocked_open++;
 
@@ -3317,7 +3317,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 	remove_wait_queue(&port->open_wait, &wait);
 
 	if (!tty_hung_up_p(filp))
-		port->count++;
+		atomic_inc(&port->count);
 	port->blocked_open--;
 
 	if (!retval)
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
index dec1565..bbf9fcc 100644
--- a/drivers/tty/synclinkmp.c
+++ b/drivers/tty/synclinkmp.c
@@ -750,7 +750,7 @@ static int open(struct tty_struct *tty, struct file *filp)
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s open(), old ref count = %d\n",
-			 __FILE__,__LINE__,tty->driver->name, info->port.count);
+			 __FILE__,__LINE__,tty->driver->name, atomic_read(&info->port.count));
 
 	info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
@@ -760,10 +760,10 @@ static int open(struct tty_struct *tty, struct file *filp)
 		spin_unlock_irqrestore(&info->netlock, flags);
 		goto cleanup;
 	}
-	info->port.count++;
+	atomic_inc(&info->port.count);
 	spin_unlock_irqrestore(&info->netlock, flags);
 
-	if (info->port.count == 1) {
+	if (atomic_read(&info->port.count) == 1) {
 		/* 1st open on this device, init hardware */
 		retval = startup(info);
 		if (retval < 0)
@@ -787,8 +787,8 @@ static int open(struct tty_struct *tty, struct file *filp)
 	if (retval) {
 		if (tty->count == 1)
 			info->port.tty = NULL; /* tty layer will release tty struct */
-		if(info->port.count)
-			info->port.count--;
+		if(atomic_read(&info->port.count))
+			atomic_dec(&info->port.count);
 	}
 
 	return retval;
@@ -806,7 +806,7 @@ static void close(struct tty_struct *tty, struct file *filp)
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s close() entry, count=%d\n",
-			 __FILE__,__LINE__, info->device_name, info->port.count);
+			 __FILE__,__LINE__, info->device_name, atomic_read(&info->port.count));
 
 	if (tty_port_close_start(&info->port, tty, filp) == 0)
 		goto cleanup;
@@ -825,7 +825,7 @@ static void close(struct tty_struct *tty, struct file *filp)
 cleanup:
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s close() exit, count=%d\n", __FILE__,__LINE__,
-			tty->driver->name, info->port.count);
+			tty->driver->name, atomic_read(&info->port.count));
 }
 
 /* Called by tty_hangup() when a hangup is signaled.
@@ -848,7 +848,7 @@ static void hangup(struct tty_struct *tty)
 	shutdown(info);
 
 	spin_lock_irqsave(&info->port.lock, flags);
-	info->port.count = 0;
+	atomic_set(&info->port.count, 0);
 	info->port.tty = NULL;
 	spin_unlock_irqrestore(&info->port.lock, flags);
 	tty_port_set_active(&info->port, 1);
@@ -1551,7 +1551,7 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
 	unsigned short new_crctype;
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	switch (encoding)
@@ -1647,7 +1647,7 @@ static int hdlcdev_open(struct net_device *dev)
 
 	/* arbitrate between network and tty opens */
 	spin_lock_irqsave(&info->netlock, flags);
-	if (info->port.count != 0 || info->netcount != 0) {
+	if (atomic_read(&info->port.count) != 0 || info->netcount != 0) {
 		printk(KERN_WARNING "%s: hdlc_open returning busy\n", dev->name);
 		spin_unlock_irqrestore(&info->netlock, flags);
 		return -EBUSY;
@@ -1733,7 +1733,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 		printk("%s:hdlcdev_ioctl(%s)\n",__FILE__,dev->name);
 
 	/* return error if TTY interface open */
-	if (info->port.count)
+	if (atomic_read(&info->port.count))
 		return -EBUSY;
 
 	if (cmd != SIOCWANDEV)
@@ -2610,7 +2610,7 @@ static irqreturn_t synclinkmp_interrupt(int dummy, void *dev_id)
 		 * do not request bottom half processing if the
 		 * device is not open in a normal mode.
 		 */
-		if ( port && (port->port.count || port->netcount) &&
+		if ( port && (atomic_read(&port->port.count) || port->netcount) &&
 		     port->pending_bh && !port->bh_running &&
 		     !port->bh_requested ) {
 			if ( debug_level >= DEBUG_LEVEL_ISR )
@@ -3300,10 +3300,10 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s block_til_ready() before block, count=%d\n",
-			 __FILE__,__LINE__, tty->driver->name, port->count );
+			 __FILE__,__LINE__, tty->driver->name, atomic_read(&port->count));
 
 	spin_lock_irqsave(&info->lock, flags);
-	port->count--;
+	atomic_dec(&port->count);
 	spin_unlock_irqrestore(&info->lock, flags);
 	port->blocked_open++;
 
@@ -3330,7 +3330,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 
 		if (debug_level >= DEBUG_LEVEL_INFO)
 			printk("%s(%d):%s block_til_ready() count=%d\n",
-				 __FILE__,__LINE__, tty->driver->name, port->count );
+				 __FILE__,__LINE__, tty->driver->name, atomic_read(&port->count));
 
 		tty_unlock(tty);
 		schedule();
@@ -3340,12 +3340,12 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&port->open_wait, &wait);
 	if (!tty_hung_up_p(filp))
-		port->count++;
+		atomic_inc(&port->count);
 	port->blocked_open--;
 
 	if (debug_level >= DEBUG_LEVEL_INFO)
 		printk("%s(%d):%s block_til_ready() after, count=%d\n",
-			 __FILE__,__LINE__, tty->driver->name, port->count );
+			 __FILE__,__LINE__, tty->driver->name, atomic_read(&port->count));
 
 	if (!retval)
 		tty_port_set_active(port, 1);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index c3f9d93..f81070c 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -236,7 +236,7 @@ void tty_port_hangup(struct tty_port *port)
 	unsigned long flags;
 
 	spin_lock_irqsave(&port->lock, flags);
-	port->count = 0;
+	atomic_set(&port->count, 0);
 	tty = port->tty;
 	if (tty)
 		set_bit(TTY_IO_ERROR, &tty->flags);
@@ -388,7 +388,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 
 	/* The port lock protects the port counts */
 	spin_lock_irqsave(&port->lock, flags);
-	port->count--;
+	atomic_dec(&port->count);
 	port->blocked_open++;
 	spin_unlock_irqrestore(&port->lock, flags);
 
@@ -429,7 +429,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 	   we must not mess that up further */
 	spin_lock_irqsave(&port->lock, flags);
 	if (!tty_hung_up_p(filp))
-		port->count++;
+		atomic_inc(&port->count);
 	port->blocked_open--;
 	spin_unlock_irqrestore(&port->lock, flags);
 	if (retval == 0)
@@ -462,18 +462,18 @@ int tty_port_close_start(struct tty_port *port,
 		return 0;
 
 	spin_lock_irqsave(&port->lock, flags);
-	if (tty->count == 1 && port->count != 1) {
+	if (tty->count == 1 && atomic_read(&port->count) != 1) {
 		tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__,
-			 port->count);
-		port->count = 1;
+			 atomic_read(&port->count));
+		atomic_set(&port->count, 1);
 	}
-	if (--port->count < 0) {
+	if (atomic_dec_return(&port->count) < 0) {
 		tty_warn(tty, "%s: bad port count (%d)\n", __func__,
-			 port->count);
-		port->count = 0;
+			 atomic_read(&port->count));
+		atomic_set(&port->count, 0);
 	}
 
-	if (port->count) {
+	if (atomic_read(&port->count)) {
 		spin_unlock_irqrestore(&port->lock, flags);
 		return 0;
 	}
@@ -567,7 +567,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
 							struct file *filp)
 {
 	spin_lock_irq(&port->lock);
-	++port->count;
+	atomic_inc(&port->count);
 	spin_unlock_irq(&port->lock);
 	tty_port_tty_set(port, tty);
 
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index e0cd1e4..0a41c55 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -752,9 +752,9 @@ static int gs_open(struct tty_struct *tty, struct file *file)
 			spin_lock_irq(&port->port_lock);
 
 			/* already open?  Great. */
-			if (port->port.count) {
+			if (atomic_read(&port->port.count)) {
 				status = 0;
-				port->port.count++;
+				atomic_inc(&port->port.count);
 
 			/* currently opening/closing? wait ... */
 			} else if (port->openclose) {
@@ -813,7 +813,7 @@ static int gs_open(struct tty_struct *tty, struct file *file)
 	tty->driver_data = port;
 	port->port.tty = tty;
 
-	port->port.count = 1;
+	atomic_set(&port->port.count, 1);
 	port->openclose = false;
 
 	/* if connected, start the I/O stream */
@@ -855,11 +855,11 @@ static void gs_close(struct tty_struct *tty, struct file *file)
 
 	spin_lock_irq(&port->port_lock);
 
-	if (port->port.count != 1) {
-		if (port->port.count == 0)
+	if (atomic_read(&port->port.count) != 1) {
+		if (atomic_read(&port->port.count) == 0)
 			WARN_ON(1);
 		else
-			--port->port.count;
+			atomic_dec(&port->port.count);
 		goto exit;
 	}
 
@@ -869,7 +869,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
 	 * and sleep if necessary
 	 */
 	port->openclose = true;
-	port->port.count = 0;
+	atomic_set(&port->port.count, 0);
 
 	gser = port->port_usb;
 	if (gser && gser->disconnect)
@@ -1324,7 +1324,7 @@ static int gs_closed(struct gs_port *port)
 	int cond;
 
 	spin_lock_irq(&port->port_lock);
-	cond = (port->port.count == 0) && !port->openclose;
+	cond = (atomic_read(&port->port.count) == 0) && !port->openclose;
 	spin_unlock_irq(&port->port_lock);
 	return cond;
 }
@@ -1469,7 +1469,7 @@ int gserial_connect(struct gserial *gser, u8 port_num)
 	/* if it's already open, start I/O ... and notify the serial
 	 * protocol about open/close status (connect/disconnect).
 	 */
-	if (port->port.count) {
+	if (atomic_read(&port->port.count)) {
 		pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
 		gs_start_io(port);
 		if (gser->connect)
@@ -1516,7 +1516,7 @@ void gserial_disconnect(struct gserial *gser)
 
 	port->port_usb = NULL;
 	gser->ioport = NULL;
-	if (port->port.count > 0 || port->openclose) {
+	if (atomic_read(&port->port.count) > 0 || port->openclose) {
 		wake_up_interruptible(&port->drain_wait);
 		if (port->port.tty)
 			tty_hangup(port->port.tty);
@@ -1529,7 +1529,7 @@ void gserial_disconnect(struct gserial *gser)
 
 	/* finally, free any unused/unusable I/O buffers */
 	spin_lock_irqsave(&port->port_lock, flags);
-	if (port->port.count == 0 && !port->openclose)
+	if (atomic_read(&port->port.count) == 0 && !port->openclose)
 		gs_buf_free(&port->port_write_buf);
 	gs_free_requests(gser->out, &port->read_pool, NULL);
 	gs_free_requests(gser->out, &port->read_queue, NULL);
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index 8967715..071c97d 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -126,7 +126,7 @@ static int usb_console_setup(struct console *co, char *options)
 
 	info->port = port;
 
-	++port->port.count;
+	atomic_inc(&port->port.count);
 	if (!tty_port_initialized(&port->port)) {
 		if (serial->type->set_termios) {
 			/*
@@ -172,7 +172,7 @@ static int usb_console_setup(struct console *co, char *options)
 	}
 	/* Now that any required fake tty operations are completed restore
 	 * the tty port count */
-	--port->port.count;
+	atomic_dec(&port->port.count);
 	/* The console is special in terms of closing the device so
 	 * indicate this port is now acting as a system console. */
 	port->port.console = 1;
@@ -184,7 +184,7 @@ static int usb_console_setup(struct console *co, char *options)
 	tty_port_tty_set(&port->port, NULL);
 	tty_kref_put(tty);
  reset_open_count:
-	port->port.count = 0;
+	atomic_set(&port->port.count, 0);
 	usb_autopm_put_interface(serial->interface);
  error_get_interface:
 	usb_serial_put(serial);
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 40144f3..16d35cf 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -225,7 +225,7 @@ struct tty_port {
 	const struct tty_port_operations *ops;	/* Port operations */
 	spinlock_t		lock;		/* Lock protecting tty field */
 	int			blocked_open;	/* Waiting to open */
-	int			count;		/* Usage count */
+	atomic_t			count;		/* Usage count */
 	wait_queue_head_t	open_wait;	/* Open waiters */
 	wait_queue_head_t	delta_msr_wait;	/* Modem status change */
 	unsigned long		flags;		/* User TTY flags ASYNC_ */
@@ -646,7 +646,7 @@ extern int tty_port_open(struct tty_port *port,
 				struct tty_struct *tty, struct file *filp);
 static inline int tty_port_users(struct tty_port *port)
 {
-	return port->count + port->blocked_open;
+	return atomic_read(&port->count) + port->blocked_open;
 }
 
 extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 8e385a0..a5bdd8e 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -752,7 +752,7 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
 	BT_DBG("tty %p id %d", tty, tty->index);
 
 	BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
-	       dev->channel, dev->port.count);
+	       dev->channel, atomic_read(&dev->port.count));
 
 	err = tty_port_open(&dev->port, tty, filp);
 	if (err)
@@ -775,7 +775,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
 	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
 
 	BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
-						dev->port.count);
+						atomic_read(&dev->port.count));
 
 	tty_port_close(&dev->port, tty, filp);
 }
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index 873c4b7..eb751e0 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -311,10 +311,10 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
 	add_wait_queue(&port->open_wait, &wait);
 
 	pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n",
-		 __FILE__, __LINE__, tty->driver->name, port->count);
+		 __FILE__, __LINE__, tty->driver->name, atomic_read(&port->count));
 
 	spin_lock_irqsave(&port->lock, flags);
-	port->count--;
+	atomic_dec(&port->count);
 	port->blocked_open++;
 	spin_unlock_irqrestore(&port->lock, flags);
 
@@ -347,7 +347,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
 		}
 
 		pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n",
-			 __FILE__, __LINE__, tty->driver->name, port->count);
+			 __FILE__, __LINE__, tty->driver->name, atomic_read(&port->count));
 
 		schedule();
 	}
@@ -357,12 +357,12 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
 
 	spin_lock_irqsave(&port->lock, flags);
 	if (!tty_hung_up_p(filp))
-		port->count++;
+		atomic_inc(&port->count);
 	port->blocked_open--;
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n",
-		 __FILE__, __LINE__, tty->driver->name, port->count);
+		 __FILE__, __LINE__, tty->driver->name, atomic_read(&port->count));
 
 	if (!retval)
 		tty_port_set_active(port, 1);
@@ -432,12 +432,12 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
 
 	/* ++ is not atomic, so this should be protected - Jean II */
 	spin_lock_irqsave(&self->port.lock, flags);
-	self->port.count++;
+	atomic_inc(&self->port.count);
 	spin_unlock_irqrestore(&self->port.lock, flags);
 	tty_port_tty_set(&self->port, tty);
 
 	pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
-		 self->line, self->port.count);
+		 self->line, atomic_read(&self->port.count));
 
 	/* Not really used by us, but lets do it anyway */
 	self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
@@ -931,7 +931,7 @@ static void ircomm_tty_hangup(struct tty_struct *tty)
 		tty_kref_put(port->tty);
 	}
 	port->tty = NULL;
-	port->count = 0;
+	atomic_set(&port->count, 0);
 	spin_unlock_irqrestore(&port->lock, flags);
 	tty_port_set_active(port, 0);
 
@@ -1275,7 +1275,7 @@ static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m)
 	seq_putc(m, '\n');
 
 	seq_printf(m, "Role: %s\n", self->client ? "client" : "server");
-	seq_printf(m, "Open count: %d\n", self->port.count);
+	seq_printf(m, "Open count: %d\n", atomic_read(&self->port.count));
 	seq_printf(m, "Max data size: %d\n", self->max_data_size);
 	seq_printf(m, "Max header size: %d\n", self->max_header_size);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kernel-hardening][RFC PATCH 3/5] tty: add overflow protection to struct tty_ldisc_ops.refcount
  2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 1/5] fs: add overflow protection to struct fs_struct.users David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 2/5] tty: add overflow protection to struct tty_port.count David Windsor
@ 2016-10-29 16:19 ` David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 4/5] fs: add overflow protection to struct pipe_inode_info.{readers|writers|files|waiting_writers} David Windsor
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2016-10-29 16:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: keescook, elena.reshetova, ishkamiel, takahiro.akashi, colin, dwindsor

Change type of struct tty_ldisc_ops.refcount to atomic_t.  This enables
overflow protection: when CONFIG_HARDENED_ATOMIC is enabled, atomic_t
variables cannot be overflowed.

The copyright for the original PAX_REFCOUNT code:
  - all REFCOUNT code in general: PaX Team <pageexec@freemail.hu>
  - various false positive fixes: Mathias Krause <minipli@googlemail.com>
---
 drivers/tty/n_tty.c       | 3 ++-
 drivers/tty/tty_ldisc.c   | 8 ++++----
 include/linux/tty_ldisc.h | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index bdf0e6e..a640ce5 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2465,7 +2465,8 @@ void n_tty_inherit_ops(struct tty_ldisc_ops *ops)
 {
 	*ops = n_tty_ops;
 	ops->owner = NULL;
-	ops->refcount = ops->flags = 0;
+	atomic_set(&ops->refcount, 0);
+	ops->flags = 0;
 }
 EXPORT_SYMBOL_GPL(n_tty_inherit_ops);
 
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 68947f6..1f85fef2 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -68,7 +68,7 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
 	raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
 	tty_ldiscs[disc] = new_ldisc;
 	new_ldisc->num = disc;
-	new_ldisc->refcount = 0;
+	atomic_set(&new_ldisc->refcount, 0);
 	raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
 
 	return ret;
@@ -96,7 +96,7 @@ int tty_unregister_ldisc(int disc)
 		return -EINVAL;
 
 	raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
-	if (tty_ldiscs[disc]->refcount)
+	if (atomic_read(&tty_ldiscs[disc]->refcount))
 		ret = -EBUSY;
 	else
 		tty_ldiscs[disc] = NULL;
@@ -117,7 +117,7 @@ static struct tty_ldisc_ops *get_ldops(int disc)
 	if (ldops) {
 		ret = ERR_PTR(-EAGAIN);
 		if (try_module_get(ldops->owner)) {
-			ldops->refcount++;
+			atomic_inc(&ldops->refcount);
 			ret = ldops;
 		}
 	}
@@ -130,7 +130,7 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
-	ldops->refcount--;
+	atomic_dec(&ldops->refcount);
 	module_put(ldops->owner);
 	raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
 }
diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h
index 3971cf0..7704c48 100644
--- a/include/linux/tty_ldisc.h
+++ b/include/linux/tty_ldisc.h
@@ -202,7 +202,7 @@ struct tty_ldisc_ops {
 
 	struct  module *owner;
 
-	int refcount;
+	atomic_t refcount;
 };
 
 struct tty_ldisc {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kernel-hardening][RFC PATCH 4/5] fs: add overflow protection to struct pipe_inode_info.{readers|writers|files|waiting_writers}
  2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
                   ` (2 preceding siblings ...)
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 3/5] tty: add overflow protection to struct tty_ldisc_ops.refcount David Windsor
@ 2016-10-29 16:19 ` David Windsor
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 5/5] mm: add overflow protection to struct kmem_cache.refcount David Windsor
  2016-10-29 21:04 ` [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection Kees Cook
  5 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2016-10-29 16:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: keescook, elena.reshetova, ishkamiel, takahiro.akashi, colin, dwindsor

Change type of struct pipe_inode_info.{readers|writers|files|waiting_writers} to atomic_t.
This enables overflow protection: when CONFIG_HARDENED_ATOMIC is enabled, atomic_t variables
cannot be overflowed.

The copyright for the original PAX_REFCOUNT code:
  - all REFCOUNT code in general: PaX Team <pageexec@freemail.hu>
  - various false positive fixes: Mathias Krause <minipli@googlemail.com>
---
 fs/coredump.c             | 10 ++++----
 fs/pipe.c                 | 59 ++++++++++++++++++++++++-----------------------
 fs/splice.c               | 36 ++++++++++++++---------------
 include/linux/pipe_fs_i.h |  8 +++----
 4 files changed, 57 insertions(+), 56 deletions(-)

diff --git a/fs/coredump.c b/fs/coredump.c
index 8d323b4..4ae7b89 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -483,8 +483,8 @@ static void wait_for_dump_helpers(struct file *file)
 	struct pipe_inode_info *pipe = file->private_data;
 
 	pipe_lock(pipe);
-	pipe->readers++;
-	pipe->writers--;
+	atomic_inc(&pipe->readers);
+	atomic_dec(&pipe->writers);
 	wake_up_interruptible_sync(&pipe->wait);
 	kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 	pipe_unlock(pipe);
@@ -493,11 +493,11 @@ static void wait_for_dump_helpers(struct file *file)
 	 * We actually want wait_event_freezable() but then we need
 	 * to clear TIF_SIGPENDING and improve dump_interrupted().
 	 */
-	wait_event_interruptible(pipe->wait, pipe->readers == 1);
+	wait_event_interruptible(pipe->wait, atomic_read(&pipe->readers) == 1);
 
 	pipe_lock(pipe);
-	pipe->readers--;
-	pipe->writers++;
+	atomic_dec(&pipe->readers);
+	atomic_inc(&pipe->writers);
 	pipe_unlock(pipe);
 }
 
diff --git a/fs/pipe.c b/fs/pipe.c
index 8e0d9f2..ceea372 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -62,7 +62,7 @@ unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;
 
 static void pipe_lock_nested(struct pipe_inode_info *pipe, int subclass)
 {
-	if (pipe->files)
+	if (atomic_read(&pipe->files))
 		mutex_lock_nested(&pipe->mutex, subclass);
 }
 
@@ -77,7 +77,7 @@ EXPORT_SYMBOL(pipe_lock);
 
 void pipe_unlock(struct pipe_inode_info *pipe)
 {
-	if (pipe->files)
+	if (atomic_read(&pipe->files))
 		mutex_unlock(&pipe->mutex);
 }
 EXPORT_SYMBOL(pipe_unlock);
@@ -310,9 +310,9 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
 		}
 		if (bufs)	/* More to do? */
 			continue;
-		if (!pipe->writers)
+		if (!atomic_read(&pipe->writers))
 			break;
-		if (!pipe->waiting_writers) {
+		if (!atomic_read(&pipe->waiting_writers)) {
 			/* syscall merging: Usually we must not sleep
 			 * if O_NONBLOCK is set, or if we got some data.
 			 * But if a writer sleeps in kernel space, then
@@ -369,7 +369,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
 
 	__pipe_lock(pipe);
 
-	if (!pipe->readers) {
+	if (!atomic_read(&pipe->readers)) {
 		send_sig(SIGPIPE, current, 0);
 		ret = -EPIPE;
 		goto out;
@@ -403,7 +403,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
 	for (;;) {
 		int bufs;
 
-		if (!pipe->readers) {
+		if (!atomic_read(&pipe->readers)) {
 			send_sig(SIGPIPE, current, 0);
 			if (!ret)
 				ret = -EPIPE;
@@ -471,9 +471,9 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
 			kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 			do_wakeup = 0;
 		}
-		pipe->waiting_writers++;
+		atomic_inc(&pipe->waiting_writers);
 		pipe_wait(pipe);
-		pipe->waiting_writers--;
+		atomic_dec(&pipe->waiting_writers);
 	}
 out:
 	__pipe_unlock(pipe);
@@ -528,7 +528,7 @@ pipe_poll(struct file *filp, poll_table *wait)
 	mask = 0;
 	if (filp->f_mode & FMODE_READ) {
 		mask = (nrbufs > 0) ? POLLIN | POLLRDNORM : 0;
-		if (!pipe->writers && filp->f_version != pipe->w_counter)
+		if (!atomic_read(&pipe->writers) && filp->f_version != pipe->w_counter)
 			mask |= POLLHUP;
 	}
 
@@ -538,7 +538,7 @@ pipe_poll(struct file *filp, poll_table *wait)
 		 * Most Unices do not set POLLERR for FIFOs but on Linux they
 		 * behave exactly like pipes for poll().
 		 */
-		if (!pipe->readers)
+		if (!atomic_read(&pipe->readers))
 			mask |= POLLERR;
 	}
 
@@ -550,7 +550,7 @@ static void put_pipe_info(struct inode *inode, struct pipe_inode_info *pipe)
 	int kill = 0;
 
 	spin_lock(&inode->i_lock);
-	if (!--pipe->files) {
+	if (atomic_dec_and_test(&pipe->files)) {
 		inode->i_pipe = NULL;
 		kill = 1;
 	}
@@ -567,11 +567,11 @@ pipe_release(struct inode *inode, struct file *file)
 
 	__pipe_lock(pipe);
 	if (file->f_mode & FMODE_READ)
-		pipe->readers--;
+		atomic_dec(&pipe->readers);
 	if (file->f_mode & FMODE_WRITE)
-		pipe->writers--;
+		atomic_dec(&pipe->writers);
 
-	if (pipe->readers || pipe->writers) {
+	if (atomic_read(&pipe->readers) || atomic_read(&pipe->writers)) {
 		wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM | POLLERR | POLLHUP);
 		kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 		kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
@@ -708,8 +708,9 @@ static struct inode * get_pipe_inode(void)
 		goto fail_iput;
 
 	inode->i_pipe = pipe;
-	pipe->files = 2;
-	pipe->readers = pipe->writers = 1;
+	atomic_set(&pipe->files, 2);
+	atomic_set(&pipe->readers, 1);
+	atomic_set(&pipe->writers, 1);
 	inode->i_fop = &pipefifo_fops;
 
 	/*
@@ -891,17 +892,17 @@ static int fifo_open(struct inode *inode, struct file *filp)
 	spin_lock(&inode->i_lock);
 	if (inode->i_pipe) {
 		pipe = inode->i_pipe;
-		pipe->files++;
+		atomic_inc(&pipe->files);
 		spin_unlock(&inode->i_lock);
 	} else {
 		spin_unlock(&inode->i_lock);
 		pipe = alloc_pipe_info();
 		if (!pipe)
 			return -ENOMEM;
-		pipe->files = 1;
+		atomic_set(&pipe->files, 1);
 		spin_lock(&inode->i_lock);
 		if (unlikely(inode->i_pipe)) {
-			inode->i_pipe->files++;
+			atomic_inc(&inode->i_pipe->files);
 			spin_unlock(&inode->i_lock);
 			free_pipe_info(pipe);
 			pipe = inode->i_pipe;
@@ -926,10 +927,10 @@ static int fifo_open(struct inode *inode, struct file *filp)
 	 *  opened, even when there is no process writing the FIFO.
 	 */
 		pipe->r_counter++;
-		if (pipe->readers++ == 0)
+		if (atomic_inc_return(&pipe->readers) == 1)
 			wake_up_partner(pipe);
 
-		if (!is_pipe && !pipe->writers) {
+		if (!is_pipe && !atomic_read(&pipe->writers)) {
 			if ((filp->f_flags & O_NONBLOCK)) {
 				/* suppress POLLHUP until we have
 				 * seen a writer */
@@ -948,14 +949,14 @@ static int fifo_open(struct inode *inode, struct file *filp)
 	 *  errno=ENXIO when there is no process reading the FIFO.
 	 */
 		ret = -ENXIO;
-		if (!is_pipe && (filp->f_flags & O_NONBLOCK) && !pipe->readers)
+		if (!is_pipe && (filp->f_flags & O_NONBLOCK) && !atomic_read(&pipe->readers))
 			goto err;
 
 		pipe->w_counter++;
-		if (!pipe->writers++)
+		if (atomic_inc_return(&pipe->writers) == 1)
 			wake_up_partner(pipe);
 
-		if (!is_pipe && !pipe->readers) {
+		if (!is_pipe && !atomic_read(&pipe->readers)) {
 			if (wait_for_partner(pipe, &pipe->r_counter))
 				goto err_wr;
 		}
@@ -969,11 +970,11 @@ static int fifo_open(struct inode *inode, struct file *filp)
 	 *  the process can at least talk to itself.
 	 */
 
-		pipe->readers++;
-		pipe->writers++;
+		atomic_read(&pipe->readers);
+		atomic_inc(&pipe->writers);
 		pipe->r_counter++;
 		pipe->w_counter++;
-		if (pipe->readers == 1 || pipe->writers == 1)
+		if (atomic_read(&pipe->readers) == 1 || atomic_read(&pipe->writers) == 1)
 			wake_up_partner(pipe);
 		break;
 
@@ -987,13 +988,13 @@ static int fifo_open(struct inode *inode, struct file *filp)
 	return 0;
 
 err_rd:
-	if (!--pipe->readers)
+	if (atomic_dec_and_test(&pipe->readers))
 		wake_up_interruptible(&pipe->wait);
 	ret = -ERESTARTSYS;
 	goto err;
 
 err_wr:
-	if (!--pipe->writers)
+	if (atomic_dec_and_test(&pipe->writers))
 		wake_up_interruptible(&pipe->wait);
 	ret = -ERESTARTSYS;
 	goto err;
diff --git a/fs/splice.c b/fs/splice.c
index 153d4f3..8376baa 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -188,7 +188,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
 	if (!spd_pages)
 		return 0;
 
-	if (unlikely(!pipe->readers)) {
+	if (unlikely(!atomic_read(&pipe->readers))) {
 		send_sig(SIGPIPE, current, 0);
 		ret = -EPIPE;
 		goto out;
@@ -227,7 +227,7 @@ ssize_t add_to_pipe(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
 {
 	int ret;
 
-	if (unlikely(!pipe->readers)) {
+	if (unlikely(!atomic_read(&pipe->readers))) {
 		send_sig(SIGPIPE, current, 0);
 		ret = -EPIPE;
 	} else if (pipe->nrbufs == pipe->buffers) {
@@ -537,7 +537,7 @@ static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_des
 			pipe_buf_release(pipe, buf);
 			pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
 			pipe->nrbufs--;
-			if (pipe->files)
+			if (atomic_read(&pipe->files))
 				sd->need_wakeup = true;
 		}
 
@@ -568,10 +568,10 @@ static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_des
 		return -ERESTARTSYS;
 
 	while (!pipe->nrbufs) {
-		if (!pipe->writers)
+		if (!atomic_read(&pipe->writers))
 			return 0;
 
-		if (!pipe->waiting_writers && sd->num_spliced)
+		if (!atomic_read(&pipe->waiting_writers) && sd->num_spliced)
 			return 0;
 
 		if (sd->flags & SPLICE_F_NONBLOCK)
@@ -785,7 +785,7 @@ iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
 				pipe_buf_release(pipe, buf);
 				pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
 				pipe->nrbufs--;
-				if (pipe->files)
+				if (atomic_read(&pipe->files))
 					sd.need_wakeup = true;
 			} else {
 				buf->offset += ret;
@@ -948,7 +948,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
 		 * out of the pipe right after the splice_to_pipe(). So set
 		 * PIPE_READERS appropriately.
 		 */
-		pipe->readers = 1;
+		atomic_set(&pipe->readers, 1);
 
 		current->splice_pipe = pipe;
 	}
@@ -1095,9 +1095,9 @@ static int wait_for_space(struct pipe_inode_info *pipe, unsigned flags)
 			return -EAGAIN;
 		if (signal_pending(current))
 			return -ERESTARTSYS;
-		pipe->waiting_writers++;
+		atomic_inc(&pipe->waiting_writers);
 		pipe_wait(pipe);
-		pipe->waiting_writers--;
+		atomic_dec(&pipe->waiting_writers);
 	}
 	return 0;
 }
@@ -1445,9 +1445,9 @@ static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
 			ret = -ERESTARTSYS;
 			break;
 		}
-		if (!pipe->writers)
+		if (!atomic_read(&pipe->writers))
 			break;
-		if (!pipe->waiting_writers) {
+		if (!atomic_read(&pipe->waiting_writers)) {
 			if (flags & SPLICE_F_NONBLOCK) {
 				ret = -EAGAIN;
 				break;
@@ -1479,7 +1479,7 @@ static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
 	pipe_lock(pipe);
 
 	while (pipe->nrbufs >= pipe->buffers) {
-		if (!pipe->readers) {
+		if (!atomic_read(&pipe->readers)) {
 			send_sig(SIGPIPE, current, 0);
 			ret = -EPIPE;
 			break;
@@ -1492,9 +1492,9 @@ static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
 			ret = -ERESTARTSYS;
 			break;
 		}
-		pipe->waiting_writers++;
+		atomic_inc(&pipe->waiting_writers);
 		pipe_wait(pipe);
-		pipe->waiting_writers--;
+		atomic_dec(&pipe->waiting_writers);
 	}
 
 	pipe_unlock(pipe);
@@ -1530,14 +1530,14 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
 	pipe_double_lock(ipipe, opipe);
 
 	do {
-		if (!opipe->readers) {
+		if (!atomic_read(&opipe->readers)) {
 			send_sig(SIGPIPE, current, 0);
 			if (!ret)
 				ret = -EPIPE;
 			break;
 		}
 
-		if (!ipipe->nrbufs && !ipipe->writers)
+		if (!ipipe->nrbufs && !atomic_read(&ipipe->writers))
 			break;
 
 		/*
@@ -1634,7 +1634,7 @@ static int link_pipe(struct pipe_inode_info *ipipe,
 	pipe_double_lock(ipipe, opipe);
 
 	do {
-		if (!opipe->readers) {
+		if (!atomic_read(&opipe->readers)) {
 			send_sig(SIGPIPE, current, 0);
 			if (!ret)
 				ret = -EPIPE;
@@ -1679,7 +1679,7 @@ static int link_pipe(struct pipe_inode_info *ipipe,
 	 * return EAGAIN if we have the potential of some data in the
 	 * future, otherwise just return 0
 	 */
-	if (!ret && ipipe->waiting_writers && (flags & SPLICE_F_NONBLOCK))
+	if (!ret && atomic_read(&ipipe->waiting_writers) && (flags & SPLICE_F_NONBLOCK))
 		ret = -EAGAIN;
 
 	pipe_unlock(ipipe);
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index e7497c9..43ebf07 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -48,10 +48,10 @@ struct pipe_inode_info {
 	struct mutex mutex;
 	wait_queue_head_t wait;
 	unsigned int nrbufs, curbuf, buffers;
-	unsigned int readers;
-	unsigned int writers;
-	unsigned int files;
-	unsigned int waiting_writers;
+	atomic_t readers;
+	atomic_t writers;
+	atomic_t files;
+	atomic_t waiting_writers;
 	unsigned int r_counter;
 	unsigned int w_counter;
 	struct page *tmp_page;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [kernel-hardening][RFC PATCH 5/5] mm: add overflow protection to struct kmem_cache.refcount
  2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
                   ` (3 preceding siblings ...)
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 4/5] fs: add overflow protection to struct pipe_inode_info.{readers|writers|files|waiting_writers} David Windsor
@ 2016-10-29 16:19 ` David Windsor
  2016-10-29 21:04 ` [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection Kees Cook
  5 siblings, 0 replies; 7+ messages in thread
From: David Windsor @ 2016-10-29 16:19 UTC (permalink / raw)
  To: kernel-hardening
  Cc: keescook, elena.reshetova, ishkamiel, takahiro.akashi, colin, dwindsor

Change type of struct kmem_cache.refcount to atomic_t.  This enables overflow
protection: when CONFIG_HARDENED_ATOMIC is enabled, atomic_t variables
cannot be overflowed.

The copyright for the original PAX_REFCOUNT code:
  - all REFCOUNT code in general: PaX Team <pageexec@freemail.hu>
  - various false positive fixes: Mathias Krause <minipli@googlemail.com>
---
 include/linux/slab_def.h |  2 +-
 include/linux/slub_def.h |  2 +-
 mm/slab.c                |  2 +-
 mm/slab.h                |  2 +-
 mm/slab_common.c         | 12 ++++++------
 mm/slub.c                | 10 +++++-----
 6 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/include/linux/slab_def.h b/include/linux/slab_def.h
index 601c69a..d018db5 100644
--- a/include/linux/slab_def.h
+++ b/include/linux/slab_def.h
@@ -40,7 +40,7 @@ struct kmem_cache {
 /* 4) cache creation/removal */
 	const char *name;
 	struct list_head list;
-	int refcount;
+	atomic_t refcount;
 	int object_size;
 	int align;
 
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index 75f56c2..32710ff 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -74,7 +74,7 @@ struct kmem_cache {
 	struct kmem_cache_order_objects max;
 	struct kmem_cache_order_objects min;
 	gfp_t allocflags;	/* gfp flags to use on each alloc */
-	int refcount;		/* Refcount for slab cache destroy */
+	atomic_t refcount;		/* Refcount for slab cache destroy */
 	void (*ctor)(void *);
 	int inuse;		/* Offset to metadata */
 	int align;		/* Alignment */
diff --git a/mm/slab.c b/mm/slab.c
index 3113caf..e0cf1b4 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -1919,7 +1919,7 @@ __kmem_cache_alias(const char *name, size_t size, size_t align,
 
 	cachep = find_mergeable(size, align, flags, name, ctor);
 	if (cachep) {
-		cachep->refcount++;
+		atomic_inc(&cachep->refcount);
 
 		/*
 		 * Adjust the object sizes so that we clear
diff --git a/mm/slab.h b/mm/slab.h
index 9653f2e..9b49151 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -22,7 +22,7 @@ struct kmem_cache {
 	unsigned int align;	/* Alignment as calculated */
 	unsigned long flags;	/* Active flags on the slab */
 	const char *name;	/* Slab name for sysfs */
-	int refcount;		/* Use counter */
+	atomic_t refcount;		/* Use counter */
 	void (*ctor)(void *);	/* Called on object slot creation */
 	struct list_head list;	/* List of all slab caches on the system */
 };
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 71f0b28..869d29f 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -244,7 +244,7 @@ int slab_unmergeable(struct kmem_cache *s)
 	/*
 	 * We may have set a slab to be unmergeable during bootstrap.
 	 */
-	if (s->refcount < 0)
+	if (atomic_read(&s->refcount) < 0)
 		return 1;
 
 	return 0;
@@ -348,7 +348,7 @@ static struct kmem_cache *create_cache(const char *name,
 	if (err)
 		goto out_free_cache;
 
-	s->refcount = 1;
+	atomic_set(&s->refcount, 1);
 	list_add(&s->list, &slab_caches);
 out:
 	if (err)
@@ -718,8 +718,8 @@ void kmem_cache_destroy(struct kmem_cache *s)
 	kasan_cache_destroy(s);
 	mutex_lock(&slab_mutex);
 
-	s->refcount--;
-	if (s->refcount)
+	atomic_dec(&s->refcount);
+	if (atomic_read(&s->refcount))
 		goto out_unlock;
 
 	err = shutdown_memcg_caches(s, &release, &need_rcu_barrier);
@@ -786,7 +786,7 @@ void __init create_boot_cache(struct kmem_cache *s, const char *name, size_t siz
 		panic("Creation of kmalloc slab %s size=%zu failed. Reason %d\n",
 					name, size, err);
 
-	s->refcount = -1;	/* Exempt from merging for now */
+	atomic_set(&s->refcount, -1);	/* Exempt from merging for now */
 }
 
 struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,
@@ -799,7 +799,7 @@ struct kmem_cache *__init create_kmalloc_cache(const char *name, size_t size,
 
 	create_boot_cache(s, name, size, flags);
 	list_add(&s->list, &slab_caches);
-	s->refcount = 1;
+	atomic_set(&s->refcount, 1);
 	return s;
 }
 
diff --git a/mm/slub.c b/mm/slub.c
index 2b3e740..d981fe3 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -4180,7 +4180,7 @@ __kmem_cache_alias(const char *name, size_t size, size_t align,
 
 	s = find_mergeable(size, align, flags, name, ctor);
 	if (s) {
-		s->refcount++;
+		atomic_inc(&s->refcount);
 
 		/*
 		 * Adjust the object sizes so that we clear
@@ -4196,7 +4196,7 @@ __kmem_cache_alias(const char *name, size_t size, size_t align,
 		}
 
 		if (sysfs_slab_alias(s, name)) {
-			s->refcount--;
+			atomic_dec(&s->refcount);
 			s = NULL;
 		}
 	}
@@ -4903,7 +4903,7 @@ SLAB_ATTR_RO(ctor);
 
 static ssize_t aliases_show(struct kmem_cache *s, char *buf)
 {
-	return sprintf(buf, "%d\n", s->refcount < 0 ? 0 : s->refcount - 1);
+	return sprintf(buf, "%d\n", atomic_read(&s->refcount) < 0 ? 0 : atomic_read(&s->refcount) - 1);
 }
 SLAB_ATTR_RO(aliases);
 
@@ -5046,7 +5046,7 @@ static ssize_t trace_store(struct kmem_cache *s, const char *buf,
 	 * as well as cause other issues like converting a mergeable
 	 * cache into an umergeable one.
 	 */
-	if (s->refcount > 1)
+	if (atomic_read(&s->refcount) > 1)
 		return -EINVAL;
 
 	s->flags &= ~SLAB_TRACE;
@@ -5164,7 +5164,7 @@ static ssize_t failslab_show(struct kmem_cache *s, char *buf)
 static ssize_t failslab_store(struct kmem_cache *s, const char *buf,
 							size_t length)
 {
-	if (s->refcount > 1)
+	if (atomic_read(&s->refcount) > 1)
 		return -EINVAL;
 
 	s->flags &= ~SLAB_FAILSLAB;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection
  2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
                   ` (4 preceding siblings ...)
  2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 5/5] mm: add overflow protection to struct kmem_cache.refcount David Windsor
@ 2016-10-29 21:04 ` Kees Cook
  5 siblings, 0 replies; 7+ messages in thread
From: Kees Cook @ 2016-10-29 21:04 UTC (permalink / raw)
  To: David Windsor
  Cc: kernel-hardening, Reshetova, Elena, Hans Liljestrand,
	AKASHI Takahiro, Colin Vidal

On Sat, Oct 29, 2016 at 9:19 AM, David Windsor <dwindsor@gmail.com> wrote:
> Expand HARDENED_ATOMIC overflow protection to cover more kernel reference
> counters.
>
> The original HARDENED_ATOMIC series adds overflow protection to existing kernel
> users of atomic_t.  This series creates 8 new users of atomic_t:
>
>  * struct fs_struct.users
>  * struct tty_port.count
>  * struct tty_ldisc_ops.refcount
>  * struct pipe_inode_info.{readers|writers|files|waiting_writers}
>  * struct kmem_cache.refcount
>
> This series changes the type of these variables to atomic_t, thus affording them
> the overflow protection provided by HARDENED_ATOMIC.

Very cool! This will be a nice addition. :)

-Kees

>
> This is based upon work done by the PaX Team [1].
>
> [1] https://forums.grsecurity.net/viewtopic.php?f=7&t=4173
>
> David Windsor (5):
>   fs: add overflow protection to struct fs_struct.users
>   tty: add overflow protection to struct tty_port.count
>   tty: add overflow protection to struct tty_ldisc_ops.refcount
>   fs: add overflow protection to struct
>     pipe_inode_info.{readers|writers|files|waiting_writers}
>   mm: add overflow protection to struct kmem_cache.refcount
>
>  arch/um/drivers/line.c                 |  2 +-
>  drivers/char/pcmcia/synclink_cs.c      | 16 ++++-----
>  drivers/isdn/gigaset/interface.c       |  8 ++---
>  drivers/isdn/i4l/isdn_tty.c            | 22 ++++++-------
>  drivers/net/usb/hso.c                  | 22 ++++++-------
>  drivers/s390/char/tty3270.c            |  2 +-
>  drivers/staging/gdm724x/gdm_tty.c      |  2 +-
>  drivers/tty/amiserial.c                |  4 +--
>  drivers/tty/bfin_jtag_comm.c           |  4 +--
>  drivers/tty/cyclades.c                 |  8 ++---
>  drivers/tty/hvc/hvc_console.c          | 14 ++++----
>  drivers/tty/hvc/hvcs.c                 | 20 ++++++------
>  drivers/tty/hvc/hvsi.c                 | 10 +++---
>  drivers/tty/ipwireless/tty.c           | 26 +++++++--------
>  drivers/tty/moxa.c                     |  2 +-
>  drivers/tty/n_gsm.c                    |  2 +-
>  drivers/tty/n_tty.c                    |  3 +-
>  drivers/tty/rocket.c                   |  8 ++---
>  drivers/tty/serial/crisv10.c           | 34 ++++++++++----------
>  drivers/tty/serial/serial_core.c       |  4 +--
>  drivers/tty/synclink.c                 | 32 +++++++++---------
>  drivers/tty/synclink_gt.c              | 28 ++++++++--------
>  drivers/tty/synclinkmp.c               | 34 ++++++++++----------
>  drivers/tty/tty_ldisc.c                |  8 ++---
>  drivers/tty/tty_port.c                 | 22 ++++++-------
>  drivers/usb/gadget/function/u_serial.c | 22 ++++++-------
>  drivers/usb/serial/console.c           |  6 ++--
>  fs/coredump.c                          | 10 +++---
>  fs/exec.c                              |  2 +-
>  fs/fs_struct.c                         |  8 ++---
>  fs/namespace.c                         |  2 +-
>  fs/pipe.c                              | 59 +++++++++++++++++-----------------
>  fs/proc/task_nommu.c                   |  2 +-
>  fs/splice.c                            | 36 ++++++++++-----------
>  include/linux/fs_struct.h              |  2 +-
>  include/linux/pipe_fs_i.h              |  8 ++---
>  include/linux/slab_def.h               |  2 +-
>  include/linux/slub_def.h               |  2 +-
>  include/linux/tty.h                    |  4 +--
>  include/linux/tty_ldisc.h              |  2 +-
>  kernel/fork.c                          |  6 ++--
>  kernel/user_namespace.c                |  2 +-
>  mm/slab.c                              |  2 +-
>  mm/slab.h                              |  2 +-
>  mm/slab_common.c                       | 12 +++----
>  mm/slub.c                              | 10 +++---
>  net/bluetooth/rfcomm/tty.c             |  4 +--
>  net/irda/ircomm/ircomm_tty.c           | 18 +++++------
>  48 files changed, 281 insertions(+), 279 deletions(-)
>
> --
> 2.7.4
>



-- 
Kees Cook
Nexus Security

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-10-29 21:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-29 16:19 [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection David Windsor
2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 1/5] fs: add overflow protection to struct fs_struct.users David Windsor
2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 2/5] tty: add overflow protection to struct tty_port.count David Windsor
2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 3/5] tty: add overflow protection to struct tty_ldisc_ops.refcount David Windsor
2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 4/5] fs: add overflow protection to struct pipe_inode_info.{readers|writers|files|waiting_writers} David Windsor
2016-10-29 16:19 ` [kernel-hardening][RFC PATCH 5/5] mm: add overflow protection to struct kmem_cache.refcount David Windsor
2016-10-29 21:04 ` [kernel-hardening][RFC PATCH 0/5] Expand HARDENED_ATOMIC overflow protection Kees Cook

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.