diff options
-rw-r--r-- | kernel/fork.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index 7f0ab5ee948c..6eb9362775f9 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1323,3 +1323,235 @@ void __init proc_caches_init(void) | |||
1323 | sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN, | 1323 | sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN, |
1324 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); | 1324 | SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); |
1325 | } | 1325 | } |
1326 | |||
1327 | |||
1328 | /* | ||
1329 | * Check constraints on flags passed to the unshare system call and | ||
1330 | * force unsharing of additional process context as appropriate. | ||
1331 | */ | ||
1332 | static inline void check_unshare_flags(unsigned long *flags_ptr) | ||
1333 | { | ||
1334 | /* | ||
1335 | * If unsharing a thread from a thread group, must also | ||
1336 | * unshare vm. | ||
1337 | */ | ||
1338 | if (*flags_ptr & CLONE_THREAD) | ||
1339 | *flags_ptr |= CLONE_VM; | ||
1340 | |||
1341 | /* | ||
1342 | * If unsharing vm, must also unshare signal handlers. | ||
1343 | */ | ||
1344 | if (*flags_ptr & CLONE_VM) | ||
1345 | *flags_ptr |= CLONE_SIGHAND; | ||
1346 | |||
1347 | /* | ||
1348 | * If unsharing signal handlers and the task was created | ||
1349 | * using CLONE_THREAD, then must unshare the thread | ||
1350 | */ | ||
1351 | if ((*flags_ptr & CLONE_SIGHAND) && | ||
1352 | (atomic_read(¤t->signal->count) > 1)) | ||
1353 | *flags_ptr |= CLONE_THREAD; | ||
1354 | |||
1355 | /* | ||
1356 | * If unsharing namespace, must also unshare filesystem information. | ||
1357 | */ | ||
1358 | if (*flags_ptr & CLONE_NEWNS) | ||
1359 | *flags_ptr |= CLONE_FS; | ||
1360 | } | ||
1361 | |||
1362 | /* | ||
1363 | * Unsharing of tasks created with CLONE_THREAD is not supported yet | ||
1364 | */ | ||
1365 | static int unshare_thread(unsigned long unshare_flags) | ||
1366 | { | ||
1367 | if (unshare_flags & CLONE_THREAD) | ||
1368 | return -EINVAL; | ||
1369 | |||
1370 | return 0; | ||
1371 | } | ||
1372 | |||
1373 | /* | ||
1374 | * Unsharing of fs info for tasks created with CLONE_FS is not supported yet | ||
1375 | */ | ||
1376 | static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp) | ||
1377 | { | ||
1378 | struct fs_struct *fs = current->fs; | ||
1379 | |||
1380 | if ((unshare_flags & CLONE_FS) && | ||
1381 | (fs && atomic_read(&fs->count) > 1)) | ||
1382 | return -EINVAL; | ||
1383 | |||
1384 | return 0; | ||
1385 | } | ||
1386 | |||
1387 | /* | ||
1388 | * Unsharing of namespace for tasks created without CLONE_NEWNS is not | ||
1389 | * supported yet | ||
1390 | */ | ||
1391 | static int unshare_namespace(unsigned long unshare_flags, struct namespace **new_nsp) | ||
1392 | { | ||
1393 | struct namespace *ns = current->namespace; | ||
1394 | |||
1395 | if ((unshare_flags & CLONE_NEWNS) && | ||
1396 | (ns && atomic_read(&ns->count) > 1)) | ||
1397 | return -EINVAL; | ||
1398 | |||
1399 | return 0; | ||
1400 | } | ||
1401 | |||
1402 | /* | ||
1403 | * Unsharing of sighand for tasks created with CLONE_SIGHAND is not | ||
1404 | * supported yet | ||
1405 | */ | ||
1406 | static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **new_sighp) | ||
1407 | { | ||
1408 | struct sighand_struct *sigh = current->sighand; | ||
1409 | |||
1410 | if ((unshare_flags & CLONE_SIGHAND) && | ||
1411 | (sigh && atomic_read(&sigh->count) > 1)) | ||
1412 | return -EINVAL; | ||
1413 | else | ||
1414 | return 0; | ||
1415 | } | ||
1416 | |||
1417 | /* | ||
1418 | * Unsharing of vm for tasks created with CLONE_VM is not supported yet | ||
1419 | */ | ||
1420 | static int unshare_vm(unsigned long unshare_flags, struct mm_struct **new_mmp) | ||
1421 | { | ||
1422 | struct mm_struct *mm = current->mm; | ||
1423 | |||
1424 | if ((unshare_flags & CLONE_VM) && | ||
1425 | (mm && atomic_read(&mm->mm_users) > 1)) | ||
1426 | return -EINVAL; | ||
1427 | |||
1428 | return 0; | ||
1429 | |||
1430 | } | ||
1431 | |||
1432 | /* | ||
1433 | * Unsharing of files for tasks created with CLONE_FILES is not supported yet | ||
1434 | */ | ||
1435 | static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp) | ||
1436 | { | ||
1437 | struct files_struct *fd = current->files; | ||
1438 | |||
1439 | if ((unshare_flags & CLONE_FILES) && | ||
1440 | (fd && atomic_read(&fd->count) > 1)) | ||
1441 | return -EINVAL; | ||
1442 | |||
1443 | return 0; | ||
1444 | } | ||
1445 | |||
1446 | /* | ||
1447 | * Unsharing of semundo for tasks created with CLONE_SYSVSEM is not | ||
1448 | * supported yet | ||
1449 | */ | ||
1450 | static int unshare_semundo(unsigned long unshare_flags, struct sem_undo_list **new_ulistp) | ||
1451 | { | ||
1452 | if (unshare_flags & CLONE_SYSVSEM) | ||
1453 | return -EINVAL; | ||
1454 | |||
1455 | return 0; | ||
1456 | } | ||
1457 | |||
1458 | /* | ||
1459 | * unshare allows a process to 'unshare' part of the process | ||
1460 | * context which was originally shared using clone. copy_* | ||
1461 | * functions used by do_fork() cannot be used here directly | ||
1462 | * because they modify an inactive task_struct that is being | ||
1463 | * constructed. Here we are modifying the current, active, | ||
1464 | * task_struct. | ||
1465 | */ | ||
1466 | asmlinkage long sys_unshare(unsigned long unshare_flags) | ||
1467 | { | ||
1468 | int err = 0; | ||
1469 | struct fs_struct *fs, *new_fs = NULL; | ||
1470 | struct namespace *ns, *new_ns = NULL; | ||
1471 | struct sighand_struct *sigh, *new_sigh = NULL; | ||
1472 | struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; | ||
1473 | struct files_struct *fd, *new_fd = NULL; | ||
1474 | struct sem_undo_list *new_ulist = NULL; | ||
1475 | |||
1476 | check_unshare_flags(&unshare_flags); | ||
1477 | |||
1478 | if ((err = unshare_thread(unshare_flags))) | ||
1479 | goto bad_unshare_out; | ||
1480 | if ((err = unshare_fs(unshare_flags, &new_fs))) | ||
1481 | goto bad_unshare_cleanup_thread; | ||
1482 | if ((err = unshare_namespace(unshare_flags, &new_ns))) | ||
1483 | goto bad_unshare_cleanup_fs; | ||
1484 | if ((err = unshare_sighand(unshare_flags, &new_sigh))) | ||
1485 | goto bad_unshare_cleanup_ns; | ||
1486 | if ((err = unshare_vm(unshare_flags, &new_mm))) | ||
1487 | goto bad_unshare_cleanup_sigh; | ||
1488 | if ((err = unshare_fd(unshare_flags, &new_fd))) | ||
1489 | goto bad_unshare_cleanup_vm; | ||
1490 | if ((err = unshare_semundo(unshare_flags, &new_ulist))) | ||
1491 | goto bad_unshare_cleanup_fd; | ||
1492 | |||
1493 | if (new_fs || new_ns || new_sigh || new_mm || new_fd || new_ulist) { | ||
1494 | |||
1495 | task_lock(current); | ||
1496 | |||
1497 | if (new_fs) { | ||
1498 | fs = current->fs; | ||
1499 | current->fs = new_fs; | ||
1500 | new_fs = fs; | ||
1501 | } | ||
1502 | |||
1503 | if (new_ns) { | ||
1504 | ns = current->namespace; | ||
1505 | current->namespace = new_ns; | ||
1506 | new_ns = ns; | ||
1507 | } | ||
1508 | |||
1509 | if (new_sigh) { | ||
1510 | sigh = current->sighand; | ||
1511 | current->sighand = new_sigh; | ||
1512 | new_sigh = sigh; | ||
1513 | } | ||
1514 | |||
1515 | if (new_mm) { | ||
1516 | mm = current->mm; | ||
1517 | active_mm = current->active_mm; | ||
1518 | current->mm = new_mm; | ||
1519 | current->active_mm = new_mm; | ||
1520 | activate_mm(active_mm, new_mm); | ||
1521 | new_mm = mm; | ||
1522 | } | ||
1523 | |||
1524 | if (new_fd) { | ||
1525 | fd = current->files; | ||
1526 | current->files = new_fd; | ||
1527 | new_fd = fd; | ||
1528 | } | ||
1529 | |||
1530 | task_unlock(current); | ||
1531 | } | ||
1532 | |||
1533 | bad_unshare_cleanup_fd: | ||
1534 | if (new_fd) | ||
1535 | put_files_struct(new_fd); | ||
1536 | |||
1537 | bad_unshare_cleanup_vm: | ||
1538 | if (new_mm) | ||
1539 | mmput(new_mm); | ||
1540 | |||
1541 | bad_unshare_cleanup_sigh: | ||
1542 | if (new_sigh) | ||
1543 | if (atomic_dec_and_test(&new_sigh->count)) | ||
1544 | kmem_cache_free(sighand_cachep, new_sigh); | ||
1545 | |||
1546 | bad_unshare_cleanup_ns: | ||
1547 | if (new_ns) | ||
1548 | put_namespace(new_ns); | ||
1549 | |||
1550 | bad_unshare_cleanup_fs: | ||
1551 | if (new_fs) | ||
1552 | put_fs_struct(new_fs); | ||
1553 | |||
1554 | bad_unshare_cleanup_thread: | ||
1555 | bad_unshare_out: | ||
1556 | return err; | ||
1557 | } | ||