diff options
author | Davidlohr Bueso <davidlohr.bueso@hp.com> | 2013-04-30 22:15:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-05-01 11:12:58 -0400 |
commit | 444d0f621b64716f7868dcbde448e0c66ece4e61 (patch) | |
tree | 3947e0fe791c53c81d87a929af6d1b8e18a5c6aa /ipc | |
parent | 4d2bff5eb86e8d7b4a20934cccb93bdeebed3558 (diff) |
ipc: introduce lockless pre_down ipcctl
Various forms of ipc use ipcctl_pre_down() to retrieve an ipc object and
check permissions, mostly for IPC_RMID and IPC_SET commands.
Introduce ipcctl_pre_down_nolock(), a lockless version of this function.
The locking version is retained, yet modified to call the nolock version
without affecting its semantics, thus transparent to all ipc callers.
Signed-off-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Rik van Riel <riel@redhat.com>
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Chegu Vinod <chegu_vinod@hp.com>
Cc: Emmanuel Benisty <benisty.e@gmail.com>
Cc: Jason Low <jason.low2@hp.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Peter Hurley <peter@hurleysoftware.com>
Cc: Stanislav Kinsbursky <skinsbursky@parallels.com>
Tested-by: Sedat Dilek <sedat.dilek@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/util.c | 31 | ||||
-rw-r--r-- | ipc/util.h | 3 |
2 files changed, 29 insertions, 5 deletions
diff --git a/ipc/util.c b/ipc/util.c index 813804ebdeba..3df0af3158a5 100644 --- a/ipc/util.c +++ b/ipc/util.c | |||
@@ -824,11 +824,28 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | |||
824 | struct ipc64_perm *perm, int extra_perm) | 824 | struct ipc64_perm *perm, int extra_perm) |
825 | { | 825 | { |
826 | struct kern_ipc_perm *ipcp; | 826 | struct kern_ipc_perm *ipcp; |
827 | |||
828 | ipcp = ipcctl_pre_down_nolock(ns, ids, id, cmd, perm, extra_perm); | ||
829 | if (IS_ERR(ipcp)) | ||
830 | goto out; | ||
831 | |||
832 | spin_lock(&ipcp->lock); | ||
833 | out: | ||
834 | return ipcp; | ||
835 | } | ||
836 | |||
837 | struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, | ||
838 | struct ipc_ids *ids, int id, int cmd, | ||
839 | struct ipc64_perm *perm, int extra_perm) | ||
840 | { | ||
827 | kuid_t euid; | 841 | kuid_t euid; |
828 | int err; | 842 | int err = -EPERM; |
843 | struct kern_ipc_perm *ipcp; | ||
829 | 844 | ||
830 | down_write(&ids->rw_mutex); | 845 | down_write(&ids->rw_mutex); |
831 | ipcp = ipc_lock_check(ids, id); | 846 | rcu_read_lock(); |
847 | |||
848 | ipcp = ipc_obtain_object_check(ids, id); | ||
832 | if (IS_ERR(ipcp)) { | 849 | if (IS_ERR(ipcp)) { |
833 | err = PTR_ERR(ipcp); | 850 | err = PTR_ERR(ipcp); |
834 | goto out_up; | 851 | goto out_up; |
@@ -837,17 +854,21 @@ struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | |||
837 | audit_ipc_obj(ipcp); | 854 | audit_ipc_obj(ipcp); |
838 | if (cmd == IPC_SET) | 855 | if (cmd == IPC_SET) |
839 | audit_ipc_set_perm(extra_perm, perm->uid, | 856 | audit_ipc_set_perm(extra_perm, perm->uid, |
840 | perm->gid, perm->mode); | 857 | perm->gid, perm->mode); |
841 | 858 | ||
842 | euid = current_euid(); | 859 | euid = current_euid(); |
843 | if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || | 860 | if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || |
844 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) | 861 | ns_capable(ns->user_ns, CAP_SYS_ADMIN)) |
845 | return ipcp; | 862 | return ipcp; |
846 | 863 | ||
847 | err = -EPERM; | ||
848 | ipc_unlock(ipcp); | ||
849 | out_up: | 864 | out_up: |
865 | /* | ||
866 | * Unsuccessful lookup, unlock and return | ||
867 | * the corresponding error. | ||
868 | */ | ||
869 | rcu_read_unlock(); | ||
850 | up_write(&ids->rw_mutex); | 870 | up_write(&ids->rw_mutex); |
871 | |||
851 | return ERR_PTR(err); | 872 | return ERR_PTR(err); |
852 | } | 873 | } |
853 | 874 | ||
diff --git a/ipc/util.h b/ipc/util.h index bfc8d4ea6e46..13d92fea15a3 100644 --- a/ipc/util.h +++ b/ipc/util.h | |||
@@ -128,6 +128,9 @@ struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id); | |||
128 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); | 128 | void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out); |
129 | void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); | 129 | void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out); |
130 | int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); | 130 | int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); |
131 | struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, | ||
132 | struct ipc_ids *ids, int id, int cmd, | ||
133 | struct ipc64_perm *perm, int extra_perm); | ||
131 | struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, | 134 | struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, |
132 | struct ipc_ids *ids, int id, int cmd, | 135 | struct ipc_ids *ids, int id, int cmd, |
133 | struct ipc64_perm *perm, int extra_perm); | 136 | struct ipc64_perm *perm, int extra_perm); |