Skip to content

Commit

Permalink
fix idr_find() locking
Browse files Browse the repository at this point in the history
This is a patch that fixes the way idr_find() used to be called in ipc_lock():
in all the paths that don't imply an update of the ipcs idr, it was called
without the idr tree being locked.

The changes are:
  . in ipc_ids, the mutex has been changed into a reader/writer semaphore.
  . ipc_lock() now takes the mutex as a reader during the idr_find().
  . a new routine ipc_lock_down() has been defined: it doesn't take the
    mutex, assuming that it is being held by the caller. This is the routine
    that is now called in all the update paths.

Signed-off-by: Nadia Derbey <[email protected]>
Acked-by: Jarek Poplawski <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Nadia Derbey authored and Linus Torvalds committed Oct 19, 2007
1 parent f4566f0 commit 3e148c7
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 70 deletions.
41 changes: 29 additions & 12 deletions ipc/msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#include <linux/syscalls.h>
#include <linux/audit.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/nsproxy.h>

#include <asm/current.h>
Expand Down Expand Up @@ -110,7 +110,7 @@ void msg_exit_ns(struct ipc_namespace *ns)
int next_id;
int total, in_use;

mutex_lock(&msg_ids(ns).mutex);
down_write(&msg_ids(ns).rw_mutex);

in_use = msg_ids(ns).in_use;

Expand All @@ -122,7 +122,8 @@ void msg_exit_ns(struct ipc_namespace *ns)
freeque(ns, msq);
total++;
}
mutex_unlock(&msg_ids(ns).mutex);

up_write(&msg_ids(ns).rw_mutex);

kfree(ns->ids[IPC_MSG_IDS]);
ns->ids[IPC_MSG_IDS] = NULL;
Expand All @@ -136,6 +137,22 @@ void __init msg_init(void)
IPC_MSG_IDS, sysvipc_msg_proc_show);
}

/*
* This routine is called in the paths where the rw_mutex is held to protect
* access to the idr tree.
*/
static inline struct msg_queue *msg_lock_check_down(struct ipc_namespace *ns,
int id)
{
struct kern_ipc_perm *ipcp = ipc_lock_check_down(&msg_ids(ns), id);

return container_of(ipcp, struct msg_queue, q_perm);
}

/*
* msg_lock_(check_) routines are called in the paths where the rw_mutex
* is not held.
*/
static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id)
{
struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id);
Expand All @@ -161,7 +178,7 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
* @ns: namespace
* @params: ptr to the structure that contains the key and msgflg
*
* Called with msg_ids.mutex held
* Called with msg_ids.rw_mutex held (writer)
*/
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
Expand Down Expand Up @@ -260,8 +277,8 @@ static void expunge_all(struct msg_queue *msq, int res)
* removes the message queue from message queue ID IDR, and cleans up all the
* messages associated with this queue.
*
* msg_ids.mutex and the spinlock for this message queue are held
* before freeque() is called. msg_ids.mutex remains locked on exit.
* msg_ids.rw_mutex (writer) and the spinlock for this message queue are held
* before freeque() is called. msg_ids.rw_mutex remains locked on exit.
*/
static void freeque(struct ipc_namespace *ns, struct msg_queue *msq)
{
Expand All @@ -286,7 +303,7 @@ static void freeque(struct ipc_namespace *ns, struct msg_queue *msq)
}

/*
* Called with msg_ids.mutex and ipcp locked.
* Called with msg_ids.rw_mutex and ipcp locked.
*/
static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg)
{
Expand Down Expand Up @@ -444,7 +461,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
msginfo.msgmnb = ns->msg_ctlmnb;
msginfo.msgssz = MSGSSZ;
msginfo.msgseg = MSGSEG;
mutex_lock(&msg_ids(ns).mutex);
down_read(&msg_ids(ns).rw_mutex);
if (cmd == MSG_INFO) {
msginfo.msgpool = msg_ids(ns).in_use;
msginfo.msgmap = atomic_read(&msg_hdrs);
Expand All @@ -455,7 +472,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
msginfo.msgtql = MSGTQL;
}
max_id = ipc_get_maxid(&msg_ids(ns));
mutex_unlock(&msg_ids(ns).mutex);
up_read(&msg_ids(ns).rw_mutex);
if (copy_to_user(buf, &msginfo, sizeof(struct msginfo)))
return -EFAULT;
return (max_id < 0) ? 0 : max_id;
Expand Down Expand Up @@ -516,8 +533,8 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
return -EINVAL;
}

