diff options
author | Manfred Spraul <manfred@colorfullife.com> | 2008-04-29 04:00:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-04-29 11:06:14 -0400 |
commit | 9edff4ab1f8d82675277a04e359d0ed8bf14a7b7 (patch) | |
tree | 05e67ca13f78b0eff666a4424e03dd6d0fa964c7 | |
parent | 44f564a4bf6ac70f2a84806203045cf515bc9367 (diff) |
ipc: sysvsem: implement sys_unshare(CLONE_SYSVSEM)
sys_unshare(CLONE_NEWIPC) doesn't handle the undo lists properly, this can
cause a kernel memory corruption. CLONE_NEWIPC must detach from the existing
undo lists.
Fix, part 1: add support for sys_unshare(CLONE_SYSVSEM)
The original reason to not support it was the potential (inevitable?)
confusion due to the fact that sys_unshare(CLONE_SYSVSEM) has the
inverse meaning of clone(CLONE_SYSVSEM).
Our two most reasonable options then appear to be (1) fully support
CLONE_SYSVSEM, or (2) continue to refuse explicit CLONE_SYSVSEM,
but always do it anyway on unshare(CLONE_SYSVSEM). This patch does
(1).
Changelog:
Apr 16: SEH: switch to Manfred's alternative patch which
removes the unshare_semundo() function which
always refused CLONE_SYSVSEM.
Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Michael Kerrisk <mtk.manpages@googlemail.com>
Cc: Pierre Peiffer <peifferp@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | ipc/sem.c | 1 | ||||
-rw-r--r-- | kernel/fork.c | 29 |
2 files changed, 12 insertions, 18 deletions
@@ -1250,6 +1250,7 @@ void exit_sem(struct task_struct *tsk) | |||
1250 | undo_list = tsk->sysvsem.undo_list; | 1250 | undo_list = tsk->sysvsem.undo_list; |
1251 | if (!undo_list) | 1251 | if (!undo_list) |
1252 | return; | 1252 | return; |
1253 | tsk->sysvsem.undo_list = NULL; | ||
1253 | 1254 | ||
1254 | if (!atomic_dec_and_test(&undo_list->refcnt)) | 1255 | if (!atomic_dec_and_test(&undo_list->refcnt)) |
1255 | return; | 1256 | return; |
diff --git a/kernel/fork.c b/kernel/fork.c index 156db96ff754..01666979beac 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1669,18 +1669,6 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp | |||
1669 | } | 1669 | } |
1670 | 1670 | ||
1671 | /* | 1671 | /* |
1672 | * Unsharing of semundo for tasks created with CLONE_SYSVSEM is not | ||
1673 | * supported yet | ||
1674 | */ | ||
1675 | static int unshare_semundo(unsigned long unshare_flags, struct sem_undo_list **new_ulistp) | ||
1676 | { | ||
1677 | if (unshare_flags & CLONE_SYSVSEM) | ||
1678 | return -EINVAL; | ||
1679 | |||
1680 | return 0; | ||
1681 | } | ||
1682 | |||
1683 | /* | ||
1684 | * unshare allows a process to 'unshare' part of the process | 1672 | * unshare allows a process to 'unshare' part of the process |
1685 | * context which was originally shared using clone. copy_* | 1673 | * context which was originally shared using clone. copy_* |
1686 | * functions used by do_fork() cannot be used here directly | 1674 | * functions used by do_fork() cannot be used here directly |
@@ -1695,8 +1683,8 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1695 | struct sighand_struct *new_sigh = NULL; | 1683 | struct sighand_struct *new_sigh = NULL; |
1696 | struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; | 1684 | struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL; |
1697 | struct files_struct *fd, *new_fd = NULL; | 1685 | struct files_struct *fd, *new_fd = NULL; |
1698 | struct sem_undo_list *new_ulist = NULL; | ||
1699 | struct nsproxy *new_nsproxy = NULL; | 1686 | struct nsproxy *new_nsproxy = NULL; |
1687 | int do_sysvsem = 0; | ||
1700 | 1688 | ||
1701 | check_unshare_flags(&unshare_flags); | 1689 | check_unshare_flags(&unshare_flags); |
1702 | 1690 | ||
@@ -1708,6 +1696,8 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1708 | CLONE_NEWNET)) | 1696 | CLONE_NEWNET)) |
1709 | goto bad_unshare_out; | 1697 | goto bad_unshare_out; |
1710 | 1698 | ||
1699 | if (unshare_flags & CLONE_SYSVSEM) | ||
1700 | do_sysvsem = 1; | ||
1711 | if ((err = unshare_thread(unshare_flags))) | 1701 | if ((err = unshare_thread(unshare_flags))) |
1712 | goto bad_unshare_out; | 1702 | goto bad_unshare_out; |
1713 | if ((err = unshare_fs(unshare_flags, &new_fs))) | 1703 | if ((err = unshare_fs(unshare_flags, &new_fs))) |
@@ -1718,13 +1708,17 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1718 | goto bad_unshare_cleanup_sigh; | 1708 | goto bad_unshare_cleanup_sigh; |
1719 | if ((err = unshare_fd(unshare_flags, &new_fd))) | 1709 | if ((err = unshare_fd(unshare_flags, &new_fd))) |
1720 | goto bad_unshare_cleanup_vm; | 1710 | goto bad_unshare_cleanup_vm; |
1721 | if ((err = unshare_semundo(unshare_flags, &new_ulist))) | ||
1722 | goto bad_unshare_cleanup_fd; | ||
1723 | if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, | 1711 | if ((err = unshare_nsproxy_namespaces(unshare_flags, &new_nsproxy, |
1724 | new_fs))) | 1712 | new_fs))) |
1725 | goto bad_unshare_cleanup_semundo; | 1713 | goto bad_unshare_cleanup_fd; |
1726 | 1714 | ||
1727 | if (new_fs || new_mm || new_fd || new_ulist || new_nsproxy) { | 1715 | if (new_fs || new_mm || new_fd || do_sysvsem || new_nsproxy) { |
1716 | if (do_sysvsem) { | ||
1717 | /* | ||
1718 | * CLONE_SYSVSEM is equivalent to sys_exit(). | ||
1719 | */ | ||
1720 | exit_sem(current); | ||
1721 | } | ||
1728 | 1722 | ||
1729 | if (new_nsproxy) { | 1723 | if (new_nsproxy) { |
1730 | switch_task_namespaces(current, new_nsproxy); | 1724 | switch_task_namespaces(current, new_nsproxy); |
@@ -1760,7 +1754,6 @@ asmlinkage long sys_unshare(unsigned long unshare_flags) | |||
1760 | if (new_nsproxy) | 1754 | if (new_nsproxy) |
1761 | put_nsproxy(new_nsproxy); | 1755 | put_nsproxy(new_nsproxy); |
1762 | 1756 | ||
1763 | bad_unshare_cleanup_semundo: | ||
1764 | bad_unshare_cleanup_fd: | 1757 | bad_unshare_cleanup_fd: |
1765 | if (new_fd) | 1758 | if (new_fd) |
1766 | put_files_struct(new_fd); | 1759 | put_files_struct(new_fd); |