linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ipc: Convert kern_ipc_perm.refcount to refcount_t
@ 2017-05-27 19:59 Kees Cook
  2017-05-27 20:19 ` kbuild test robot
  2017-05-27 21:57 ` kbuild test robot
  0 siblings, 2 replies; 3+ messages in thread
From: Kees Cook @ 2017-05-27 19:59 UTC (permalink / raw)
  To: linux-kernel
  Cc: Andrew Morton, David Windsor, Kees Cook, Hans Liljestrand,
	Elena Reshetova, Davidlohr Bueso, Manfred Spraul,
	Mauro Carvalho Chehab, Bjorn Helgaas, Michal Hocko

From: Elena Reshetova <elena.reshetova@intel.com>

refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.

Cc: Manfred Spraul <manfred@colorfullife.com>
Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Hans Liljestrand <ishkamiel@gmail.com>
Signed-off-by: David Windsor <dwindsor@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 include/linux/ipc.h | 2 +-
 ipc/util.c          | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/include/linux/ipc.h b/include/linux/ipc.h
index 5591f055e13f..6502087614b4 100644
--- a/include/linux/ipc.h
+++ b/include/linux/ipc.h
@@ -22,7 +22,7 @@ struct kern_ipc_perm {
 	void		*security;
 
 	struct rcu_head rcu;
-	atomic_t refcount;
+	refcount_t	refcount;
 } ____cacheline_aligned_in_smp;
 
 #endif /* _LINUX_IPC_H */
diff --git a/ipc/util.c b/ipc/util.c
index 1a2cb02467ab..069bb22c9f64 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -232,7 +232,7 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
 
 	idr_preload(GFP_KERNEL);
 
-	atomic_set(&new->refcount, 1);
+	refcount_set(&new->refcount, 1);
 	spin_lock_init(&new->lock);
 	new->deleted = false;
 	rcu_read_lock();
@@ -397,13 +397,13 @@ void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
 
 int ipc_rcu_getref(struct kern_ipc_perm *ptr)
 {
-	return atomic_inc_not_zero(&ptr->refcount);
+	return refcount_inc_not_zero(&ptr->refcount);
 }
 
 void ipc_rcu_putref(struct kern_ipc_perm *ptr,
 			void (*func)(struct rcu_head *head))
 {
-	if (!atomic_dec_and_test(&ptr->refcount))
+	if (!refcount_dec_and_test(&ptr->refcount))
 		return;
 
 	call_rcu(&ptr->rcu, func);
-- 
2.7.4


-- 
Kees Cook
Pixel Security

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

* Re: [PATCH] ipc: Convert kern_ipc_perm.refcount to refcount_t
  2017-05-27 19:59 [PATCH] ipc: Convert kern_ipc_perm.refcount to refcount_t Kees Cook
@ 2017-05-27 20:19 ` kbuild test robot
  2017-05-27 21:57 ` kbuild test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kbuild test robot @ 2017-05-27 20:19 UTC (permalink / raw)
  To: Kees Cook
  Cc: kbuild-all, linux-kernel, Andrew Morton, David Windsor,
	Kees Cook, Hans Liljestrand, Elena Reshetova, Davidlohr Bueso,
	Manfred Spraul, Mauro Carvalho Chehab, Bjorn Helgaas,
	Michal Hocko

[-- Attachment #1: Type: text/plain, Size: 1732 bytes --]

Hi Elena,

[auto build test ERROR on next-20170526]
[cannot apply to linus/master linux/master kees/for-next/pstore v4.9-rc8 v4.9-rc7 v4.9-rc6 v4.12-rc2]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Kees-Cook/ipc-Convert-kern_ipc_perm-refcount-to-refcount_t/20170528-040601
config: x86_64-randconfig-x019-201722 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   In file included from include/uapi/linux/sem.h:4:0,
                    from include/linux/sem.h:7,
                    from include/linux/sched.h:14,
                    from include/linux/uaccess.h:4,
                    from include/linux/crypto.h:26,
                    from arch/x86/kernel/asm-offsets.c:8:
>> include/linux/ipc.h:25:2: error: unknown type name 'refcount_t'
     refcount_t refcount;
     ^~~~~~~~~~
   make[2]: *** [arch/x86/kernel/asm-offsets.s] Error 1
   make[2]: Target '__build' not remade because of errors.
   make[1]: *** [prepare0] Error 2
   make[1]: Target 'prepare' not remade because of errors.
   make: *** [sub-make] Error 2

vim +/refcount_t +25 include/linux/ipc.h

    19		kgid_t		cgid;
    20		umode_t		mode;
    21		unsigned long	seq;
    22		void		*security;
    23	
    24		struct rcu_head rcu;
  > 25		refcount_t	refcount;
    26	} ____cacheline_aligned_in_smp;
    27	
    28	#endif /* _LINUX_IPC_H */

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 23389 bytes --]

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

