1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
27 */
28
29 /*
30 * System calls for creating and inquiring about tasks and projects
31 */
32
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/errno.h>
36 #include <sys/thread.h>
37 #include <sys/proc.h>
38 #include <sys/task.h>
39 #include <sys/systm.h>
40 #include <sys/project.h>
41 #include <sys/cpuvar.h>
42 #include <sys/policy.h>
43 #include <sys/zone.h>
44 #include <sys/rctl.h>
45
46 /*
47 * Limit projlist to 256k projects.
48 */
49 #define MAX_PROJLIST_BUFSIZE 1048576
50
51 typedef struct projlist_walk {
52 projid_t *pw_buf;
53 size_t pw_bufsz;
54 } projlist_walk_t;
55
56 /*
57 * taskid_t tasksys_settaskid(projid_t projid, uint_t flags);
58 *
59 * Overview
60 * Place the calling process in a new task if sufficiently privileged. If the
61 * present task is finalized, the process may not create a new task.
62 *
63 * Return values
64 * 0 on success, errno on failure.
65 */
66 static long
tasksys_settaskid(projid_t projid,uint_t flags)67 tasksys_settaskid(projid_t projid, uint_t flags)
68 {
69 proc_t *p = ttoproc(curthread);
70 kproject_t *oldpj;
71 kproject_t *kpj;
72 task_t *tk, *oldtk;
73 rctl_entity_p_t e;
74 zone_t *zone;
75 int rctlfail = 0;
76
77 if (secpolicy_tasksys(CRED()) != 0)
78 return (set_errno(EPERM));
79
80 if (projid < 0 || projid > MAXPROJID)
81 return (set_errno(EINVAL));
82
83 if (flags & ~TASK_FINAL)
84 return (set_errno(EINVAL));
85
86 mutex_enter(&pidlock);
87 if (p->p_task->tk_flags & TASK_FINAL) {
88 mutex_exit(&pidlock);
89 return (set_errno(EACCES));
90 }
91 mutex_exit(&pidlock);
92
93 /*
94 * Try to stop all other lwps in the process while we're changing
95 * our project. This way, curthread doesn't need to grab its own
96 * thread_lock to find its project ID (see curprojid()). If this
97 * is the /proc agent lwp, we know that the other lwps are already
98 * held. If we failed to hold all lwps, bail out and return EINTR.
99 */
100 if (curthread != p->p_agenttp && !holdlwps(SHOLDFORK1))
101 return (set_errno(EINTR));
102 /*
103 * Put a hold on our new project and make sure that nobody is
104 * trying to bind it to a pool while we're joining.
105 */
106 kpj = project_hold_by_id(projid, p->p_zone, PROJECT_HOLD_INSERT);
107 e.rcep_p.proj = kpj;
108 e.rcep_t = RCENTITY_PROJECT;
109
110 mutex_enter(&p->p_lock);
111 oldpj = p->p_task->tk_proj;
112 zone = p->p_zone;
113
114 mutex_enter(&zone->zone_nlwps_lock);
115 mutex_enter(&zone->zone_mem_lock);
116
117 if (kpj->kpj_nlwps + p->p_lwpcnt > kpj->kpj_nlwps_ctl)
118 if (rctl_test_entity(rc_project_nlwps, kpj->kpj_rctls, p, &e,
119 p->p_lwpcnt, 0) & RCT_DENY)
120 rctlfail = 1;
121
122 if (kpj->kpj_ntasks + 1 > kpj->kpj_ntasks_ctl)
123 if (rctl_test_entity(rc_project_ntasks, kpj->kpj_rctls, p, &e,
124 1, 0) & RCT_DENY)
125 rctlfail = 1;
126
127 if (kpj != proj0p && kpj->kpj_nprocs + 1 > kpj->kpj_nprocs_ctl)
128 if (rctl_test_entity(rc_project_nprocs, kpj->kpj_rctls, p, &e,
129 1, 0) & RCT_DENY)
130 rctlfail = 1;
131
132 if (kpj->kpj_data.kpd_locked_mem + p->p_locked_mem >
133 kpj->kpj_data.kpd_locked_mem_ctl)
134 if (rctl_test_entity(rc_project_locked_mem, kpj->kpj_rctls, p,
135 &e, p->p_locked_mem, 0) & RCT_DENY)
136 rctlfail = 1;
137
138 mutex_enter(&(kpj->kpj_data.kpd_crypto_lock));
139 if (kpj->kpj_data.kpd_crypto_mem + p->p_crypto_mem >
140 kpj->kpj_data.kpd_crypto_mem_ctl)
141 if (rctl_test_entity(rc_project_crypto_mem, kpj->kpj_rctls, p,
142 &e, p->p_crypto_mem, 0) & RCT_DENY)
143 rctlfail = 1;
144
145 if (rctlfail) {
146 mutex_exit(&(kpj->kpj_data.kpd_crypto_lock));
147 mutex_exit(&zone->zone_mem_lock);
148 mutex_exit(&zone->zone_nlwps_lock);
149 if (curthread != p->p_agenttp)
150 continuelwps(p);
151 mutex_exit(&p->p_lock);
152 project_rele(kpj);
153 return (set_errno(EAGAIN));
154 }
155 kpj->kpj_data.kpd_crypto_mem += p->p_crypto_mem;
156 mutex_exit(&(kpj->kpj_data.kpd_crypto_lock));
157 kpj->kpj_data.kpd_locked_mem += p->p_locked_mem;
158 kpj->kpj_nlwps += p->p_lwpcnt;
159 kpj->kpj_ntasks++;
160 kpj->kpj_nprocs++;
161
162 oldpj->kpj_data.kpd_locked_mem -= p->p_locked_mem;
163 mutex_enter(&(oldpj->kpj_data.kpd_crypto_lock));
164 oldpj->kpj_data.kpd_crypto_mem -= p->p_crypto_mem;
165 mutex_exit(&(oldpj->kpj_data.kpd_crypto_lock));
166 oldpj->kpj_nlwps -= p->p_lwpcnt;
167 oldpj->kpj_nprocs--;
168
169 mutex_exit(&zone->zone_mem_lock);
170 mutex_exit(&zone->zone_nlwps_lock);
171 mutex_exit(&p->p_lock);
172
173 mutex_enter(&kpj->kpj_poolbind);
174 tk = task_create(projid, curproc->p_zone);
175 mutex_enter(&cpu_lock);
176 /*
177 * Returns with p_lock held.
178 */
179 oldtk = task_join(tk, flags);
180 if (curthread != p->p_agenttp)
181 continuelwps(p);
182 mutex_exit(&p->p_lock);
183 mutex_exit(&cpu_lock);
184 mutex_exit(&kpj->kpj_poolbind);
185 task_rele(oldtk);
186 project_rele(kpj);
187 return (tk->tk_tkid);
188 }
189
190 /*
191 * taskid_t tasksys_gettaskid(void);
192 *
193 * Overview
194 * Return the current task ID for this process.
195 *
196 * Return value
197 * The ID for the task to which the current process belongs.
198 */
199 static long
tasksys_gettaskid()200 tasksys_gettaskid()
201 {
202 long ret;
203 proc_t *p = ttoproc(curthread);
204
205 mutex_enter(&pidlock);
206 ret = p->p_task->tk_tkid;
207 mutex_exit(&pidlock);
208 return (ret);
209 }
210
211 /*
212 * projid_t tasksys_getprojid(void);
213 *
214 * Overview
215 * Return the current project ID for this process.
216 *
217 * Return value
218 * The ID for the project to which the current process belongs.
219 */
220 static long
tasksys_getprojid()221 tasksys_getprojid()
222 {
223 long ret;
224 proc_t *p = ttoproc(curthread);
225
226 mutex_enter(&pidlock);
227 ret = p->p_task->tk_proj->kpj_id;
228 mutex_exit(&pidlock);
229 return (ret);
230 }
231
232 static int
tasksys_projlist_cb(kproject_t * kp,void * buf)233 tasksys_projlist_cb(kproject_t *kp, void *buf)
234 {
235 projlist_walk_t *pw = (projlist_walk_t *)buf;
236
237 if (pw && pw->pw_bufsz >= sizeof (projid_t)) {
238 *pw->pw_buf = kp->kpj_id;
239 pw->pw_buf++;
240 pw->pw_bufsz -= sizeof (projid_t);
241 }
242
243 return (0);
244 }
245
246 /*
247 * long tasksys_projlist(void *buf, size_t bufsz)
248 *
249 * Overview
250 * Return a buffer containing the project IDs of all currently active projects
251 * in the current zone.
252 *
253 * Return values
254 * The minimum size of a buffer sufficiently large to contain all of the
255 * active project IDs, or -1 if an error occurs during copyout.
256 */
257 static long
tasksys_projlist(void * buf,size_t bufsz)258 tasksys_projlist(void *buf, size_t bufsz)
259 {
260 long ret = 0;
261 projlist_walk_t pw;
262 void *kbuf;
263
264 if (buf == NULL || bufsz == 0)
265 return (project_walk_all(getzoneid(), tasksys_projlist_cb,
266 NULL));
267
268 if (bufsz > MAX_PROJLIST_BUFSIZE)
269 return (set_errno(ENOMEM));
270
271 kbuf = pw.pw_buf = kmem_zalloc(bufsz, KM_SLEEP);
272 pw.pw_bufsz = bufsz;
273
274 ret = project_walk_all(getzoneid(), tasksys_projlist_cb, &pw);
275
276 if (copyout(kbuf, buf, bufsz) == -1)
277 ret = set_errno(EFAULT);
278
279 kmem_free(kbuf, bufsz);
280 return (ret);
281 }
282
283 long
tasksys(int code,projid_t projid,uint_t flags,void * projidbuf,size_t pbufsz)284 tasksys(int code, projid_t projid, uint_t flags, void *projidbuf, size_t pbufsz)
285 {
286 switch (code) {
287 case 0:
288 return (tasksys_settaskid(projid, flags));
289 case 1:
290 return (tasksys_gettaskid());
291 case 2:
292 return (tasksys_getprojid());
293 case 3:
294 return (tasksys_projlist(projidbuf, pbufsz));
295 default:
296 return (set_errno(EINVAL));
297 }
298 }
299