1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999 Marcel Moolenaar
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/sdt.h>
35 #include <sys/systm.h>
36 #include <sys/sysctl.h>
37 #include <sys/proc.h>
38 #include <sys/malloc.h>
39 #include <sys/mount.h>
40 #include <sys/jail.h>
41 #include <sys/lock.h>
42 #include <sys/sx.h>
43
44 #include <compat/linux/linux_mib.h>
45 #include <compat/linux/linux_misc.h>
46
47 struct linux_prison {
48 char pr_osname[LINUX_MAX_UTSNAME];
49 char pr_osrelease[LINUX_MAX_UTSNAME];
50 int pr_oss_version;
51 int pr_osrel;
52 };
53
54 static struct linux_prison lprison0 = {
55 .pr_osname = "Linux",
56 .pr_osrelease = LINUX_VERSION_STR,
57 .pr_oss_version = 0x030600,
58 .pr_osrel = LINUX_VERSION_CODE
59 };
60
61 static unsigned linux_osd_jail_slot;
62
63 SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, "Linux mode");
64
65 int linux_debug = 1;
66 SYSCTL_INT(_compat_linux, OID_AUTO, debug, CTLFLAG_RWTUN,
67 &linux_debug, 0, "Log warnings from linux(4); or 0 to disable");
68
69 int linux_default_openfiles = 1024;
70 SYSCTL_INT(_compat_linux, OID_AUTO, default_openfiles, CTLFLAG_RWTUN,
71 &linux_default_openfiles, 0,
72 "Default soft openfiles resource limit, or -1 for unlimited");
73
74 int linux_ignore_ip_recverr = 1;
75 SYSCTL_INT(_compat_linux, OID_AUTO, ignore_ip_recverr, CTLFLAG_RWTUN,
76 &linux_ignore_ip_recverr, 0, "Ignore enabling IP_RECVERR");
77
78 int linux_preserve_vstatus = 0;
79 SYSCTL_INT(_compat_linux, OID_AUTO, preserve_vstatus, CTLFLAG_RWTUN,
80 &linux_preserve_vstatus, 0, "Preserve VSTATUS termios(4) flag");
81
82 bool linux_map_sched_prio = true;
83 SYSCTL_BOOL(_compat_linux, OID_AUTO, map_sched_prio, CTLFLAG_RDTUN,
84 &linux_map_sched_prio, 0, "Map scheduler priorities to Linux priorities "
85 "(not POSIX compliant)");
86
87 static int linux_set_osname(struct thread *td, char *osname);
88 static int linux_set_osrelease(struct thread *td, char *osrelease);
89 static int linux_set_oss_version(struct thread *td, int oss_version);
90
91 static int
92 linux_sysctl_osname(SYSCTL_HANDLER_ARGS)
93 {
94 char osname[LINUX_MAX_UTSNAME];
95 int error;
96
97 linux_get_osname(req->td, osname);
98 error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req);
99 if (error != 0 || req->newptr == NULL)
100 return (error);
101 error = linux_set_osname(req->td, osname);
102
103 return (error);
104 }
105
106 SYSCTL_PROC(_compat_linux, OID_AUTO, osname,
107 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
108 0, 0, linux_sysctl_osname, "A",
109 "Linux kernel OS name");
110
111 static int
112 linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS)
113 {
114 char osrelease[LINUX_MAX_UTSNAME];
115 int error;
116
117 linux_get_osrelease(req->td, osrelease);
118 error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req);
119 if (error != 0 || req->newptr == NULL)
120 return (error);
121 error = linux_set_osrelease(req->td, osrelease);
122
123 return (error);
124 }
125
126 SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease,
127 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
128 0, 0, linux_sysctl_osrelease, "A",
129 "Linux kernel OS release");
130
131 static int
132 linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS)
133 {
134 int oss_version;
135 int error;
136
137 oss_version = linux_get_oss_version(req->td);
138 error = sysctl_handle_int(oidp, &oss_version, 0, req);
139 if (error != 0 || req->newptr == NULL)
140 return (error);
141 error = linux_set_oss_version(req->td, oss_version);
142
143 return (error);
144 }
145
146 SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version,
147 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE,
148 0, 0, linux_sysctl_oss_version, "I",
149 "Linux OSS version");
150
151 /*
152 * Map the osrelease into integer
153 */
154 static int
155 linux_map_osrel(char *osrelease, int *osrel)
156 {
157 char *sep, *eosrelease;
158 int len, v0, v1, v2, v;
159
160 len = strlen(osrelease);
161 eosrelease = osrelease + len;
162 v0 = strtol(osrelease, &sep, 10);
163 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.')
164 return (EINVAL);
165 osrelease = sep + 1;
166 v1 = strtol(osrelease, &sep, 10);
167 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.')
168 return (EINVAL);
169 osrelease = sep + 1;
170 v2 = strtol(osrelease, &sep, 10);
171 if (osrelease == sep ||
172 (sep != eosrelease && (sep + 1 >= eosrelease || *sep != '-')))
173 return (EINVAL);
174
175 v = LINUX_KERNVER(v0, v1, v2);
176 if (v < LINUX_KERNVER(1, 0, 0))
177 return (EINVAL);
178
179 if (osrel != NULL)
180 *osrel = v;
181
182 return (0);
183 }
184
185 /*
186 * Find a prison with Linux info.
187 * Return the Linux info and the (locked) prison.
188 */
189 static struct linux_prison *
190 linux_find_prison(struct prison *spr, struct prison **prp)
191 {
192 struct prison *pr;
193 struct linux_prison *lpr;
194
195 for (pr = spr;; pr = pr->pr_parent) {
196 mtx_lock(&pr->pr_mtx);
197 lpr = (pr == &prison0)
198 ? &lprison0
199 : osd_jail_get(pr, linux_osd_jail_slot);
200 if (lpr != NULL)
201 break;
202 mtx_unlock(&pr->pr_mtx);
203 }
204 *prp = pr;
205
206 return (lpr);
207 }
208
209 /*
210 * Ensure a prison has its own Linux info. If lprp is non-null, point it to
211 * the Linux info and lock the prison.
212 */
213 static void
214 linux_alloc_prison(struct prison *pr, struct linux_prison **lprp)
215 {
216 struct prison *ppr;
217 struct linux_prison *lpr, *nlpr;
218 void **rsv;
219
220 /* If this prison already has Linux info, return that. */
221 lpr = linux_find_prison(pr, &ppr);
222 if (ppr == pr)
223 goto done;
224 /*
225 * Allocate a new info record. Then check again, in case something
226 * changed during the allocation.
227 */
228 mtx_unlock(&ppr->pr_mtx);
229 nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK);
230 rsv = osd_reserve(linux_osd_jail_slot);
231 lpr = linux_find_prison(pr, &ppr);
232 if (ppr == pr) {
233 free(nlpr, M_PRISON);
234 osd_free_reserved(rsv);
235 goto done;
236 }
237 /* Inherit the initial values from the ancestor. */
238 mtx_lock(&pr->pr_mtx);
239 (void)osd_jail_set_reserved(pr, linux_osd_jail_slot, rsv, nlpr);
240 bcopy(lpr, nlpr, sizeof(*lpr));
241 lpr = nlpr;
242 mtx_unlock(&ppr->pr_mtx);
243 done:
244 if (lprp != NULL)
245 *lprp = lpr;
246 else
247 mtx_unlock(&pr->pr_mtx);
248 }
249
250 /*
251 * Jail OSD methods for Linux prison data.
252 */
253 static int
254 linux_prison_create(void *obj, void *data)
255 {
256 struct prison *pr = obj;
257 struct vfsoptlist *opts = data;
258 int jsys;
259
260 if (vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)) == 0 &&
261 jsys == JAIL_SYS_INHERIT)
262 return (0);
263 /*
264 * Inherit a prison's initial values from its parent
265 * (different from JAIL_SYS_INHERIT which also inherits changes).
266 */
267 linux_alloc_prison(pr, NULL);
268 return (0);
269 }
270
271 static int
272 linux_prison_check(void *obj __unused, void *data)
273 {
274 struct vfsoptlist *opts = data;
275 char *osname, *osrelease;
276 int error, jsys, len, oss_version;
277
278 /* Check that the parameters are correct. */
279 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
280 if (error != ENOENT) {
281 if (error != 0)
282 return (error);
283 if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT)
284 return (EINVAL);
285 }
286 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len);
287 if (error != ENOENT) {
288 if (error != 0)
289 return (error);
290 if (len == 0 || osname[len - 1] != '\0')
291 return (EINVAL);
292 if (len > LINUX_MAX_UTSNAME) {
293 vfs_opterror(opts, "linux.osname too long");
294 return (ENAMETOOLONG);
295 }
296 }
297 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len);
298 if (error != ENOENT) {
299 if (error != 0)
300 return (error);
301 if (len == 0 || osrelease[len - 1] != '\0')
302 return (EINVAL);
303 if (len > LINUX_MAX_UTSNAME) {
304 vfs_opterror(opts, "linux.osrelease too long");
305 return (ENAMETOOLONG);
306 }
307 error = linux_map_osrel(osrelease, NULL);
308 if (error != 0) {
309 vfs_opterror(opts, "linux.osrelease format error");
310 return (error);
311 }
312 }
313 error = vfs_copyopt(opts, "linux.oss_version", &oss_version,
314 sizeof(oss_version));
315
316 if (error == ENOENT)
317 error = 0;
318 return (error);
319 }
320
321 static int
322 linux_prison_set(void *obj, void *data)
323 {
324 struct linux_prison *lpr;
325 struct prison *pr = obj;
326 struct vfsoptlist *opts = data;
327 char *osname, *osrelease;
328 int error, gotversion, jsys, len, oss_version;
329
330 /* Set the parameters, which should be correct. */
331 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys));
332 if (error == ENOENT)
333 jsys = -1;
334 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len);
335 if (error == ENOENT)
336 osname = NULL;
337 else
338 jsys = JAIL_SYS_NEW;
339 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len);
340 if (error == ENOENT)
341 osrelease = NULL;
342 else
343 jsys = JAIL_SYS_NEW;
344 error = vfs_copyopt(opts, "linux.oss_version", &oss_version,
345 sizeof(oss_version));
346 if (error == ENOENT)
347 gotversion = 0;
348 else {
349 gotversion = 1;
350 jsys = JAIL_SYS_NEW;
351 }
352 switch (jsys) {
353 case JAIL_SYS_INHERIT:
354 /* "linux=inherit": inherit the parent's Linux info. */
355 mtx_lock(&pr->pr_mtx);
356 osd_jail_del(pr, linux_osd_jail_slot);
357 mtx_unlock(&pr->pr_mtx);
358 break;
359 case JAIL_SYS_NEW:
360 /*
361 * "linux=new" or "linux.*":
362 * the prison gets its own Linux info.
363 */
364 linux_alloc_prison(pr, &lpr);
365 if (osrelease) {
366 (void)linux_map_osrel(osrelease, &lpr->pr_osrel);
367 strlcpy(lpr->pr_osrelease, osrelease,
368 LINUX_MAX_UTSNAME);
369 }
370 if (osname)
371 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME);
372 if (gotversion)
373 lpr->pr_oss_version = oss_version;
374 mtx_unlock(&pr->pr_mtx);
375 }
376
377 return (0);
378 }
379
380 SYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters");
381 SYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME,
382 "Jail Linux kernel OS name");
383 SYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME,
384 "Jail Linux kernel OS release");
385 SYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW,
386 "I", "Jail Linux OSS version");
387
388 static int
389 linux_prison_get(void *obj, void *data)
390 {
391 struct linux_prison *lpr;
392 struct prison *ppr;
393 struct prison *pr = obj;
394 struct vfsoptlist *opts = data;
395 int error, i;
396
397 static int version0;
398
399 /* See if this prison is the one with the Linux info. */
400 lpr = linux_find_prison(pr, &ppr);
401 i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT;
402 error = vfs_setopt(opts, "linux", &i, sizeof(i));
403 if (error != 0 && error != ENOENT)
404 goto done;
405 if (i) {
406 error = vfs_setopts(opts, "linux.osname", lpr->pr_osname);
407 if (error != 0 && error != ENOENT)
408 goto done;
409 error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease);
410 if (error != 0 && error != ENOENT)
411 goto done;
412 error = vfs_setopt(opts, "linux.oss_version",
413 &lpr->pr_oss_version, sizeof(lpr->pr_oss_version));
414 if (error != 0 && error != ENOENT)
415 goto done;
416 } else {
417 /*
418 * If this prison is inheriting its Linux info, report
419 * empty/zero parameters.
420 */
421 error = vfs_setopts(opts, "linux.osname", "");
422 if (error != 0 && error != ENOENT)
423 goto done;
424 error = vfs_setopts(opts, "linux.osrelease", "");
425 if (error != 0 && error != ENOENT)
426 goto done;
427 error = vfs_setopt(opts, "linux.oss_version", &version0,
428 sizeof(lpr->pr_oss_version));
429 if (error != 0 && error != ENOENT)
430 goto done;
431 }
432 error = 0;
433
434 done:
435 mtx_unlock(&ppr->pr_mtx);
436
437 return (error);
438 }
439
440 static void
441 linux_prison_destructor(void *data)
442 {
443
444 free(data, M_PRISON);
445 }
446
447 void
448 linux_osd_jail_register(void)
449 {
450 struct prison *pr;
451 osd_method_t methods[PR_MAXMETHOD] = {
452 [PR_METHOD_CREATE] = linux_prison_create,
453 [PR_METHOD_GET] = linux_prison_get,
454 [PR_METHOD_SET] = linux_prison_set,
455 [PR_METHOD_CHECK] = linux_prison_check
456 };
457
458 linux_osd_jail_slot =
459 osd_jail_register(linux_prison_destructor, methods);
460 /* Copy the system Linux info to any current prisons. */
461 sx_slock(&allprison_lock);
462 TAILQ_FOREACH(pr, &allprison, pr_list)
463 linux_alloc_prison(pr, NULL);
464 sx_sunlock(&allprison_lock);
465 }
466
467 void
468 linux_osd_jail_deregister(void)
469 {
470
471 osd_jail_deregister(linux_osd_jail_slot);
472 }
473
474 void
475 linux_get_osname(struct thread *td, char *dst)
476 {
477 struct prison *pr;
478 struct linux_prison *lpr;
479
480 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
481 bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME);
482 mtx_unlock(&pr->pr_mtx);
483 }
484
485 static int
486 linux_set_osname(struct thread *td, char *osname)
487 {
488 struct prison *pr;
489 struct linux_prison *lpr;
490
491 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
492 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME);
493 mtx_unlock(&pr->pr_mtx);
494
495 return (0);
496 }
497
498 void
499 linux_get_osrelease(struct thread *td, char *dst)
500 {
501 struct prison *pr;
502 struct linux_prison *lpr;
503
504 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
505 bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME);
506 mtx_unlock(&pr->pr_mtx);
507 }
508
509 int
510 linux_kernver(struct thread *td)
511 {
512 struct prison *pr;
513 struct linux_prison *lpr;
514 int osrel;
515
516 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
517 osrel = lpr->pr_osrel;
518 mtx_unlock(&pr->pr_mtx);
519
520 return (osrel);
521 }
522
523 static int
524 linux_set_osrelease(struct thread *td, char *osrelease)
525 {
526 struct prison *pr;
527 struct linux_prison *lpr;
528 int error;
529
530 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
531 error = linux_map_osrel(osrelease, &lpr->pr_osrel);
532 if (error == 0)
533 strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME);
534 mtx_unlock(&pr->pr_mtx);
535
536 return (error);
537 }
538
539 int
540 linux_get_oss_version(struct thread *td)
541 {
542 struct prison *pr;
543 struct linux_prison *lpr;
544 int version;
545
546 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
547 version = lpr->pr_oss_version;
548 mtx_unlock(&pr->pr_mtx);
549
550 return (version);
551 }
552
553 static int
554 linux_set_oss_version(struct thread *td, int oss_version)
555 {
556 struct prison *pr;
557 struct linux_prison *lpr;
558
559 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr);
560 lpr->pr_oss_version = oss_version;
561 mtx_unlock(&pr->pr_mtx);
562
563 return (0);
564 }
Cache object: a55fef967a2ece1779d3bb7dd4566ade
|