aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/fork.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c232
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 */
1332static 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(&current->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 */
1365static 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 */
1376static 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 */
1391static 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 */
1406static 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 */
1420static 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 */
1435static 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 */
1450static 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 */
1466asmlinkage 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
1533bad_unshare_cleanup_fd:
1534 if (new_fd)
1535 put_files_struct(new_fd);
1536
1537bad_unshare_cleanup_vm:
1538 if (new_mm)
1539 mmput(new_mm);
1540
1541bad_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
1546bad_unshare_cleanup_ns:
1547 if (new_ns)
1548 put_namespace(new_ns);
1549
1550bad_unshare_cleanup_fs:
1551 if (new_fs)
1552 put_fs_struct(new_fs);
1553
1554bad_unshare_cleanup_thread:
1555bad_unshare_out:
1556 return err;
1557}