* Re: [PATCH] ipc: Convert kern_ipc_perm.refcount to refcount_t
  2017-05-27 19:59 [PATCH] ipc: Convert kern_ipc_perm.refcount to refcount_t Kees Cook
  2017-05-27 20:19 ` kbuild test robot
@ 2017-05-27 21:57 ` kbuild test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kbuild test robot @ 2017-05-27 21:57 UTC (permalink / raw)
  To: Kees Cook
  Cc: kbuild-all, linux-kernel, Andrew Morton, David Windsor,
	Kees Cook, Hans Liljestrand, Elena Reshetova, Davidlohr Bueso,
	Manfred Spraul, Mauro Carvalho Chehab, Bjorn Helgaas,
	Michal Hocko

[-- Attachment #1: Type: text/plain, Size: 14292 bytes --]

Hi Elena,

[auto build test ERROR on next-20170526]
[cannot apply to linus/master linux/master kees/for-next/pstore v4.9-rc8 v4.9-rc7 v4.9-rc6 v4.12-rc2]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Kees-Cook/ipc-Convert-kern_ipc_perm-refcount-to-refcount_t/20170528-040601
config: m32r-m32104ut_defconfig (attached as .config)
compiler: m32r-linux-gcc (GCC) 6.2.0
reproduce:
        wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=m32r 

All errors (new ones prefixed by >>):

   In file included from include/uapi/linux/shm.h:4:0,
                    from include/linux/shm.h:6,
                    from ipc/util.c:44:
   include/linux/ipc.h:25:2: error: unknown type name 'refcount_t'
     refcount_t refcount;
     ^~~~~~~~~~
   ipc/util.c: In function 'ipc_addid':
>> ipc/util.c:235:15: error: passing argument 1 of 'refcount_set' from incompatible pointer type [-Werror=incompatible-pointer-types]
     refcount_set(&new->refcount, 1);
                  ^
   In file included from include/linux/key.h:26:0,
                    from include/linux/security.h:26,
                    from ipc/util.c:52:
   include/linux/refcount.h:28:20: note: expected 'refcount_t * {aka struct refcount_struct *}' but argument is of type 'int *'
    static inline void refcount_set(refcount_t *r, unsigned int n)
                       ^~~~~~~~~~~~
   ipc/util.c: In function 'ipc_rcu_getref':
>> ipc/util.c:400:31: error: passing argument 1 of 'refcount_inc_not_zero' from incompatible pointer type [-Werror=incompatible-pointer-types]
     return refcount_inc_not_zero(&ptr->refcount);
                                  ^
   In file included from include/linux/key.h:26:0,
                    from include/linux/security.h:26,
                    from ipc/util.c:52:
   include/linux/refcount.h:47:26: note: expected 'refcount_t * {aka struct refcount_struct *}' but argument is of type 'int *'
    extern __must_check bool refcount_inc_not_zero(refcount_t *r);
                             ^~~~~~~~~~~~~~~~~~~~~
   ipc/util.c: In function 'ipc_rcu_putref':
>> ipc/util.c:406:29: error: passing argument 1 of 'refcount_dec_and_test' from incompatible pointer type [-Werror=incompatible-pointer-types]
     if (!refcount_dec_and_test(&ptr->refcount))
                                ^
   In file included from include/linux/key.h:26:0,
                    from include/linux/security.h:26,
                    from ipc/util.c:52:
   include/linux/refcount.h:53:26: note: expected 'refcount_t * {aka struct refcount_struct *}' but argument is of type 'int *'
    extern __must_check bool refcount_dec_and_test(refcount_t *r);
                             ^~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +/refcount_set +235 ipc/util.c

    38	 *
    39	 *  Note that sems have a special fast path that avoids kern_ipc_perm.lock -
    40	 *  see sem_lock().
    41	 */
    42	
    43	#include <linux/mm.h>
  > 44	#include <linux/shm.h>
    45	#include <linux/init.h>
    46	#include <linux/msg.h>
    47	#include <linux/vmalloc.h>
    48	#include <linux/slab.h>
    49	#include <linux/notifier.h>
    50	#include <linux/capability.h>
    51	#include <linux/highuid.h>
  > 52	#include <linux/security.h>
    53	#include <linux/rcupdate.h>
    54	#include <linux/workqueue.h>
    55	#include <linux/seq_file.h>
    56	#include <linux/proc_fs.h>
    57	#include <linux/audit.h>
    58	#include <linux/nsproxy.h>
    59	#include <linux/rwsem.h>
    60	#include <linux/memory.h>
    61	#include <linux/ipc_namespace.h>
    62	
    63	#include <asm/unistd.h>
    64	
    65	#include "util.h"
    66	
    67	struct ipc_proc_iface {
    68		const char *path;
    69		const char *header;
    70		int ids;
    71		int (*show)(struct seq_file *, void *);
    72	};
    73	
    74	/**
    75	 * ipc_init - initialise ipc subsystem
    76	 *
    77	 * The various sysv ipc resources (semaphores, messages and shared
    78	 * memory) are initialised.
    79	 *
    80	 * A callback routine is registered into the memory hotplug notifier
    81	 * chain: since msgmni scales to lowmem this callback routine will be
    82	 * called upon successful memory add / remove to recompute msmgni.
    83	 */
    84	static int __init ipc_init(void)
    85	{
    86		sem_init();
    87		msg_init();
    88		shm_init();
    89		return 0;
    90	}
    91	device_initcall(ipc_init);
    92	
    93	/**
    94	 * ipc_init_ids	- initialise ipc identifiers
    95	 * @ids: ipc identifier set
    96	 *
    97	 * Set up the sequence range to use for the ipc identifier range (limited
    98	 * below IPCMNI) then initialise the ids idr.
    99	 */
   100	void ipc_init_ids(struct ipc_ids *ids)
   101	{
   102		ids->in_use = 0;
   103		ids->seq = 0;
   104		ids->next_id = -1;
   105		init_rwsem(&ids->rwsem);
   106		idr_init(&ids->ipcs_idr);
   107	}
   108	
   109	#ifdef CONFIG_PROC_FS
   110	static const struct file_operations sysvipc_proc_fops;
   111	/**
   112	 * ipc_init_proc_interface -  create a proc interface for sysipc types using a seq_file interface.
   113	 * @path: Path in procfs
   114	 * @header: Banner to be printed at the beginning of the file.
   115	 * @ids: ipc id table to iterate.
   116	 * @show: show routine.
   117	 */
   118	void __init ipc_init_proc_interface(const char *path, const char *header,
   119			int ids, int (*show)(struct seq_file *, void *))
   120	{
   121		struct proc_dir_entry *pde;
   122		struct ipc_proc_iface *iface;
   123	
   124		iface = kmalloc(sizeof(*iface), GFP_KERNEL);
   125		if (!iface)
   126			return;
   127		iface->path	= path;
   128		iface->header	= header;
   129		iface->ids	= ids;
   130		iface->show	= show;
   131	
   132		pde = proc_create_data(path,
   133				       S_IRUGO,        /* world readable */
   134				       NULL,           /* parent dir */
   135				       &sysvipc_proc_fops,
   136				       iface);
   137		if (!pde)
   138			kfree(iface);
   139	}
   140	#endif
   141	
   142	/**
   143	 * ipc_findkey	- find a key in an ipc identifier set
   144	 * @ids: ipc identifier set
   145	 * @key: key to find
   146	 *
   147	 * Returns the locked pointer to the ipc structure if found or NULL
   148	 * otherwise. If key is found ipc points to the owning ipc structure
   149	 *
   150	 * Called with ipc_ids.rwsem held.
   151	 */
   152	static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
   153	{
   154		struct kern_ipc_perm *ipc;
   155		int next_id;
   156		int total;
   157	
   158		for (total = 0, next_id = 0; total < ids->in_use; next_id++) {
   159			ipc = idr_find(&ids->ipcs_idr, next_id);
   160	
   161			if (ipc == NULL)
   162				continue;
   163	
   164			if (ipc->key != key) {
   165				total++;
   166				continue;
   167			}
   168	
   169			rcu_read_lock();
   170			ipc_lock_object(ipc);
   171			return ipc;
   172		}
   173	
   174		return NULL;
   175	}
   176	
   177	/**
   178	 * ipc_get_maxid - get the last assigned id
   179	 * @ids: ipc identifier set
   180	 *
   181	 * Called with ipc_ids.rwsem held.
   182	 */
   183	int ipc_get_maxid(struct ipc_ids *ids)
   184	{
   185		struct kern_ipc_perm *ipc;
   186		int max_id = -1;
   187		int total, id;
   188	
   189		if (ids->in_use == 0)
   190			return -1;
   191	
   192		if (ids->in_use == IPCMNI)
   193			return IPCMNI - 1;
   194	
   195		/* Look for the last assigned id */
   196		total = 0;
   197		for (id = 0; id < IPCMNI && total < ids->in_use; id++) {
   198			ipc = idr_find(&ids->ipcs_idr, id);
   199			if (ipc != NULL) {
   200				max_id = id;
   201				total++;
   202			}
   203		}
   204		return max_id;
   205	}
   206	
   207	/**
   208	 * ipc_addid - add an ipc identifier
   209	 * @ids: ipc identifier set
   210	 * @new: new ipc permission set
   211	 * @size: limit for the number of used ids
   212	 *
   213	 * Add an entry 'new' to the ipc ids idr. The permissions object is
   214	 * initialised and the first free entry is set up and the id assigned
   215	 * is returned. The 'new' entry is returned in a locked state on success.
   216	 * On failure the entry is not locked and a negative err-code is returned.
   217	 *
   218	 * Called with writer ipc_ids.rwsem held.
   219	 */
   220	int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
   221	{
   222		kuid_t euid;
   223		kgid_t egid;
   224		int id;
   225		int next_id = ids->next_id;
   226	
   227		if (size > IPCMNI)
   228			size = IPCMNI;
   229	
   230		if (ids->in_use >= size)
   231			return -ENOSPC;
   232	
   233		idr_preload(GFP_KERNEL);
   234	
 > 235		refcount_set(&new->refcount, 1);
   236		spin_lock_init(&new->lock);
   237		new->deleted = false;
   238		rcu_read_lock();
   239		spin_lock(&new->lock);
   240	
   241		current_euid_egid(&euid, &egid);
   242		new->cuid = new->uid = euid;
   243		new->gid = new->cgid = egid;
   244	
   245		id = idr_alloc(&ids->ipcs_idr, new,
   246			       (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
   247			       GFP_NOWAIT);
   248		idr_preload_end();
   249		if (id < 0) {
   250			spin_unlock(&new->lock);
   251			rcu_read_unlock();
   252			return id;
   253		}
   254	
   255		ids->in_use++;
   256	
   257		if (next_id < 0) {
   258			new->seq = ids->seq++;
   259			if (ids->seq > IPCID_SEQ_MAX)
   260				ids->seq = 0;
   261		} else {
   262			new->seq = ipcid_to_seqx(next_id);
   263			ids->next_id = -1;
   264		}
   265	
   266		new->id = ipc_buildid(id, new->seq);
   267		return id;
   268	}
   269	
   270	/**
   271	 * ipcget_new -	create a new ipc object
   272	 * @ns: ipc namespace
   273	 * @ids: ipc identifier set
   274	 * @ops: the actual creation routine to call
   275	 * @params: its parameters
   276	 *
   277	 * This routine is called by sys_msgget, sys_semget() and sys_shmget()
   278	 * when the key is IPC_PRIVATE.
   279	 */
   280	static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
   281			const struct ipc_ops *ops, struct ipc_params *params)
   282	{
   283		int err;
   284	
   285		down_write(&ids->rwsem);
   286		err = ops->getnew(ns, params);
   287		up_write(&ids->rwsem);
   288		return err;
   289	}
   290	
   291	/**
   292	 * ipc_check_perms - check security and permissions for an ipc object
   293	 * @ns: ipc namespace
   294	 * @ipcp: ipc permission set
   295	 * @ops: the actual security routine to call
   296	 * @params: its parameters
   297	 *
   298	 * This routine is called by sys_msgget(), sys_semget() and sys_shmget()
   299	 * when the key is not IPC_PRIVATE and that key already exists in the
   300	 * ds IDR.
   301	 *
   302	 * On success, the ipc id is returned.
   303	 *
   304	 * It is called with ipc_ids.rwsem and ipcp->lock held.
   305	 */
   306	static int ipc_check_perms(struct ipc_namespace *ns,
   307				   struct kern_ipc_perm *ipcp,
   308				   const struct ipc_ops *ops,
   309				   struct ipc_params *params)
   310	{
   311		int err;
   312	
   313		if (ipcperms(ns, ipcp, params->flg))
   314			err = -EACCES;
   315		else {
   316			err = ops->associate(ipcp, params->flg);
   317			if (!err)
   318				err = ipcp->id;
   319		}
   320	
   321		return err;
   322	}
   323	
   324	/**
   325	 * ipcget_public - get an ipc object or create a new one
   326	 * @ns: ipc namespace
   327	 * @ids: ipc identifier set
   328	 * @ops: the actual creation routine to call
   329	 * @params: its parameters
   330	 *
   331	 * This routine is called by sys_msgget, sys_semget() and sys_shmget()
   332	 * when the key is not IPC_PRIVATE.
   333	 * It adds a new entry if the key is not found and does some permission
   334	 * / security checkings if the key is found.
   335	 *
   336	 * On success, the ipc id is returned.
   337	 */
   338	static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
   339			const struct ipc_ops *ops, struct ipc_params *params)
   340	{
   341		struct kern_ipc_perm *ipcp;
   342		int flg = params->flg;
   343		int err;
   344	
   345		/*
   346		 * Take the lock as a writer since we are potentially going to add
   347		 * a new entry + read locks are not "upgradable"
   348		 */
   349		down_write(&ids->rwsem);
   350		ipcp = ipc_findkey(ids, params->key);
   351		if (ipcp == NULL) {
   352			/* key not used */
   353			if (!(flg & IPC_CREAT))
   354				err = -ENOENT;
   355			else
   356				err = ops->getnew(ns, params);
   357		} else {
   358			/* ipc object has been locked by ipc_findkey() */
   359	
   360			if (flg & IPC_CREAT && flg & IPC_EXCL)
   361				err = -EEXIST;
   362			else {
   363				err = 0;
   364				if (ops->more_checks)
   365					err = ops->more_checks(ipcp, params);
   366				if (!err)
   367					/*
   368					 * ipc_check_perms returns the IPC id on
   369					 * success
   370					 */
   371					err = ipc_check_perms(ns, ipcp, ops, params);
   372			}
   373			ipc_unlock(ipcp);
   374		}
   375		up_write(&ids->rwsem);
   376	
   377		return err;
   378	}
   379	
   380	
   381	/**
   382	 * ipc_rmid - remove an ipc identifier
   383	 * @ids: ipc identifier set
   384	 * @ipcp: ipc perm structure containing the identifier to remove
   385	 *
   386	 * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held
   387	 * before this function is called, and remain locked on the exit.
   388	 */
   389	void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp)
   390	{
   391		int lid = ipcid_to_idx(ipcp->id);
   392	
   393		idr_remove(&ids->ipcs_idr, lid);
   394		ids->in_use--;
   395		ipcp->deleted = true;
   396	}
   397	
   398	int ipc_rcu_getref(struct kern_ipc_perm *ptr)
   399	{
 > 400		return refcount_inc_not_zero(&ptr->refcount);
   401	}
   402	
   403	void ipc_rcu_putref(struct kern_ipc_perm *ptr,
   404				void (*func)(struct rcu_head *head))
   405	{
 > 406		if (!refcount_dec_and_test(&ptr->refcount))
   407			return;
   408	
   409		call_rcu(&ptr->rcu, func);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 11266 bytes --]

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

end of thread, other threads:[~2017-05-27 21:57 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-27 19:59 [PATCH] ipc: Convert kern_ipc_perm.refcount to refcount_t Kees Cook
2017-05-27 20:19 ` kbuild test robot
2017-05-27 21:57 ` kbuild test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).