X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fexec.c;h=c567e1a203156198040049474868bae6a6d52ccf;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=f73d2c4cc1e602e7fced42659d2af67ad23e896e;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/fs/exec.c b/fs/exec.c index f73d2c4cc..c567e1a20 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -22,7 +22,6 @@ * formats. */ -#include #include #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -41,14 +41,18 @@ #include #include #include -#include +#include #include #include #include #include +#include +#include +#include +#include +#include #include -#include #include #ifdef CONFIG_KMOD @@ -57,10 +61,13 @@ int core_uses_pid; char core_pattern[65] = "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 rwlock_t binfmt_lock = RW_LOCK_UNLOCKED; +static DEFINE_RWLOCK(binfmt_lock); int register_binfmt(struct linux_binfmt * fmt) { @@ -122,8 +129,7 @@ asmlinkage long sys_uselib(const char __user * library) struct nameidata nd; int error; - nd.intent.open.flags = FMODE_READ; - error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd); + error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC); if (error) goto out; @@ -131,11 +137,11 @@ asmlinkage long sys_uselib(const char __user * library) if (!S_ISREG(nd.dentry->d_inode->i_mode)) goto exit; - error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC, &nd); + error = vfs_permission(&nd, MAY_READ | MAY_EXEC); if (error) goto exit; - file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); + file = nameidata_to_filp(&nd, O_RDONLY); error = PTR_ERR(file); if (IS_ERR(file)) goto out; @@ -163,6 +169,7 @@ asmlinkage long sys_uselib(const char __user * library) out: return error; exit: + release_open_intent(&nd); path_release(&nd); goto out; } @@ -185,6 +192,7 @@ static int count(char __user * __user * argv, int max) argv++; if(++i > max) return -E2BIG; + cond_resched(); } } return i; @@ -195,7 +203,8 @@ static int count(char __user * __user * argv, int max) * memory to free pages in kernel mem. These are in a format ready * to be put directly into the top of new user memory. */ -int copy_strings(int argc,char __user * __user * argv, struct linux_binprm *bprm) +static int copy_strings(int argc, char __user * __user * argv, + struct linux_binprm *bprm) { struct page *kmapped_page = NULL; char *kaddr = NULL; @@ -293,61 +302,50 @@ EXPORT_SYMBOL(copy_strings_kernel); * This routine is used to map in a page into an address space: needed by * execve() for the initial stack and environment pages. * - * tsk->mmap_sem is held for writing. + * vma->vm_mm->mmap_sem is held for writing. */ -void put_dirty_page(struct task_struct *tsk, struct page *page, - unsigned long address, pgprot_t prot) +void install_arg_page(struct vm_area_struct *vma, + struct page *page, unsigned long address) { - pgd_t * pgd; - pmd_t * pmd; + struct mm_struct *mm = vma->vm_mm; pte_t * pte; - struct pte_chain *pte_chain; - - if (page_count(page) != 1) - printk(KERN_ERR "mem_map disagrees with %p at %08lx\n", - page, address); - - pgd = pgd_offset(tsk->mm, address); - pte_chain = pte_chain_alloc(GFP_KERNEL); - if (!pte_chain) - goto out_sig; - spin_lock(&tsk->mm->page_table_lock); - pmd = pmd_alloc(tsk->mm, pgd, address); - if (!pmd) + spinlock_t *ptl; + + if (unlikely(anon_vma_prepare(vma))) goto out; - pte = pte_alloc_map(tsk->mm, pmd, address); + + flush_dcache_page(page); + pte = get_locked_pte(mm, address, &ptl); if (!pte) goto out; if (!pte_none(*pte)) { - pte_unmap(pte); + pte_unmap_unlock(pte, ptl); goto out; } + inc_mm_counter(mm, anon_rss); lru_cache_add_active(page); - flush_dcache_page(page); - set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, prot)))); - pte_chain = page_add_rmap(page, pte, pte_chain); - pte_unmap(pte); - tsk->mm->rss++; - spin_unlock(&tsk->mm->page_table_lock); + 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 */ - pte_chain_free(pte_chain); return; out: - spin_unlock(&tsk->mm->page_table_lock); -out_sig: __free_page(page); - force_sig(SIGKILL, tsk); - pte_chain_free(pte_chain); - return; + force_sig(SIGKILL, current); } -int setup_arg_pages(struct linux_binprm *bprm, int executable_stack) +#define EXTRA_STACK_VM_PAGES 20 /* random */ + +int setup_arg_pages(struct linux_binprm *bprm, + unsigned long stack_top, + int executable_stack) { unsigned long stack_base; struct vm_area_struct *mpnt; struct mm_struct *mm = current->mm; - int i; + int i, ret; long arg_size; #ifdef CONFIG_STACK_GROWSUP @@ -379,14 +377,14 @@ int setup_arg_pages(struct linux_binprm *bprm, int executable_stack) memmove(to, to + offset, PAGE_SIZE - offset); kunmap(bprm->page[j - 1]); - /* Adjust bprm->p to point to the end of the strings. */ - bprm->p = PAGE_SIZE * i - offset; - /* Limit stack size to 1GB */ - stack_base = current->rlim[RLIMIT_STACK].rlim_max; + 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); + 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; mm->arg_start = stack_base; arg_size = i << PAGE_SHIFT; @@ -395,12 +393,15 @@ int setup_arg_pages(struct linux_binprm *bprm, int executable_stack) while (i < MAX_ARG_PAGES) bprm->page[i++] = NULL; #else - stack_base = STACK_TOP - MAX_ARG_PAGES * PAGE_SIZE; - mm->arg_start = bprm->p + stack_base; - arg_size = STACK_TOP - (PAGE_MASK & (unsigned long) mm->arg_start); + stack_base = arch_align_stack(stack_top - MAX_ARG_PAGES*PAGE_SIZE); + stack_base = PAGE_ALIGN(stack_base); + bprm->p += stack_base; + mm->arg_start = bprm->p; + arg_size = stack_top - (PAGE_MASK & (unsigned long) mm->arg_start); #endif - bprm->p += stack_base; + arg_size += EXTRA_STACK_VM_PAGES * PAGE_SIZE; + if (bprm->loader) bprm->loader += stack_base; bprm->exec += stack_base; @@ -409,21 +410,17 @@ int setup_arg_pages(struct linux_binprm *bprm, int executable_stack) if (!mpnt) return -ENOMEM; - if (security_vm_enough_memory(arg_size >> PAGE_SHIFT)) { - kmem_cache_free(vm_area_cachep, mpnt); - return -ENOMEM; - } + memset(mpnt, 0, sizeof(*mpnt)); down_write(&mm->mmap_sem); { mpnt->vm_mm = mm; #ifdef CONFIG_STACK_GROWSUP mpnt->vm_start = stack_base; - mpnt->vm_end = PAGE_MASK & - (PAGE_SIZE - 1 + (unsigned long) bprm->p); + mpnt->vm_end = stack_base + arg_size; #else - mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; - mpnt->vm_end = STACK_TOP; + 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 @@ -434,22 +431,22 @@ int setup_arg_pages(struct linux_binprm *bprm, int executable_stack) 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]; - mpnt->vm_ops = NULL; - mpnt->vm_pgoff = 0; - mpnt->vm_file = NULL; - INIT_LIST_HEAD(&mpnt->shared); - mpnt->vm_private_data = (void *) 0; - insert_vm_struct(mm, mpnt); - mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + if ((ret = insert_vm_struct(mm, mpnt))) { + up_write(&mm->mmap_sem); + kmem_cache_free(vm_area_cachep, mpnt); + return ret; + } + vx_vmpages_sub(mm, mm->total_vm - vma_pages(mpnt)); + mm->stack_vm = mm->total_vm; } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { struct page *page = bprm->page[i]; if (page) { bprm->page[i] = NULL; - put_dirty_page(current, page, stack_base, - mpnt->vm_page_prot); + install_arg_page(mpnt, page, stack_base); } stack_base += PAGE_SIZE; } @@ -483,8 +480,7 @@ struct file *open_exec(const char *name) int err; struct file *file; - nd.intent.open.flags = FMODE_READ; - err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd); + err = path_lookup_open(AT_FDCWD, name, LOOKUP_FOLLOW, &nd, FMODE_READ|FMODE_EXEC); file = ERR_PTR(err); if (!err) { @@ -492,12 +488,10 @@ struct file *open_exec(const char *name) file = ERR_PTR(-EACCES); if (!(nd.mnt->mnt_flags & MNT_NOEXEC) && S_ISREG(inode->i_mode)) { - int err = permission(inode, MAY_EXEC, &nd); - if (!err && !(inode->i_mode & 0111)) - err = -EACCES; + int err = vfs_permission(&nd, MAY_EXEC); file = ERR_PTR(err); if (!err) { - file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); + file = nameidata_to_filp(&nd, O_RDONLY); if (!IS_ERR(file)) { err = deny_write_access(file); if (err) { @@ -509,6 +503,7 @@ out: return file; } } + release_open_intent(&nd); path_release(&nd); } goto out; @@ -538,25 +533,36 @@ static int exec_mmap(struct mm_struct *mm) struct task_struct *tsk; struct mm_struct * old_mm, *active_mm; - /* Add it to the list of mm's */ - spin_lock(&mmlist_lock); - list_add(&mm->mmlist, &init_mm.mmlist); - mmlist_nr++; - spin_unlock(&mmlist_lock); - /* Notify parent that we're no longer interested in the old VM */ tsk = current; old_mm = current->mm; mm_release(tsk, old_mm); + if (old_mm) { + /* + * Make sure that if there is a core dump in progress + * for the old mm, we get out and die instead of going + * through with the exec. We must hold mmap_sem around + * checking core_waiters and changing tsk->mm. The + * core-inducing thread will increment core_waiters for + * each thread whose ->mm == old_mm. + */ + down_read(&old_mm->mmap_sem); + if (unlikely(old_mm->core_waiters)) { + up_read(&old_mm->mmap_sem); + return -EINTR; + } + } task_lock(tsk); active_mm = tsk->active_mm; tsk->mm = mm; tsk->active_mm = mm; activate_mm(active_mm, mm); task_unlock(tsk); + arch_pick_mmap_layout(mm); if (old_mm) { - if (active_mm != old_mm) BUG(); + up_read(&old_mm->mmap_sem); + BUG_ON(active_mm != old_mm); mmput(old_mm); return 0; } @@ -570,54 +576,28 @@ static int exec_mmap(struct mm_struct *mm) * disturbing other processes. (Other processes might share the signal * table via the CLONE_SIGHAND option to clone().) */ -static inline int de_thread(struct task_struct *tsk) +static int de_thread(struct task_struct *tsk) { - struct signal_struct *newsig, *oldsig = tsk->signal; + struct signal_struct *sig = tsk->signal; struct sighand_struct *newsighand, *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) + 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; - spin_lock_init(&newsighand->siglock); - atomic_set(&newsighand->count, 1); - memcpy(newsighand->action, oldsighand->action, sizeof(newsighand->action)); - - /* - * See if we need to allocate a new signal structure - */ - newsig = NULL; - if (atomic_read(&oldsig->count) > 1) { - newsig = kmem_cache_alloc(signal_cachep, GFP_KERNEL); - if (!newsig) { - kmem_cache_free(sighand_cachep, newsighand); - return -ENOMEM; - } - atomic_set(&newsig->count, 1); - newsig->group_exit = 0; - newsig->group_exit_code = 0; - newsig->group_exit_task = NULL; - newsig->group_stop_count = 0; - newsig->curr_target = NULL; - init_sigpending(&newsig->shared_pending); - INIT_LIST_HEAD(&newsig->posix_timers); - - newsig->tty = oldsig->tty; - newsig->pgrp = oldsig->pgrp; - newsig->session = oldsig->session; - newsig->leader = oldsig->leader; - newsig->tty_old_pgrp = oldsig->tty_old_pgrp; - } - if (thread_group_empty(current)) goto no_thread_group; @@ -627,7 +607,7 @@ static inline int de_thread(struct task_struct *tsk) */ read_lock(&tasklist_lock); spin_lock_irq(lock); - if (oldsig->group_exit) { + if (sig->flags & SIGNAL_GROUP_EXIT) { /* * Another group action in progress, just * return so that the signal is processed. @@ -635,28 +615,49 @@ static inline int de_thread(struct task_struct *tsk) spin_unlock_irq(lock); read_unlock(&tasklist_lock); kmem_cache_free(sighand_cachep, newsighand); - if (newsig) - kmem_cache_free(signal_cachep, newsig); return -EAGAIN; } - oldsig->group_exit = 1; + + /* + * 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(current->group_leader == child_reaper)) + child_reaper = current; + zap_other_threads(current); read_unlock(&tasklist_lock); /* * Account for the thread group leader hanging around: */ - count = 2; - if (current->pid == current->tgid) - count = 1; - while (atomic_read(&oldsig->count) > count) { - oldsig->group_exit_task = current; - oldsig->notify_count = count; + count = 1; + if (!thread_group_leader(current)) { + 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 = current; + spin_unlock_irq(lock); + if (hrtimer_cancel(&sig->real_timer)) + hrtimer_restart(&sig->real_timer); + spin_lock_irq(lock); + } + while (atomic_read(&sig->count) > count) { + sig->group_exit_task = current; + 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); /* @@ -664,101 +665,109 @@ static inline int de_thread(struct task_struct *tsk) * do is to wait for the thread group leader to become inactive, * and to assume its PID: */ - if (current->pid != current->tgid) { - struct task_struct *leader = current->group_leader, *parent; - struct dentry *proc_dentry1, *proc_dentry2; - unsigned long state, ptrace; - + if (!thread_group_leader(current)) { /* * Wait for the thread group leader to be a zombie. * It should already be zombie at this point, most * of the time. */ - while (leader->state != TASK_ZOMBIE) + leader = current->group_leader; + while (leader->exit_state != EXIT_ZOMBIE) yield(); - spin_lock(&leader->proc_lock); - spin_lock(¤t->proc_lock); - proc_dentry1 = proc_pid_unhash(current); - proc_dentry2 = proc_pid_unhash(leader); + /* + * The only record we have of the real-time age of a + * process, regardless of execs it's done, is start_time. + * All the past CPU time is accumulated in signal_struct + * from sister threads now dead. But in this non-leader + * exec, nothing survives from the original leader thread, + * whose birth marks the true age of this process now. + * When we take on its identity by switching to its PID, we + * also take its birthdate (always earlier than our own). + */ + current->start_time = leader->start_time; + write_lock_irq(&tasklist_lock); - if (leader->tgid != current->tgid) - BUG(); - if (current->pid == current->tgid) - BUG(); + BUG_ON(leader->tgid != current->tgid); + BUG_ON(current->pid == current->tgid); /* * An exec() starts a new thread group with the * TGID of the previous thread group. Rehash the * two threads with a switched PID, and release * the former thread group leader: */ - ptrace = leader->ptrace; - parent = leader->parent; - ptrace_unlink(current); - ptrace_unlink(leader); - remove_parent(current); - remove_parent(leader); - - switch_exec_pids(leader, current); + /* Become a process group leader with the old leader's pid. + * Note: The old leader also uses thispid until release_task + * is called. Odd but simple and correct. + */ + detach_pid(current, PIDTYPE_PID); + current->pid = leader->pid; + attach_pid(current, PIDTYPE_PID, current->pid); + attach_pid(current, PIDTYPE_PGID, current->signal->pgrp); + attach_pid(current, PIDTYPE_SID, current->signal->session); + list_replace_rcu(&leader->tasks, ¤t->tasks); - current->parent = current->real_parent = leader->real_parent; - leader->parent = leader->real_parent = child_reaper; current->group_leader = current; - leader->group_leader = leader; + leader->group_leader = current; - add_parent(current, current->parent); - add_parent(leader, leader->parent); - if (ptrace) { - current->ptrace = ptrace; - __ptrace_link(current, parent); - } + /* Reduce leader to a thread */ + detach_pid(leader, PIDTYPE_PGID); + detach_pid(leader, PIDTYPE_SID); - list_del(¤t->tasks); - list_add_tail(¤t->tasks, &init_task.tasks); current->exit_signal = SIGCHLD; - state = leader->state; - write_unlock_irq(&tasklist_lock); - spin_unlock(&leader->proc_lock); - spin_unlock(¤t->proc_lock); - proc_pid_flush(proc_dentry1); - proc_pid_flush(proc_dentry2); + BUG_ON(leader->exit_state != EXIT_ZOMBIE); + leader->exit_state = EXIT_DEAD; - if (state != TASK_ZOMBIE) - BUG(); - release_task(leader); + 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; + no_thread_group: + exit_itimers(sig); + if (leader) + release_task(leader); - write_lock_irq(&tasklist_lock); - spin_lock(&oldsighand->siglock); - spin_lock(&newsighand->siglock); + BUG_ON(atomic_read(&sig->count) != 1); - if (current == oldsig->curr_target) - oldsig->curr_target = next_thread(current); - if (newsig) - current->signal = newsig; - current->sighand = newsighand; - init_sigpending(¤t->pending); - recalc_sigpending(); + if (atomic_read(&oldsighand->count) == 1) { + /* + * 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. + */ + atomic_set(&newsighand->count, 1); + memcpy(newsighand->action, oldsighand->action, + sizeof(newsighand->action)); - spin_unlock(&newsighand->siglock); - spin_unlock(&oldsighand->siglock); - write_unlock_irq(&tasklist_lock); + write_lock_irq(&tasklist_lock); + spin_lock(&oldsighand->siglock); + spin_lock_nested(&newsighand->siglock, SINGLE_DEPTH_NESTING); - if (newsig && atomic_dec_and_test(&oldsig->count)) - kmem_cache_free(signal_cachep, oldsig); + rcu_assign_pointer(current->sighand, newsighand); + recalc_sigpending(); - if (atomic_dec_and_test(&oldsighand->count)) - kmem_cache_free(sighand_cachep, oldsighand); + spin_unlock(&newsighand->siglock); + spin_unlock(&oldsighand->siglock); + write_unlock_irq(&tasklist_lock); - if (!thread_group_empty(current)) - BUG(); - if (current->tgid != current->pid) - BUG(); + if (atomic_dec_and_test(&oldsighand->count)) + kmem_cache_free(sighand_cachep, oldsighand); + } + + BUG_ON(!thread_group_leader(current)); return 0; } @@ -767,9 +776,10 @@ no_thread_group: * so that a new one can be started */ -static inline void flush_old_files(struct files_struct * files) +static void flush_old_files(struct files_struct * files) { long j = -1; + struct fdtable *fdt; spin_lock(&files->file_lock); for (;;) { @@ -777,12 +787,13 @@ static inline void flush_old_files(struct files_struct * files) j++; i = j * __NFDBITS; - if (i >= files->max_fds || i >= files->max_fdset) + fdt = files_fdtable(files); + if (i >= fdt->max_fds || i >= fdt->max_fdset) break; - set = files->close_on_exec->fds_bits[j]; + set = fdt->close_on_exec->fds_bits[j]; if (!set) continue; - files->close_on_exec->fds_bits[j] = 0; + fdt->close_on_exec->fds_bits[j] = 0; spin_unlock(&files->file_lock); for ( ; set ; i++,set >>= 1) { if (set & 1) { @@ -795,11 +806,27 @@ static inline void flush_old_files(struct files_struct * files) spin_unlock(&files->file_lock); } +void 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); +} + +void set_task_comm(struct task_struct *tsk, char *buf) +{ + task_lock(tsk); + strlcpy(tsk->comm, buf, sizeof(tsk->comm)); + task_unlock(tsk); +} + int flush_old_exec(struct linux_binprm * bprm) { char * name; int i, ch, retval; struct files_struct *files; + char tcomm[sizeof(current->comm)]; /* * Make sure we have a private signal table and that @@ -828,28 +855,43 @@ int flush_old_exec(struct linux_binprm * bprm) bprm->mm = NULL; /* We're using it now */ /* This is the point of no return */ - steal_locks(files); 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; + else + current->mm->dumpable = suid_dumpable; + name = bprm->filename; + + /* Copies the binary name from after last slash */ for (i=0; (ch = *(name++)) != '\0';) { if (ch == '/') - i = 0; + i = 0; /* overwrite what we wrote */ else - if (i < 15) - current->comm[i++] = ch; + if (i < (sizeof(tcomm) - 1)) + tcomm[i++] = ch; } - current->comm[i] = '\0'; + tcomm[i] = '\0'; + set_task_comm(current, tcomm); + current->flags &= ~PF_RANDOMIZE; flush_thread(); + /* Set the new mm task size. We have to do that late because it may + * depend on TIF_32BIT which is only updated in flush_thread() on + * some architectures like powerpc + */ + current->mm->task_size = TASK_SIZE; + if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || - permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL)) - current->mm->dumpable = 0; + file_permission(bprm->file, MAY_READ) || + (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { + suid_keys(current); + current->mm->dumpable = suid_dumpable; + } /* An exec changes our domain. We are no longer part of the thread group */ @@ -881,12 +923,6 @@ int prepare_binprm(struct linux_binprm *bprm) int retval; mode = inode->i_mode; - /* - * Check execute perms again - if the caller has CAP_DAC_OVERRIDE, - * vfs_permission lets a non-executable through - */ - if (!(mode & 0111)) /* with at least _one_ execute bit set */ - return -EACCES; if (bprm->file->f_op == NULL) return -EACCES; @@ -895,8 +931,10 @@ int prepare_binprm(struct linux_binprm *bprm) if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) { /* Set-uid? */ - if (mode & S_ISUID) + if (mode & S_ISUID) { + current->personality &= ~PER_CLEAR_ON_SETID; bprm->e_uid = inode->i_uid; + } /* Set-gid? */ /* @@ -904,8 +942,10 @@ int prepare_binprm(struct linux_binprm *bprm) * is a candidate for mandatory locking, not a setgid * executable. */ - if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) + if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { + current->personality &= ~PER_CLEAR_ON_SETID; bprm->e_gid = inode->i_gid; + } } /* fill in binprm security blob */ @@ -919,15 +959,9 @@ int prepare_binprm(struct linux_binprm *bprm) EXPORT_SYMBOL(prepare_binprm); -static inline int unsafe_exec(struct task_struct *p) +static int unsafe_exec(struct task_struct *p) { - int unsafe = 0; - if (p->ptrace & PT_PTRACED) { - if (p->ptrace & PT_PTRACE_CAP) - unsafe |= LSM_UNSAFE_PTRACE_CAP; - else - unsafe |= LSM_UNSAFE_PTRACE; - } + int unsafe = tracehook_unsafe_exec(p); if (atomic_read(&p->fs->count) > 1 || atomic_read(&p->files->count) > 1 || atomic_read(&p->sighand->count) > 1) @@ -939,10 +973,16 @@ static inline int unsafe_exec(struct task_struct *p) void compute_creds(struct linux_binprm *bprm) { int unsafe; + + if (bprm->e_uid != current->uid) + suid_keys(current); + exec_keys(current); + task_lock(current); unsafe = unsafe_exec(current); security_bprm_apply_creds(bprm, unsafe); task_unlock(current); + security_bprm_post_apply_creds(bprm); } EXPORT_SYMBOL(compute_creds); @@ -978,7 +1018,7 @@ EXPORT_SYMBOL(remove_arg_zero); */ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) { - int try,retval=0; + int try,retval; struct linux_binfmt *fmt; #ifdef __alpha__ /* handle /sbin/loader.. */ @@ -1003,7 +1043,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) return retval; /* Remember if the application is TASO. */ - bprm->sh_bang = eh->ah.entry < 0x100000000; + bprm->sh_bang = eh->ah.entry < 0x100000000UL; bprm->file = file; bprm->loader = loader; @@ -1022,6 +1062,12 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) /* kernel module loader fixup */ /* so we don't try to load run modprobe in kernel space. */ set_fs(USER_DS); + + retval = audit_bprm(bprm); + if (retval) + return retval; + + retval = -ENOENT; for (try=0; try<2; try++) { read_lock(&binfmt_lock); for (fmt = formats ; fmt ; fmt = fmt->next) { @@ -1039,6 +1085,8 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) fput(bprm->file); bprm->file = NULL; current->did_exec = 1; + proc_exec_connector(current); + tracehook_report_exec(bprm, regs); return retval; } read_lock(&binfmt_lock); @@ -1078,101 +1126,105 @@ int do_execve(char * filename, char __user *__user *envp, struct pt_regs * regs) { - struct linux_binprm bprm; + struct linux_binprm *bprm; struct file *file; int retval; int i; - sched_balance_exec(); + retval = -ENOMEM; + bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); + if (!bprm) + goto out_ret; file = open_exec(filename); - retval = PTR_ERR(file); if (IS_ERR(file)) - return retval; + goto out_kfree; + + sched_exec(); + + bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); - bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); - memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); - - bprm.file = file; - bprm.filename = filename; - bprm.interp = filename; - bprm.sh_bang = 0; - bprm.loader = 0; - bprm.exec = 0; - bprm.security = NULL; - bprm.mm = mm_alloc(); + bprm->file = file; + bprm->filename = filename; + bprm->interp = filename; + bprm->mm = mm_alloc(); retval = -ENOMEM; - if (!bprm.mm) + if (!bprm->mm) goto out_file; - retval = init_new_context(current, bprm.mm); + retval = init_new_context(current, bprm->mm); if (retval < 0) goto out_mm; - bprm.argc = count(argv, bprm.p / sizeof(void *)); - if ((retval = bprm.argc) < 0) + bprm->argc = count(argv, bprm->p / sizeof(void *)); + if ((retval = bprm->argc) < 0) goto out_mm; - bprm.envc = count(envp, bprm.p / sizeof(void *)); - if ((retval = bprm.envc) < 0) + bprm->envc = count(envp, bprm->p / sizeof(void *)); + if ((retval = bprm->envc) < 0) goto out_mm; - retval = security_bprm_alloc(&bprm); + retval = security_bprm_alloc(bprm); if (retval) goto out; - retval = prepare_binprm(&bprm); + retval = prepare_binprm(bprm); if (retval < 0) goto out; - retval = copy_strings_kernel(1, &bprm.filename, &bprm); + retval = copy_strings_kernel(1, &bprm->filename, bprm); if (retval < 0) goto out; - bprm.exec = bprm.p; - retval = copy_strings(bprm.envc, envp, &bprm); + bprm->exec = bprm->p; + retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) goto out; - retval = copy_strings(bprm.argc, argv, &bprm); + retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out; - retval = search_binary_handler(&bprm,regs); + retval = search_binary_handler(bprm,regs); if (retval >= 0) { - free_arg_pages(&bprm); + free_arg_pages(bprm); /* execve success */ - security_bprm_free(&bprm); + security_bprm_free(bprm); + acct_update_integrals(current); + kfree(bprm); 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]; + struct page * page = bprm->page[i]; if (page) __free_page(page); } - if (bprm.security) - security_bprm_free(&bprm); + if (bprm->security) + security_bprm_free(bprm); out_mm: - if (bprm.mm) - mmdrop(bprm.mm); + if (bprm->mm) + mmdrop(bprm->mm); out_file: - if (bprm.file) { - allow_write_access(bprm.file); - fput(bprm.file); + if (bprm->file) { + allow_write_access(bprm->file); + fput(bprm->file); } + +out_kfree: + kfree(bprm); + +out_ret: return retval; } -EXPORT_SYMBOL(do_execve); - int set_binfmt(struct linux_binfmt *new) { struct linux_binfmt *old = current->binfmt; @@ -1195,7 +1247,7 @@ EXPORT_SYMBOL(set_binfmt); * name into corename, which must have space for at least * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator. */ -void format_corename(char *corename, const char *pattern, long signr) +static void format_corename(char *corename, const char *pattern, long signr) { const char *pat_ptr = pattern; char *out_ptr = corename; @@ -1268,7 +1320,7 @@ void format_corename(char *corename, const char *pattern, long signr) case 'h': down_read(&uts_sem); rc = snprintf(out_ptr, out_end - out_ptr, - "%s", system_utsname.nodename); + "%s", vx_new_uts(nodename)); up_read(&uts_sem); if (rc > out_end - out_ptr) goto out; @@ -1305,49 +1357,102 @@ void format_corename(char *corename, const char *pattern, long signr) *out_ptr = 0; } -static void zap_threads (struct mm_struct *mm) +static void zap_process(struct task_struct *start) +{ + struct task_struct *t; + + start->signal->flags = SIGNAL_GROUP_EXIT; + start->signal->group_stop_count = 0; + + t = start; + do { + if (t != current && t->mm) { + t->mm->core_waiters++; + sigaddset(&t->pending.signal, SIGKILL); + signal_wake_up(t, 1); + } + } while ((t = next_thread(t)) != start); +} + +static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, + int exit_code) { struct task_struct *g, *p; + unsigned long flags; + int err = -EAGAIN; + + spin_lock_irq(&tsk->sighand->siglock); + if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT)) { + tsk->signal->group_exit_code = exit_code; + zap_process(tsk); + err = 0; + } + spin_unlock_irq(&tsk->sighand->siglock); + if (err) + return err; + + if (atomic_read(&mm->mm_users) == mm->core_waiters + 1) + goto done; + + rcu_read_lock(); + for_each_process(g) { + if (g == tsk->group_leader) + continue; + + p = g; + do { + if (p->mm) { + if (p->mm == mm) { + /* + * p->sighand can't disappear, but + * may be changed by de_thread() + */ + lock_task_sighand(p, &flags); + zap_process(p); + unlock_task_sighand(p, &flags); + } + break; + } + } while ((p = next_thread(p)) != g); + } + rcu_read_unlock(); +done: + return mm->core_waiters; +} + +static int coredump_wait(int exit_code) +{ struct task_struct *tsk = current; - struct completion *vfork_done = tsk->vfork_done; + struct mm_struct *mm = tsk->mm; + struct completion startup_done; + struct completion *vfork_done; + int core_waiters; + + init_completion(&mm->core_done); + init_completion(&startup_done); + mm->core_startup_done = &startup_done; + + core_waiters = zap_threads(tsk, mm, exit_code); + up_write(&mm->mmap_sem); + + if (unlikely(core_waiters < 0)) + goto fail; /* * Make sure nobody is waiting for us to release the VM, * otherwise we can deadlock when we wait on each other */ + vfork_done = tsk->vfork_done; if (vfork_done) { tsk->vfork_done = NULL; complete(vfork_done); } - read_lock(&tasklist_lock); - do_each_thread(g,p) - if (mm == p->mm && p != tsk) { - force_sig_specific(SIGKILL, p); - mm->core_waiters++; - } - while_each_thread(g,p); - - read_unlock(&tasklist_lock); -} - -static void coredump_wait(struct mm_struct *mm) -{ - DECLARE_COMPLETION(startup_done); - - mm->core_waiters++; /* let other threads block */ - mm->core_startup_done = &startup_done; - - /* give other threads a chance to run: */ - yield(); - - zap_threads(mm); - if (--mm->core_waiters) { - up_write(&mm->mmap_sem); + if (core_waiters) wait_for_completion(&startup_done); - } else - up_write(&mm->mmap_sem); +fail: BUG_ON(mm->core_waiters); + return core_waiters; } int do_coredump(long signr, int exit_code, struct pt_regs * regs) @@ -1358,27 +1463,52 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) struct inode * inode; struct file * file; int retval = 0; + int fsuid = current->fsuid; + int flag = 0; - lock_kernel(); binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) goto fail; + if (current->tux_exit) + current->tux_exit(); down_write(&mm->mmap_sem); if (!mm->dumpable) { up_write(&mm->mmap_sem); goto fail; } + + /* + * We cannot trust fsuid as being the "true" uid of the + * 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 */ + flag = O_EXCL; /* Stop rewrite attacks */ + current->fsuid = 0; /* Dump root private */ + } mm->dumpable = 0; - init_completion(&mm->core_done); - current->signal->group_exit = 1; - current->signal->group_exit_code = exit_code; - coredump_wait(mm); - if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) + retval = coredump_wait(exit_code); + if (retval < 0) + goto fail; + + /* + * Clear any false indication of pending signals that might + * be seen by the filesystem code called to write the core file. + */ + clear_thread_flag(TIF_SIGPENDING); + + if (current->signal->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) goto fail_unlock; - format_corename(corename, core_pattern, signr); - file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600); + /* + * lock_kernel() because format_corename() is controlled by sysctl, which + * uses lock_kernel() + */ + lock_kernel(); + format_corename(corename, core_pattern, signr); + unlock_kernel(); + file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 0600); if (IS_ERR(file)) goto fail_unlock; inode = file->f_dentry->d_inode; @@ -1393,17 +1523,18 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) goto close_fail; if (!file->f_op->write) goto close_fail; - if (do_truncate(file->f_dentry, 0) != 0) + if (do_truncate(file->f_dentry, 0, 0, file) != 0) goto close_fail; retval = binfmt->core_dump(signr, regs, file); - current->signal->group_exit_code |= 0x80; + if (retval) + current->signal->group_exit_code |= 0x80; close_fail: filp_close(file, NULL); fail_unlock: + current->fsuid = fsuid; complete_all(&mm->core_done); fail: - unlock_kernel(); return retval; }