mutex_lock(&msg_ids(ns).mutex);
msq = msg_lock_check(ns, msqid);
down_write(&msg_ids(ns).rw_mutex);
msq = msg_lock_check_down(ns, msqid);
if (IS_ERR(msq)) {
err = PTR_ERR(msq);
goto out_up;
Expand Down Expand Up @@ -576,7 +593,7 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
}
err = 0;
out_up:
mutex_unlock(&msg_ids(ns).mutex);
up_write(&msg_ids(ns).rw_mutex);
return err;
out_unlock_up:
msg_unlock(msq);
Expand Down
44 changes: 30 additions & 14 deletions ipc/sem.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/nsproxy.h>

#include <asm/uaccess.h>
Expand Down Expand Up @@ -148,7 +148,7 @@ void sem_exit_ns(struct ipc_namespace *ns)
int next_id;
int total, in_use;

mutex_lock(&sem_ids(ns).mutex);
down_write(&sem_ids(ns).rw_mutex);

in_use = sem_ids(ns).in_use;

Expand All @@ -160,7 +160,7 @@ void sem_exit_ns(struct ipc_namespace *ns)
freeary(ns, sma);
total++;
}
mutex_unlock(&sem_ids(ns).mutex);
up_write(&sem_ids(ns).rw_mutex);

kfree(ns->ids[IPC_SEM_IDS]);
ns->ids[IPC_SEM_IDS] = NULL;
Expand All @@ -174,6 +174,22 @@ void __init sem_init (void)
IPC_SEM_IDS, sysvipc_sem_proc_show);
}

/*
* This routine is called in the paths where the rw_mutex is held to protect
* access to the idr tree.
*/
static inline struct sem_array *sem_lock_check_down(struct ipc_namespace *ns,
int id)
{
struct kern_ipc_perm *ipcp = ipc_lock_check_down(&sem_ids(ns), id);

return container_of(ipcp, struct sem_array, sem_perm);
}

/*
* sem_lock_(check_) routines are called in the paths where the rw_mutex
* is not held.
*/
static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id)
{
struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id);
Expand Down Expand Up @@ -233,7 +249,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
* @ns: namespace
* @params: ptr to the structure that contains key, semflg and nsems
*
* Called with sem_ids.mutex held
* Called with sem_ids.rw_mutex held (as a writer)
*/

static int newary(struct ipc_namespace *ns, struct ipc_params *params)
Expand Down Expand Up @@ -290,7 +306,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)


/*
* Called with sem_ids.mutex and ipcp locked.
* Called with sem_ids.rw_mutex and ipcp locked.
*/
static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
{
Expand All @@ -301,7 +317,7 @@ static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg)
}

/*
* Called with sem_ids.mutex and ipcp locked.
* Called with sem_ids.rw_mutex and ipcp locked.
*/
static inline int sem_more_checks(struct kern_ipc_perm *ipcp,
struct ipc_params *params)
Expand Down Expand Up @@ -528,9 +544,9 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum)
return semzcnt;
}

/* Free a semaphore set. freeary() is called with sem_ids.mutex locked and
* the spinlock for this semaphore set hold. sem_ids.mutex remains locked
* on exit.
/* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked
* as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex
* remains locked on exit.
*/
static void freeary(struct ipc_namespace *ns, struct sem_array *sma)
{
Expand Down Expand Up @@ -615,7 +631,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
seminfo.semmnu = SEMMNU;
seminfo.semmap = SEMMAP;
seminfo.semume = SEMUME;
mutex_lock(&sem_ids(ns).mutex);
down_read(&sem_ids(ns).rw_mutex);
if (cmd == SEM_INFO) {
seminfo.semusz = sem_ids(ns).in_use;
seminfo.semaem = ns->used_sems;
Expand All @@ -624,7 +640,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, int semnum,
seminfo.semaem = SEMAEM;
}
max_id = ipc_get_maxid(&sem_ids(ns));
mutex_unlock(&sem_ids(ns).mutex);
up_read(&sem_ids(ns).rw_mutex);
if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo)))
return -EFAULT;
return (max_id < 0) ? 0: max_id;
Expand Down Expand Up @@ -895,7 +911,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, int semnum,
if(copy_semid_from_user (&setbuf, arg.buf, version))
return -EFAULT;
}
sma = sem_lock_check(ns, semid);
sma = sem_lock_check_down(ns, semid);
if (IS_ERR(sma))
return PTR_ERR(sma);

Expand Down Expand Up @@ -976,9 +992,9 @@ asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
return err;
case IPC_RMID:
case IPC_SET:
mutex_lock(&sem_ids(ns).mutex);
down_write(&sem_ids(ns).rw_mutex);
err = semctl_down(ns,semid,semnum,cmd,version,arg);
mutex_unlock(&sem_ids(ns).mutex);
up_write(&sem_ids(ns).rw_mutex);
return err;
default:
return -EINVAL;
Expand Down
Loading

0 comments on commit 3e148c7

Please sign in to comment.