| .. | .. |
|---|
| 42 | 42 | goto fail; |
|---|
| 43 | 43 | |
|---|
| 44 | 44 | err = -ENOMEM; |
|---|
| 45 | | - ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL); |
|---|
| 45 | + ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL); |
|---|
| 46 | 46 | if (ns == NULL) |
|---|
| 47 | 47 | goto fail_dec; |
|---|
| 48 | 48 | |
|---|
| .. | .. |
|---|
| 117 | 117 | |
|---|
| 118 | 118 | static void free_ipc_ns(struct ipc_namespace *ns) |
|---|
| 119 | 119 | { |
|---|
| 120 | + /* mq_put_mnt() waits for a grace period as kern_unmount() |
|---|
| 121 | + * uses synchronize_rcu(). |
|---|
| 122 | + */ |
|---|
| 123 | + mq_put_mnt(ns); |
|---|
| 120 | 124 | sem_exit_ns(ns); |
|---|
| 121 | 125 | msg_exit_ns(ns); |
|---|
| 122 | 126 | shm_exit_ns(ns); |
|---|
| .. | .. |
|---|
| 126 | 130 | ns_free_inum(&ns->ns); |
|---|
| 127 | 131 | kfree(ns); |
|---|
| 128 | 132 | } |
|---|
| 133 | + |
|---|
| 134 | +static LLIST_HEAD(free_ipc_list); |
|---|
| 135 | +static void free_ipc(struct work_struct *unused) |
|---|
| 136 | +{ |
|---|
| 137 | + struct llist_node *node = llist_del_all(&free_ipc_list); |
|---|
| 138 | + struct ipc_namespace *n, *t; |
|---|
| 139 | + |
|---|
| 140 | + llist_for_each_entry_safe(n, t, node, mnt_llist) |
|---|
| 141 | + free_ipc_ns(n); |
|---|
| 142 | +} |
|---|
| 143 | + |
|---|
| 144 | +/* |
|---|
| 145 | + * The work queue is used to avoid the cost of synchronize_rcu in kern_unmount. |
|---|
| 146 | + */ |
|---|
| 147 | +static DECLARE_WORK(free_ipc_work, free_ipc); |
|---|
| 129 | 148 | |
|---|
| 130 | 149 | /* |
|---|
| 131 | 150 | * put_ipc_ns - drop a reference to an ipc namespace. |
|---|
| .. | .. |
|---|
| 148 | 167 | if (refcount_dec_and_lock(&ns->count, &mq_lock)) { |
|---|
| 149 | 168 | mq_clear_sbinfo(ns); |
|---|
| 150 | 169 | spin_unlock(&mq_lock); |
|---|
| 151 | | - mq_put_mnt(ns); |
|---|
| 152 | | - free_ipc_ns(ns); |
|---|
| 170 | + |
|---|
| 171 | + if (llist_add(&ns->mnt_llist, &free_ipc_list)) |
|---|
| 172 | + schedule_work(&free_ipc_work); |
|---|
| 153 | 173 | } |
|---|
| 154 | 174 | } |
|---|
| 155 | 175 | |
|---|
| .. | .. |
|---|
| 177 | 197 | return put_ipc_ns(to_ipc_ns(ns)); |
|---|
| 178 | 198 | } |
|---|
| 179 | 199 | |
|---|
| 180 | | -static int ipcns_install(struct nsproxy *nsproxy, struct ns_common *new) |
|---|
| 200 | +static int ipcns_install(struct nsset *nsset, struct ns_common *new) |
|---|
| 181 | 201 | { |
|---|
| 202 | + struct nsproxy *nsproxy = nsset->nsproxy; |
|---|
| 182 | 203 | struct ipc_namespace *ns = to_ipc_ns(new); |
|---|
| 183 | 204 | if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || |
|---|
| 184 | | - !ns_capable(current_user_ns(), CAP_SYS_ADMIN)) |
|---|
| 205 | + !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN)) |
|---|
| 185 | 206 | return -EPERM; |
|---|
| 186 | 207 | |
|---|
| 187 | | - /* Ditch state from the old ipc namespace */ |
|---|
| 188 | | - exit_sem(current); |
|---|
| 189 | 208 | put_ipc_ns(nsproxy->ipc_ns); |
|---|
| 190 | 209 | nsproxy->ipc_ns = get_ipc_ns(ns); |
|---|
| 191 | 210 | return 0; |
|---|