#include <linux/slab.h>
#include <linux/file.h>
+#include <linux/fdtable.h>
#include <linux/mman.h>
-#include <linux/a.out.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/smp_lock.h>
+#include <linux/string.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
+#include <asm/tlb.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
+#ifdef __alpha__
+/* for /sbin/loader handling in search_binary_handler() */
+#include <linux/a.out.h>
+#endif
+
int core_uses_pid;
-char core_pattern[128] = "core";
+char core_pattern[CORENAME_MAX_SIZE] = "core";
int suid_dumpable = 0;
-EXPORT_SYMBOL(suid_dumpable);
/* The maximal length of core_pattern is also specified in sysctl.c */
-static struct linux_binfmt *formats;
+static LIST_HEAD(formats);
static DEFINE_RWLOCK(binfmt_lock);
int register_binfmt(struct linux_binfmt * fmt)
{
- struct linux_binfmt ** tmp = &formats;
-
if (!fmt)
return -EINVAL;
- if (fmt->next)
- return -EBUSY;
write_lock(&binfmt_lock);
- while (*tmp) {
- if (fmt == *tmp) {
- write_unlock(&binfmt_lock);
- return -EBUSY;
- }
- tmp = &(*tmp)->next;
- }
- fmt->next = formats;
- formats = fmt;
+ list_add(&fmt->lh, &formats);
write_unlock(&binfmt_lock);
return 0;
}
EXPORT_SYMBOL(register_binfmt);
-int unregister_binfmt(struct linux_binfmt * fmt)
+void unregister_binfmt(struct linux_binfmt * fmt)
{
- struct linux_binfmt ** tmp = &formats;
-
write_lock(&binfmt_lock);
- while (*tmp) {
- if (fmt == *tmp) {
- *tmp = fmt->next;
- write_unlock(&binfmt_lock);
- return 0;
- }
- tmp = &(*tmp)->next;
- }
+ list_del(&fmt->lh);
write_unlock(&binfmt_lock);
- return -EINVAL;
}
EXPORT_SYMBOL(unregister_binfmt);
goto out;
error = -EINVAL;
- if (!S_ISREG(nd.dentry->d_inode->i_mode))
+ if (!S_ISREG(nd.path.dentry->d_inode->i_mode))
goto exit;
error = vfs_permission(&nd, MAY_READ | MAY_EXEC);
if (error)
goto exit;
- file = nameidata_to_filp(&nd, O_RDONLY);
+ file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
error = PTR_ERR(file);
if (IS_ERR(file))
goto out;
struct linux_binfmt * fmt;
read_lock(&binfmt_lock);
- for (fmt = formats ; fmt ; fmt = fmt->next) {
+ list_for_each_entry(fmt, &formats, lh) {
if (!fmt->load_shlib)
continue;
if (!try_module_get(fmt->module))
return error;
exit:
release_open_intent(&nd);
- path_release(&nd);
+ path_put(&nd.path);
goto out;
}
+#ifdef CONFIG_MMU
+
+static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
+ int write)
+{
+ struct page *page;
+ int ret;
+
+#ifdef CONFIG_STACK_GROWSUP
+ if (write) {
+ ret = expand_stack_downwards(bprm->vma, pos);
+ if (ret < 0)
+ return NULL;
+ }
+#endif
+ ret = get_user_pages(current, bprm->mm, pos,
+ 1, write, 1, &page, NULL);
+ if (ret <= 0)
+ return NULL;
+
+ if (write) {
+ unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start;
+ struct rlimit *rlim;
+
+ /*
+ * We've historically supported up to 32 pages (ARG_MAX)
+ * of argument strings even with small stacks
+ */
+ if (size <= ARG_MAX)
+ return page;
+
+ /*
+ * Limit to 1/4-th the stack size for the argv+env strings.
+ * This ensures that:
+ * - the remaining binfmt code will not run out of stack space,
+ * - the program will have a reasonable amount of stack left
+ * to work from.
+ */
+ rlim = current->signal->rlim;
+ if (size > rlim[RLIMIT_STACK].rlim_cur / 4) {
+ put_page(page);
+ return NULL;
+ }
+ }
+
+ return page;
+}
+
+static void put_arg_page(struct page *page)
+{
+ put_page(page);
+}
+
+static void free_arg_page(struct linux_binprm *bprm, int i)
+{
+}
+
+static void free_arg_pages(struct linux_binprm *bprm)
+{
+}
+
+static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos,
+ struct page *page)
+{
+ flush_cache_page(bprm->vma, pos, page_to_pfn(page));
+}
+
+static int __bprm_mm_init(struct linux_binprm *bprm)
+{
+ int err = -ENOMEM;
+ struct vm_area_struct *vma = NULL;
+ struct mm_struct *mm = bprm->mm;
+
+ bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
+ if (!vma)
+ goto err;
+
+ down_write(&mm->mmap_sem);
+ vma->vm_mm = mm;
+
+ /*
+ * Place the stack at the largest stack address the architecture
+ * supports. Later, we'll move this to an appropriate place. We don't
+ * use STACK_TOP because that can depend on attributes which aren't
+ * configured yet.
+ */
+ vma->vm_end = STACK_TOP_MAX;
+ vma->vm_start = vma->vm_end - PAGE_SIZE;
+
+ vma->vm_flags = VM_STACK_FLAGS;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ err = insert_vm_struct(mm, vma);
+ if (err) {
+ up_write(&mm->mmap_sem);
+ goto err;
+ }
+
+ mm->stack_vm = mm->total_vm = 1;
+ up_write(&mm->mmap_sem);
+
+ bprm->p = vma->vm_end - sizeof(void *);
+
+ return 0;
+
+err:
+ if (vma) {
+ bprm->vma = NULL;
+ kmem_cache_free(vm_area_cachep, vma);
+ }
+
+ return err;
+}
+
+static bool valid_arg_len(struct linux_binprm *bprm, long len)
+{
+ return len <= MAX_ARG_STRLEN;
+}
+
+#else
+
+static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
+ int write)
+{
+ struct page *page;
+
+ page = bprm->page[pos / PAGE_SIZE];
+ if (!page && write) {
+ page = alloc_page(GFP_HIGHUSER|__GFP_ZERO);
+ if (!page)
+ return NULL;
+ bprm->page[pos / PAGE_SIZE] = page;
+ }
+
+ return page;
+}
+
+static void put_arg_page(struct page *page)
+{
+}
+
+static void free_arg_page(struct linux_binprm *bprm, int i)
+{
+ if (bprm->page[i]) {
+ __free_page(bprm->page[i]);
+ bprm->page[i] = NULL;
+ }
+}
+
+static void free_arg_pages(struct linux_binprm *bprm)
+{
+ int i;
+
+ for (i = 0; i < MAX_ARG_PAGES; i++)
+ free_arg_page(bprm, i);
+}
+
+static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos,
+ struct page *page)
+{
+}
+
+static int __bprm_mm_init(struct linux_binprm *bprm)
+{
+ bprm->p = PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *);
+ return 0;
+}
+
+static bool valid_arg_len(struct linux_binprm *bprm, long len)
+{
+ return len <= bprm->p;
+}
+
+#endif /* CONFIG_MMU */
+
+/*
+ * Create a new mm_struct and populate it with a temporary stack
+ * vm_area_struct. We don't have enough context at this point to set the stack
+ * flags, permissions, and offset, so we use temporary values. We'll update
+ * them later in setup_arg_pages().
+ */
+int bprm_mm_init(struct linux_binprm *bprm)
+{
+ int err;
+ struct mm_struct *mm = NULL;
+
+ bprm->mm = mm = mm_alloc();
+ err = -ENOMEM;
+ if (!mm)
+ goto err;
+
+ err = init_new_context(current, mm);
+ if (err)
+ goto err;
+
+ err = __bprm_mm_init(bprm);
+ if (err)
+ goto err;
+
+ return 0;
+
+err:
+ if (mm) {
+ bprm->mm = NULL;
+ mmdrop(mm);
+ }
+
+ return err;
+}
+
/*
* count() counts the number of strings in array ARGV.
*/
}
/*
- * 'copy_strings()' copies argument/environment strings from user
- * memory to free pages in kernel mem. These are in a format ready
- * to be put directly into the top of new user memory.
+ * 'copy_strings()' copies argument/environment strings from the old
+ * processes's memory to the new process's stack. The call to get_user_pages()
+ * ensures the destination page is created and not swapped out.
*/
static int copy_strings(int argc, char __user * __user * argv,
struct linux_binprm *bprm)
{
struct page *kmapped_page = NULL;
char *kaddr = NULL;
+ unsigned long kpos = 0;
int ret;
while (argc-- > 0) {
unsigned long pos;
if (get_user(str, argv+argc) ||
- !(len = strnlen_user(str, bprm->p))) {
+ !(len = strnlen_user(str, MAX_ARG_STRLEN))) {
ret = -EFAULT;
goto out;
}
- if (bprm->p < len) {
+ if (!valid_arg_len(bprm, len)) {
ret = -E2BIG;
goto out;
}
- bprm->p -= len;
- /* XXX: add architecture specific overflow check here. */
+ /* We're going to work our way backwords. */
pos = bprm->p;
+ str += len;
+ bprm->p -= len;
while (len > 0) {
- int i, new, err;
int offset, bytes_to_copy;
- struct page *page;
offset = pos % PAGE_SIZE;
- i = pos/PAGE_SIZE;
- page = bprm->page[i];
- new = 0;
- if (!page) {
- page = alloc_page(GFP_HIGHUSER);
- bprm->page[i] = page;
+ if (offset == 0)
+ offset = PAGE_SIZE;
+
+ bytes_to_copy = offset;
+ if (bytes_to_copy > len)
+ bytes_to_copy = len;
+
+ offset -= bytes_to_copy;
+ pos -= bytes_to_copy;
+ str -= bytes_to_copy;
+ len -= bytes_to_copy;
+
+ if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
+ struct page *page;
+
+ page = get_arg_page(bprm, pos, 1);
if (!page) {
- ret = -ENOMEM;
+ ret = -E2BIG;
goto out;
}
- new = 1;
- }
- if (page != kmapped_page) {
- if (kmapped_page)
+ if (kmapped_page) {
+ flush_kernel_dcache_page(kmapped_page);
kunmap(kmapped_page);
+ put_arg_page(kmapped_page);
+ }
kmapped_page = page;
kaddr = kmap(kmapped_page);
+ kpos = pos & PAGE_MASK;
+ flush_arg_page(bprm, kpos, kmapped_page);
}
- if (new && offset)
- memset(kaddr, 0, offset);
- bytes_to_copy = PAGE_SIZE - offset;
- if (bytes_to_copy > len) {
- bytes_to_copy = len;
- if (new)
- memset(kaddr+offset+len, 0,
- PAGE_SIZE-offset-len);
- }
- err = copy_from_user(kaddr+offset, str, bytes_to_copy);
- if (err) {
+ if (copy_from_user(kaddr+offset, str, bytes_to_copy)) {
ret = -EFAULT;
goto out;
}
-
- pos += bytes_to_copy;
- str += bytes_to_copy;
- len -= bytes_to_copy;
}
}
ret = 0;
out:
- if (kmapped_page)
+ if (kmapped_page) {
+ flush_kernel_dcache_page(kmapped_page);
kunmap(kmapped_page);
+ put_arg_page(kmapped_page);
+ }
return ret;
}
set_fs(oldfs);
return r;
}
-
EXPORT_SYMBOL(copy_strings_kernel);
#ifdef CONFIG_MMU
+
/*
- * This routine is used to map in a page into an address space: needed by
- * execve() for the initial stack and environment pages.
+ * During bprm_mm_init(), we create a temporary stack at STACK_TOP_MAX. Once
+ * the binfmt code determines where the new stack should reside, we shift it to
+ * its final location. The process proceeds as follows:
*
- * vma->vm_mm->mmap_sem is held for writing.
+ * 1) Use shift to calculate the new vma endpoints.
+ * 2) Extend vma to cover both the old and new ranges. This ensures the
+ * arguments passed to subsequent functions are consistent.
+ * 3) Move vma's page tables to the new range.
+ * 4) Free up any cleared pgd range.
+ * 5) Shrink the vma to cover only the new range.
*/
-void install_arg_page(struct vm_area_struct *vma,
- struct page *page, unsigned long address)
+static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
{
struct mm_struct *mm = vma->vm_mm;
- pte_t * pte;
- spinlock_t *ptl;
+ unsigned long old_start = vma->vm_start;
+ unsigned long old_end = vma->vm_end;
+ unsigned long length = old_end - old_start;
+ unsigned long new_start = old_start - shift;
+ unsigned long new_end = old_end - shift;
+ struct mmu_gather *tlb;
- if (unlikely(anon_vma_prepare(vma)))
- goto out;
+ BUG_ON(new_start > new_end);
- flush_dcache_page(page);
- pte = get_locked_pte(mm, address, &ptl);
- if (!pte)
- goto out;
- if (!pte_none(*pte)) {
- pte_unmap_unlock(pte, ptl);
- goto out;
+ /*
+ * ensure there are no vmas between where we want to go
+ * and where we are
+ */
+ if (vma != find_vma(mm, new_start))
+ return -EFAULT;
+
+ /*
+ * cover the whole range: [new_start, old_end)
+ */
+ vma_adjust(vma, new_start, old_end, vma->vm_pgoff, NULL);
+
+ /*
+ * move the page tables downwards, on failure we rely on
+ * process cleanup to remove whatever mess we made.
+ */
+ if (length != move_page_tables(vma, old_start,
+ vma, new_start, length))
+ return -ENOMEM;
+
+ lru_add_drain();
+ tlb = tlb_gather_mmu(mm, 0);
+ if (new_end > old_start) {
+ /*
+ * when the old and new regions overlap clear from new_end.
+ */
+ free_pgd_range(&tlb, new_end, old_end, new_end,
+ vma->vm_next ? vma->vm_next->vm_start : 0);
+ } else {
+ /*
+ * otherwise, clean from old_start; this is done to not touch
+ * the address space in [new_end, old_start) some architectures
+ * have constraints on va-space that make this illegal (IA64) -
+ * for the others its just a little faster.
+ */
+ free_pgd_range(&tlb, old_start, old_end, new_end,
+ vma->vm_next ? vma->vm_next->vm_start : 0);
}
- inc_mm_counter(mm, anon_rss);
- lru_cache_add_active(page);
- set_pte_at(mm, address, pte, pte_mkdirty(pte_mkwrite(mk_pte(
- page, vma->vm_page_prot))));
- page_add_new_anon_rmap(page, vma, address);
- pte_unmap_unlock(pte, ptl);
-
- /* no need for flush_tlb */
- return;
-out:
- __free_page(page);
- force_sig(SIGKILL, current);
+ tlb_finish_mmu(tlb, new_end, old_end);
+
+ /*
+ * shrink the vma to just the new range.
+ */
+ vma_adjust(vma, new_start, new_end, vma->vm_pgoff, NULL);
+
+ return 0;
}
#define EXTRA_STACK_VM_PAGES 20 /* random */
+/*
+ * Finalizes the stack vm_area_struct. The flags and permissions are updated,
+ * the stack is optionally relocated, and some extra space is added.
+ */
int setup_arg_pages(struct linux_binprm *bprm,
unsigned long stack_top,
int executable_stack)
{
- unsigned long stack_base;
- struct vm_area_struct *mpnt;
+ unsigned long ret;
+ unsigned long stack_shift;
struct mm_struct *mm = current->mm;
- int i, ret;
- long arg_size;
+ struct vm_area_struct *vma = bprm->vma;
+ struct vm_area_struct *prev = NULL;
+ unsigned long vm_flags;
+ unsigned long stack_base;
#ifdef CONFIG_STACK_GROWSUP
- /* Move the argument and environment strings to the bottom of the
- * stack space.
- */
- int offset, j;
- char *to, *from;
-
- /* Start by shifting all the pages down */
- i = 0;
- for (j = 0; j < MAX_ARG_PAGES; j++) {
- struct page *page = bprm->page[j];
- if (!page)
- continue;
- bprm->page[i++] = page;
- }
-
- /* Now move them within their pages */
- offset = bprm->p % PAGE_SIZE;
- to = kmap(bprm->page[0]);
- for (j = 1; j < i; j++) {
- memmove(to, to + offset, PAGE_SIZE - offset);
- from = kmap(bprm->page[j]);
- memcpy(to + PAGE_SIZE - offset, from, offset);
- kunmap(bprm->page[j - 1]);
- to = from;
- }
- memmove(to, to + offset, PAGE_SIZE - offset);
- kunmap(bprm->page[j - 1]);
-
/* Limit stack size to 1GB */
stack_base = current->signal->rlim[RLIMIT_STACK].rlim_max;
if (stack_base > (1 << 30))
stack_base = 1 << 30;
- stack_base = PAGE_ALIGN(stack_top - stack_base);
- /* Adjust bprm->p to point to the end of the strings. */
- bprm->p = stack_base + PAGE_SIZE * i - offset;
+ /* Make sure we didn't let the argument array grow too large. */
+ if (vma->vm_end - vma->vm_start > stack_base)
+ return -ENOMEM;
- mm->arg_start = stack_base;
- arg_size = i << PAGE_SHIFT;
+ stack_base = PAGE_ALIGN(stack_top - stack_base);
- /* zero pages that were copied above */
- while (i < MAX_ARG_PAGES)
- bprm->page[i++] = NULL;
+ stack_shift = vma->vm_start - stack_base;
+ mm->arg_start = bprm->p - stack_shift;
+ bprm->p = vma->vm_end - stack_shift;
#else
- stack_base = arch_align_stack(stack_top - MAX_ARG_PAGES*PAGE_SIZE);
- stack_base = PAGE_ALIGN(stack_base);
- bprm->p += stack_base;
+ stack_top = arch_align_stack(stack_top);
+ stack_top = PAGE_ALIGN(stack_top);
+ stack_shift = vma->vm_end - stack_top;
+
+ bprm->p -= stack_shift;
mm->arg_start = bprm->p;
- arg_size = stack_top - (PAGE_MASK & (unsigned long) mm->arg_start);
#endif
- arg_size += EXTRA_STACK_VM_PAGES * PAGE_SIZE;
-
if (bprm->loader)
- bprm->loader += stack_base;
- bprm->exec += stack_base;
-
- mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
- if (!mpnt)
- return -ENOMEM;
+ bprm->loader -= stack_shift;
+ bprm->exec -= stack_shift;
down_write(&mm->mmap_sem);
- {
- mpnt->vm_mm = mm;
-#ifdef CONFIG_STACK_GROWSUP
- mpnt->vm_start = stack_base;
- mpnt->vm_end = stack_base + arg_size;
-#else
- mpnt->vm_end = stack_top;
- mpnt->vm_start = mpnt->vm_end - arg_size;
-#endif
- /* Adjust stack execute permissions; explicitly enable
- * for EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X
- * and leave alone (arch default) otherwise. */
- if (unlikely(executable_stack == EXSTACK_ENABLE_X))
- mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC;
- else if (executable_stack == EXSTACK_DISABLE_X)
- mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC;
- else
- mpnt->vm_flags = VM_STACK_FLAGS;
- mpnt->vm_flags |= mm->def_flags;
- mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7];
- if ((ret = insert_vm_struct(mm, mpnt))) {
+ vm_flags = VM_STACK_FLAGS;
+
+ /*
+ * Adjust stack execute permissions; explicitly enable for
+ * EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X and leave alone
+ * (arch default) otherwise.
+ */
+ if (unlikely(executable_stack == EXSTACK_ENABLE_X))
+ vm_flags |= VM_EXEC;
+ else if (executable_stack == EXSTACK_DISABLE_X)
+ vm_flags &= ~VM_EXEC;
+ vm_flags |= mm->def_flags;
+
+ ret = mprotect_fixup(vma, &prev, vma->vm_start, vma->vm_end,
+ vm_flags);
+ if (ret)
+ goto out_unlock;
+ BUG_ON(prev != vma);
+
+ /* Move stack pages down in memory. */
+ if (stack_shift) {
+ ret = shift_arg_pages(vma, stack_shift);
+ if (ret) {
up_write(&mm->mmap_sem);
- kmem_cache_free(vm_area_cachep, mpnt);
return ret;
}
- mm->stack_vm = mm->total_vm = vma_pages(mpnt);
}
- for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
- struct page *page = bprm->page[i];
- if (page) {
- bprm->page[i] = NULL;
- install_arg_page(mpnt, page, stack_base);
- }
- stack_base += PAGE_SIZE;
- }
+#ifdef CONFIG_STACK_GROWSUP
+ stack_base = vma->vm_end + EXTRA_STACK_VM_PAGES * PAGE_SIZE;
+#else
+ stack_base = vma->vm_start - EXTRA_STACK_VM_PAGES * PAGE_SIZE;
+#endif
+ ret = expand_stack(vma, stack_base);
+ if (ret)
+ ret = -EFAULT;
+
+out_unlock:
up_write(&mm->mmap_sem);
-
return 0;
}
-
EXPORT_SYMBOL(setup_arg_pages);
-#define free_arg_pages(bprm) do { } while (0)
-
-#else
-
-static inline void free_arg_pages(struct linux_binprm *bprm)
-{
- int i;
-
- for (i = 0; i < MAX_ARG_PAGES; i++) {
- if (bprm->page[i])
- __free_page(bprm->page[i]);
- bprm->page[i] = NULL;
- }
-}
-
#endif /* CONFIG_MMU */
struct file *open_exec(const char *name)
file = ERR_PTR(err);
if (!err) {
- struct inode *inode = nd.dentry->d_inode;
+ struct inode *inode = nd.path.dentry->d_inode;
file = ERR_PTR(-EACCES);
- if (!(nd.mnt->mnt_flags & MNT_NOEXEC) &&
- S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->i_mode)) {
int err = vfs_permission(&nd, MAY_EXEC);
file = ERR_PTR(err);
if (!err) {
- file = nameidata_to_filp(&nd, O_RDONLY);
+ file = nameidata_to_filp(&nd,
+ O_RDONLY|O_LARGEFILE);
if (!IS_ERR(file)) {
err = deny_write_access(file);
if (err) {
}
}
release_open_intent(&nd);
- path_release(&nd);
+ path_put(&nd.path);
}
goto out;
}
tsk->active_mm = mm;
activate_mm(active_mm, mm);
task_unlock(tsk);
+ mm_update_next_owner(old_mm);
arch_pick_mmap_layout(mm);
if (old_mm) {
up_read(&old_mm->mmap_sem);
static int de_thread(struct task_struct *tsk)
{
struct signal_struct *sig = tsk->signal;
- struct sighand_struct *newsighand, *oldsighand = tsk->sighand;
+ struct sighand_struct *oldsighand = tsk->sighand;
spinlock_t *lock = &oldsighand->siglock;
struct task_struct *leader = NULL;
int count;
- /*
- * If we don't share sighandlers, then we aren't sharing anything
- * and we can just re-use it all.
- */
- if (atomic_read(&oldsighand->count) <= 1) {
- BUG_ON(atomic_read(&sig->count) != 1);
- exit_itimers(sig);
- return 0;
- }
-
- newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
- if (!newsighand)
- return -ENOMEM;
-
if (thread_group_empty(tsk))
goto no_thread_group;
/*
* Kill all other threads in the thread group.
- * We must hold tasklist_lock to call zap_other_threads.
*/
- read_lock(&tasklist_lock);
spin_lock_irq(lock);
- if (sig->flags & SIGNAL_GROUP_EXIT) {
+ if (signal_group_exit(sig)) {
/*
* Another group action in progress, just
* return so that the signal is processed.
*/
spin_unlock_irq(lock);
- read_unlock(&tasklist_lock);
- kmem_cache_free(sighand_cachep, newsighand);
return -EAGAIN;
}
-
- /*
- * child_reaper ignores SIGKILL, change it now.
- * Reparenting needs write_lock on tasklist_lock,
- * so it is safe to do it under read_lock.
- */
- if (unlikely(tsk->group_leader == child_reaper(tsk)))
- tsk->nsproxy->pid_ns->child_reaper = tsk;
-
+ sig->group_exit_task = tsk;
zap_other_threads(tsk);
- read_unlock(&tasklist_lock);
- /*
- * Account for the thread group leader hanging around:
- */
- count = 1;
- if (!thread_group_leader(tsk)) {
- count = 2;
- /*
- * The SIGALRM timer survives the exec, but needs to point
- * at us as the new group leader now. We have a race with
- * a timer firing now getting the old leader, so we need to
- * synchronize with any firing (by calling del_timer_sync)
- * before we can safely let the old group leader die.
- */
- sig->tsk = tsk;
- spin_unlock_irq(lock);
- if (hrtimer_cancel(&sig->real_timer))
- hrtimer_restart(&sig->real_timer);
- spin_lock_irq(lock);
- }
+ /* Account for the thread group leader hanging around: */
+ count = thread_group_leader(tsk) ? 1 : 2;
+ sig->notify_count = count;
while (atomic_read(&sig->count) > count) {
- sig->group_exit_task = tsk;
- sig->notify_count = count;
__set_current_state(TASK_UNINTERRUPTIBLE);
spin_unlock_irq(lock);
schedule();
spin_lock_irq(lock);
}
- sig->group_exit_task = NULL;
- sig->notify_count = 0;
spin_unlock_irq(lock);
/*
* and to assume its PID:
*/
if (!thread_group_leader(tsk)) {
- /*
- * Wait for the thread group leader to be a zombie.
- * It should already be zombie at this point, most
- * of the time.
- */
leader = tsk->group_leader;
- while (leader->exit_state != EXIT_ZOMBIE)
- yield();
+ sig->notify_count = -1; /* for exit_notify() */
+ for (;;) {
+ write_lock_irq(&tasklist_lock);
+ if (likely(leader->exit_state))
+ break;
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+ write_unlock_irq(&tasklist_lock);
+ schedule();
+ }
+
+ if (unlikely(task_child_reaper(tsk) == leader))
+ task_active_pid_ns(tsk)->child_reaper = tsk;
/*
* The only record we have of the real-time age of a
* process, regardless of execs it's done, is start_time.
*/
tsk->start_time = leader->start_time;
- write_lock_irq(&tasklist_lock);
-
- BUG_ON(leader->tgid != tsk->tgid);
- BUG_ON(tsk->pid == tsk->tgid);
+ BUG_ON(!same_thread_group(leader, tsk));
+ BUG_ON(has_group_leader_pid(tsk));
/*
* An exec() starts a new thread group with the
* TGID of the previous thread group. Rehash the
*/
detach_pid(tsk, PIDTYPE_PID);
tsk->pid = leader->pid;
- attach_pid(tsk, PIDTYPE_PID, tsk->pid);
+ attach_pid(tsk, PIDTYPE_PID, task_pid(leader));
transfer_pid(leader, tsk, PIDTYPE_PGID);
transfer_pid(leader, tsk, PIDTYPE_SID);
list_replace_rcu(&leader->tasks, &tsk->tasks);
leader->exit_state = EXIT_DEAD;
write_unlock_irq(&tasklist_lock);
- }
+ }
- /*
- * There may be one thread left which is just exiting,
- * but it's safe to stop telling the group to kill themselves.
- */
- sig->flags = 0;
+ sig->group_exit_task = NULL;
+ sig->notify_count = 0;
no_thread_group:
exit_itimers(sig);
+ flush_itimer_signals();
if (leader)
release_task(leader);
- BUG_ON(atomic_read(&sig->count) != 1);
-
- if (atomic_read(&oldsighand->count) == 1) {
+ if (atomic_read(&oldsighand->count) != 1) {
+ struct sighand_struct *newsighand;
/*
- * Now that we nuked the rest of the thread group,
- * it turns out we are not sharing sighand any more either.
- * So we can just keep it.
- */
- kmem_cache_free(sighand_cachep, newsighand);
- } else {
- /*
- * Move our state over to newsighand and switch it in.
+ * This ->sighand is shared with the CLONE_SIGHAND
+ * but not CLONE_THREAD task, switch to the new one.
*/
+ newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
+ if (!newsighand)
+ return -ENOMEM;
+
atomic_set(&newsighand->count, 1);
memcpy(newsighand->action, oldsighand->action,
sizeof(newsighand->action));
write_lock_irq(&tasklist_lock);
spin_lock(&oldsighand->siglock);
- spin_lock_nested(&newsighand->siglock, SINGLE_DEPTH_NESTING);
-
rcu_assign_pointer(tsk->sighand, newsighand);
- recalc_sigpending();
-
- spin_unlock(&newsighand->siglock);
spin_unlock(&oldsighand->siglock);
write_unlock_irq(&tasklist_lock);
- if (atomic_dec_and_test(&oldsighand->count))
- kmem_cache_free(sighand_cachep, oldsighand);
+ __cleanup_sighand(oldsighand);
}
BUG_ON(!thread_group_leader(tsk));
return 0;
}
-
+
/*
* These functions flushes out all traces of the currently running executable
* so that a new one can be started
*/
-
static void flush_old_files(struct files_struct * files)
{
long j = -1;
spin_unlock(&files->file_lock);
}
-void get_task_comm(char *buf, struct task_struct *tsk)
+char *get_task_comm(char *buf, struct task_struct *tsk)
{
/* buf must be at least sizeof(tsk->comm) in size */
task_lock(tsk);
strncpy(buf, tsk->comm, sizeof(tsk->comm));
task_unlock(tsk);
+ return buf;
}
void set_task_comm(struct task_struct *tsk, char *buf)
{
char * name;
int i, ch, retval;
- struct files_struct *files;
char tcomm[sizeof(current->comm)];
/*
if (retval)
goto out;
- /*
- * Make sure we have private file handles. Ask the
- * fork helper to do the work for us and the exit
- * helper to do the cleanup of the old one.
- */
- files = current->files; /* refcounted so safe to hold */
- retval = unshare_files();
- if (retval)
- goto out;
+ set_mm_exe_file(bprm->mm, bprm->file);
+
/*
* Release all of the old mmap stuff
*/
retval = exec_mmap(bprm->mm);
if (retval)
- goto mmap_failed;
+ goto out;
bprm->mm = NULL; /* We're using it now */
/* This is the point of no return */
- put_files_struct(files);
-
current->sas_ss_sp = current->sas_ss_size = 0;
if (current->euid == current->uid && current->egid == current->gid)
- current->mm->dumpable = 1;
+ set_dumpable(current->mm, 1);
else
- current->mm->dumpable = suid_dumpable;
+ set_dumpable(current->mm, suid_dumpable);
name = bprm->filename;
*/
current->mm->task_size = TASK_SIZE;
- if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
- file_permission(bprm->file, MAY_READ) ||
- (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
+ if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) {
+ suid_keys(current);
+ set_dumpable(current->mm, suid_dumpable);
+ current->pdeath_signal = 0;
+ } else if (file_permission(bprm->file, MAY_READ) ||
+ (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
suid_keys(current);
- current->mm->dumpable = suid_dumpable;
+ set_dumpable(current->mm, suid_dumpable);
}
/* An exec changes our domain. We are no longer part of the thread
return 0;
-mmap_failed:
- reset_files_struct(current, files);
out:
return retval;
}
{
int unsafe;
- if (bprm->e_uid != current->uid)
+ if (bprm->e_uid != current->uid) {
suid_keys(current);
+ current->pdeath_signal = 0;
+ }
exec_keys(current);
task_lock(current);
task_unlock(current);
security_bprm_post_apply_creds(bprm);
}
-
EXPORT_SYMBOL(compute_creds);
-void remove_arg_zero(struct linux_binprm *bprm)
+/*
+ * Arguments are '\0' separated strings found at the location bprm->p
+ * points to; chop off the first by relocating brpm->p to right after
+ * the first '\0' encountered.
+ */
+int remove_arg_zero(struct linux_binprm *bprm)
{
- if (bprm->argc) {
- unsigned long offset;
- char * kaddr;
- struct page *page;
+ int ret = 0;
+ unsigned long offset;
+ char *kaddr;
+ struct page *page;
- offset = bprm->p % PAGE_SIZE;
- goto inside;
+ if (!bprm->argc)
+ return 0;
- while (bprm->p++, *(kaddr+offset++)) {
- if (offset != PAGE_SIZE)
- continue;
- offset = 0;
- kunmap_atomic(kaddr, KM_USER0);
-inside:
- page = bprm->page[bprm->p/PAGE_SIZE];
- kaddr = kmap_atomic(page, KM_USER0);
+ do {
+ offset = bprm->p & ~PAGE_MASK;
+ page = get_arg_page(bprm, bprm->p, 0);
+ if (!page) {
+ ret = -EFAULT;
+ goto out;
}
+ kaddr = kmap_atomic(page, KM_USER0);
+
+ for (; offset < PAGE_SIZE && kaddr[offset];
+ offset++, bprm->p++)
+ ;
+
kunmap_atomic(kaddr, KM_USER0);
- bprm->argc--;
- }
-}
+ put_arg_page(page);
+
+ if (offset == PAGE_SIZE)
+ free_arg_page(bprm, (bprm->p >> PAGE_SHIFT) - 1);
+ } while (offset == PAGE_SIZE);
+ bprm->p++;
+ bprm->argc--;
+ ret = 0;
+
+out:
+ return ret;
+}
EXPORT_SYMBOL(remove_arg_zero);
/*
fput(bprm->file);
bprm->file = NULL;
- loader = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+ loader = bprm->vma->vm_end - sizeof(void *);
file = open_exec("/sbin/loader");
retval = PTR_ERR(file);
retval = -ENOENT;
for (try=0; try<2; try++) {
read_lock(&binfmt_lock);
- for (fmt = formats ; fmt ; fmt = fmt->next) {
+ list_for_each_entry(fmt, &formats, lh) {
int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
if (!fn)
continue;
EXPORT_SYMBOL(search_binary_handler);
+void free_bprm(struct linux_binprm *bprm)
+{
+ free_arg_pages(bprm);
+ kfree(bprm);
+}
+
/*
* sys_execve() executes a new program.
*/
{
struct linux_binprm *bprm;
struct file *file;
+ struct files_struct *displaced;
int retval;
- int i;
+
+ retval = unshare_files(&displaced);
+ if (retval)
+ goto out_ret;
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
- goto out_ret;
+ goto out_files;
file = open_exec(filename);
retval = PTR_ERR(file);
sched_exec();
- bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
-
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
- bprm->mm = mm_alloc();
- retval = -ENOMEM;
- if (!bprm->mm)
- goto out_file;
- retval = init_new_context(current, bprm->mm);
- if (retval < 0)
- goto out_mm;
+ retval = bprm_mm_init(bprm);
+ if (retval)
+ goto out_file;
- bprm->argc = count(argv, bprm->p / sizeof(void *));
+ bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
goto out_mm;
- bprm->envc = count(envp, bprm->p / sizeof(void *));
+ bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
goto out_mm;
retval = search_binary_handler(bprm,regs);
if (retval >= 0) {
- free_arg_pages(bprm);
-
/* execve success */
security_bprm_free(bprm);
acct_update_integrals(current);
- kfree(bprm);
+ free_bprm(bprm);
+ if (displaced)
+ put_files_struct(displaced);
return retval;
}
out:
- /* Something went wrong, return the inode and free the argument pages*/
- for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
- struct page * page = bprm->page[i];
- if (page)
- __free_page(page);
- }
-
if (bprm->security)
security_bprm_free(bprm);
out_mm:
if (bprm->mm)
- mmdrop(bprm->mm);
+ mmput (bprm->mm);
out_file:
if (bprm->file) {
allow_write_access(bprm->file);
fput(bprm->file);
}
-
out_kfree:
- kfree(bprm);
+ free_bprm(bprm);
+out_files:
+ if (displaced)
+ reset_files_struct(displaced);
out_ret:
return retval;
}
EXPORT_SYMBOL(set_binfmt);
-#define CORENAME_MAX_SIZE 64
-
/* format_corename will inspect the pattern parameter, and output a
* name into corename, which must have space for at least
* CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
*/
-static void format_corename(char *corename, const char *pattern, long signr)
+static int format_corename(char *corename, const char *pattern, long signr)
{
const char *pat_ptr = pattern;
char *out_ptr = corename;
char *const out_end = corename + CORENAME_MAX_SIZE;
int rc;
int pid_in_pattern = 0;
+ int ispipe = 0;
+
+ if (*pattern == '|')
+ ispipe = 1;
/* Repeat as long as we have more pattern to process and more output
space */
case 'p':
pid_in_pattern = 1;
rc = snprintf(out_ptr, out_end - out_ptr,
- "%d", current->tgid);
+ "%d", task_tgid_vnr(current));
if (rc > out_end - out_ptr)
goto out;
out_ptr += rc;
goto out;
out_ptr += rc;
break;
+ /* core limit size */
+ case 'c':
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%lu", current->signal->rlim[RLIMIT_CORE].rlim_cur);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
default:
break;
}
*
* If core_pattern does not include a %p (as is the default)
* and core_uses_pid is set, then .%pid will be appended to
- * the filename */
- if (!pid_in_pattern
+ * the filename. Do not do this for piped commands. */
+ if (!ispipe && !pid_in_pattern
&& (core_uses_pid || atomic_read(¤t->mm->mm_users) != 1)) {
rc = snprintf(out_ptr, out_end - out_ptr,
- ".%d", current->tgid);
+ ".%d", task_tgid_vnr(current));
if (rc > out_end - out_ptr)
goto out;
out_ptr += rc;
}
- out:
+out:
*out_ptr = 0;
+ return ispipe;
}
static void zap_process(struct task_struct *start)
int err = -EAGAIN;
spin_lock_irq(&tsk->sighand->siglock);
- if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT)) {
+ if (!signal_group_exit(tsk->signal)) {
tsk->signal->group_exit_code = exit_code;
zap_process(tsk);
err = 0;
return core_waiters;
}
+/*
+ * set_dumpable converts traditional three-value dumpable to two flags and
+ * stores them into mm->flags. It modifies lower two bits of mm->flags, but
+ * these bits are not changed atomically. So get_dumpable can observe the
+ * intermediate state. To avoid doing unexpected behavior, get get_dumpable
+ * return either old dumpable or new one by paying attention to the order of
+ * modifying the bits.
+ *
+ * dumpable | mm->flags (binary)
+ * old new | initial interim final
+ * ---------+-----------------------
+ * 0 1 | 00 01 01
+ * 0 2 | 00 10(*) 11
+ * 1 0 | 01 00 00
+ * 1 2 | 01 11 11
+ * 2 0 | 11 10(*) 00
+ * 2 1 | 11 11 01
+ *
+ * (*) get_dumpable regards interim value of 10 as 11.
+ */
+void set_dumpable(struct mm_struct *mm, int value)
+{
+ switch (value) {
+ case 0:
+ clear_bit(MMF_DUMPABLE, &mm->flags);
+ smp_wmb();
+ clear_bit(MMF_DUMP_SECURELY, &mm->flags);
+ break;
+ case 1:
+ set_bit(MMF_DUMPABLE, &mm->flags);
+ smp_wmb();
+ clear_bit(MMF_DUMP_SECURELY, &mm->flags);
+ break;
+ case 2:
+ set_bit(MMF_DUMP_SECURELY, &mm->flags);
+ smp_wmb();
+ set_bit(MMF_DUMPABLE, &mm->flags);
+ break;
+ }
+}
+
+int get_dumpable(struct mm_struct *mm)
+{
+ int ret;
+
+ ret = mm->flags & 0x3;
+ return (ret >= 2) ? 2 : ret;
+}
+
int do_coredump(long signr, int exit_code, struct pt_regs * regs)
{
char corename[CORENAME_MAX_SIZE + 1];
int fsuid = current->fsuid;
int flag = 0;
int ispipe = 0;
+ unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
+ char **helper_argv = NULL;
+ int helper_argc = 0;
+ char *delimit;
+
+ audit_core_dumps(signr);
binfmt = current->binfmt;
if (!binfmt || !binfmt->core_dump)
goto fail;
down_write(&mm->mmap_sem);
- if (!mm->dumpable) {
+ /*
+ * If another thread got here first, or we are not dumpable, bail out.
+ */
+ if (mm->core_waiters || !get_dumpable(mm)) {
up_write(&mm->mmap_sem);
goto fail;
}
* process nor do we know its entire history. We only know it
* was tainted so we dump it as root in mode 2.
*/
- if (mm->dumpable == 2) { /* Setuid core dump mode */
+ if (get_dumpable(mm) == 2) { /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
current->fsuid = 0; /* Dump root private */
}
- mm->dumpable = 0;
retval = coredump_wait(exit_code);
if (retval < 0)
*/
clear_thread_flag(TIF_SIGPENDING);
- if (current->signal->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
- goto fail_unlock;
-
/*
* lock_kernel() because format_corename() is controlled by sysctl, which
* uses lock_kernel()
*/
lock_kernel();
- format_corename(corename, core_pattern, signr);
+ ispipe = format_corename(corename, core_pattern, signr);
unlock_kernel();
- if (corename[0] == '|') {
+ /*
+ * Don't bother to check the RLIMIT_CORE value if core_pattern points
+ * to a pipe. Since we're not writing directly to the filesystem
+ * RLIMIT_CORE doesn't really apply, as no actual core file will be
+ * created unless the pipe reader choses to write out the core file
+ * at which point file size limits and permissions will be imposed
+ * as it does with any other process
+ */
+ if ((!ispipe) && (core_limit < binfmt->min_coredump))
+ goto fail_unlock;
+
+ if (ispipe) {
+ helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc);
+ /* Terminate the string before the first option */
+ delimit = strchr(corename, ' ');
+ if (delimit)
+ *delimit = '\0';
+ delimit = strrchr(helper_argv[0], '/');
+ if (delimit)
+ delimit++;
+ else
+ delimit = helper_argv[0];
+ if (!strcmp(delimit, current->comm)) {
+ printk(KERN_NOTICE "Recursive core dump detected, "
+ "aborting\n");
+ goto fail_unlock;
+ }
+
+ core_limit = RLIM_INFINITY;
+
/* SIGPIPE can happen, but it's just never processed */
- if(call_usermodehelper_pipe(corename+1, NULL, NULL, &file)) {
+ if (call_usermodehelper_pipe(corename+1, helper_argv, NULL,
+ &file)) {
printk(KERN_INFO "Core dump to %s pipe failed\n",
corename);
goto fail_unlock;
}
- ispipe = 1;
} else
file = filp_open(corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
but keep the previous behaviour for now. */
if (!ispipe && !S_ISREG(inode->i_mode))
goto close_fail;
+ /*
+ * Dont allow local users get cute and trick others to coredump
+ * into their pre-created files:
+ */
+ if (inode->i_uid != current->fsuid)
+ goto close_fail;
if (!file->f_op)
goto close_fail;
if (!file->f_op->write)
if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
goto close_fail;
- retval = binfmt->core_dump(signr, regs, file);
+ retval = binfmt->core_dump(signr, regs, file, core_limit);
if (retval)
current->signal->group_exit_code |= 0x80;
close_fail:
filp_close(file, NULL);
fail_unlock:
+ if (helper_argv)
+ argv_free(helper_argv);
+
current->fsuid = fsuid;
complete_all(&mm->core_done);
fail: