aboutsummaryrefslogtreecommitdiff
path: root/fs/exec.c
diff options
context:
space:
mode:
authormerge <null@invalid>2009-01-22 13:55:32 +0000
committerAndy Green <agreen@octopus.localdomain>2009-01-22 13:55:32 +0000
commitaa6f5ffbdba45aa8e19e5048648fc6c7b25376d3 (patch)
treefbb786d0ac6f8a774fd834e9ce951197e60fbffa /fs/exec.c
parentf2d78193eae5dccd3d588d2c8ea0866efc368332 (diff)
MERGE-via-pending-tracking-hist-MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040-1232632141
pending-tracking-hist top was MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040-1232632141 / fdf777a63bcb59e0dfd78bfe2c6242e01f6d4eb9 ... parent commitmessage: From: merge <null@invalid> MERGE-via-stable-tracking-hist-MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040 stable-tracking-hist top was MERGE-via-mokopatches-tracking-fix-stray-endmenu-patch-1232632040 / 90463bfd2d5a3c8b52f6e6d71024a00e052b0ced ... parent commitmessage: From: merge <null@invalid> MERGE-via-mokopatches-tracking-hist-fix-stray-endmenu-patch mokopatches-tracking-hist top was fix-stray-endmenu-patch / 3630e0be570de8057e7f8d2fe501ed353cdf34e6 ... parent commitmessage: From: Andy Green <andy@openmoko.com> fix-stray-endmenu.patch Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'fs/exec.c')
-rw-r--r--fs/exec.c281
1 files changed, 143 insertions, 138 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 4e834f16d9d..0dd60a01f1b 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -51,15 +51,12 @@
#include <linux/audit.h>
#include <linux/tracehook.h>
#include <linux/kmod.h>
+#include <linux/fsnotify.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/tlb.h>
-
-#ifdef __alpha__
-/* for /sbin/loader handling in search_binary_handler() */
-#include <linux/a.out.h>
-#endif
+#include "internal.h"
int core_uses_pid;
char core_pattern[CORENAME_MAX_SIZE] = "core";
@@ -102,7 +99,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt)
*
* Also note that we take the address to load from from the file itself.
*/
-asmlinkage long sys_uselib(const char __user * library)
+SYSCALL_DEFINE1(uselib, const char __user *, library)
{
struct file *file;
struct nameidata nd;
@@ -126,7 +123,8 @@ asmlinkage long sys_uselib(const char __user * library)
if (nd.path.mnt->mnt_flags & MNT_NOEXEC)
goto exit;
- error = vfs_permission(&nd, MAY_READ | MAY_EXEC | MAY_OPEN);
+ error = inode_permission(nd.path.dentry->d_inode,
+ MAY_READ | MAY_EXEC | MAY_OPEN);
if (error)
goto exit;
@@ -135,6 +133,8 @@ asmlinkage long sys_uselib(const char __user * library)
if (IS_ERR(file))
goto out;
+ fsnotify_open(file->f_path.dentry);
+
error = -ENOEXEC;
if(file->f_op) {
struct linux_binfmt * fmt;
@@ -232,13 +232,13 @@ static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos,
static int __bprm_mm_init(struct linux_binprm *bprm)
{
- int err = -ENOMEM;
+ int err;
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;
+ return -ENOMEM;
down_write(&mm->mmap_sem);
vma->vm_mm = mm;
@@ -251,28 +251,20 @@ static int __bprm_mm_init(struct linux_binprm *bprm)
*/
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);
+ if (err)
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);
- }
-
+ up_write(&mm->mmap_sem);
+ bprm->vma = NULL;
+ kmem_cache_free(vm_area_cachep, vma);
return err;
}
@@ -679,7 +671,7 @@ struct file *open_exec(const char *name)
if (nd.path.mnt->mnt_flags & MNT_NOEXEC)
goto out_path_put;
- err = vfs_permission(&nd, MAY_EXEC | MAY_OPEN);
+ err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
if (err)
goto out_path_put;
@@ -687,6 +679,8 @@ struct file *open_exec(const char *name)
if (IS_ERR(file))
return file;
+ fsnotify_open(file->f_path.dentry);
+
err = deny_write_access(file);
if (err) {
fput(file);
@@ -772,7 +766,6 @@ static int de_thread(struct task_struct *tsk)
struct signal_struct *sig = tsk->signal;
struct sighand_struct *oldsighand = tsk->sighand;
spinlock_t *lock = &oldsighand->siglock;
- struct task_struct *leader = NULL;
int count;
if (thread_group_empty(tsk))
@@ -810,7 +803,7 @@ static int de_thread(struct task_struct *tsk)
* and to assume its PID:
*/
if (!thread_group_leader(tsk)) {
- leader = tsk->group_leader;
+ struct task_struct *leader = tsk->group_leader;
sig->notify_count = -1; /* for exit_notify() */
for (;;) {
@@ -862,8 +855,9 @@ static int de_thread(struct task_struct *tsk)
BUG_ON(leader->exit_state != EXIT_ZOMBIE);
leader->exit_state = EXIT_DEAD;
-
write_unlock_irq(&tasklist_lock);
+
+ release_task(leader);
}
sig->group_exit_task = NULL;
@@ -872,8 +866,6 @@ static int de_thread(struct task_struct *tsk)
no_thread_group:
exit_itimers(sig);
flush_itimer_signals();
- if (leader)
- release_task(leader);
if (atomic_read(&oldsighand->count) != 1) {
struct sighand_struct *newsighand;
@@ -980,7 +972,7 @@ int flush_old_exec(struct linux_binprm * bprm)
/* This is the point of no return */
current->sas_ss_sp = current->sas_ss_size = 0;
- if (current->euid == current->uid && current->egid == current->gid)
+ if (current_euid() == current_uid() && current_egid() == current_gid())
set_dumpable(current->mm, 1);
else
set_dumpable(current->mm, suid_dumpable);
@@ -1007,16 +999,17 @@ int flush_old_exec(struct linux_binprm * bprm)
*/
current->mm->task_size = TASK_SIZE;
- if (bprm->e_uid != current->euid || bprm->e_gid != current->egid) {
- suid_keys(current);
- set_dumpable(current->mm, suid_dumpable);
+ /* install the new credentials */
+ if (bprm->cred->uid != current_euid() ||
+ bprm->cred->gid != current_egid()) {
current->pdeath_signal = 0;
} else if (file_permission(bprm->file, MAY_READ) ||
- (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
- suid_keys(current);
+ bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {
set_dumpable(current->mm, suid_dumpable);
}
+ current->personality &= ~bprm->per_clear;
+
/* An exec changes our domain. We are no longer part of the thread
group */
@@ -1033,13 +1026,50 @@ out:
EXPORT_SYMBOL(flush_old_exec);
+/*
+ * install the new credentials for this executable
+ */
+void install_exec_creds(struct linux_binprm *bprm)
+{
+ security_bprm_committing_creds(bprm);
+
+ commit_creds(bprm->cred);
+ bprm->cred = NULL;
+
+ /* cred_exec_mutex must be held at least to this point to prevent
+ * ptrace_attach() from altering our determination of the task's
+ * credentials; any time after this it may be unlocked */
+
+ security_bprm_committed_creds(bprm);
+}
+EXPORT_SYMBOL(install_exec_creds);
+
+/*
+ * determine how safe it is to execute the proposed program
+ * - the caller must hold current->cred_exec_mutex to protect against
+ * PTRACE_ATTACH
+ */
+void check_unsafe_exec(struct linux_binprm *bprm)
+{
+ struct task_struct *p = current;
+
+ bprm->unsafe = tracehook_unsafe_exec(p);
+
+ if (atomic_read(&p->fs->count) > 1 ||
+ atomic_read(&p->files->count) > 1 ||
+ atomic_read(&p->sighand->count) > 1)
+ bprm->unsafe |= LSM_UNSAFE_SHARE;
+}
+
/*
* Fill the binprm structure from the inode.
* Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
+ *
+ * This may be called multiple times for binary chains (scripts for example).
*/
int prepare_binprm(struct linux_binprm *bprm)
{
- int mode;
+ umode_t mode;
struct inode * inode = bprm->file->f_path.dentry->d_inode;
int retval;
@@ -1047,14 +1077,15 @@ int prepare_binprm(struct linux_binprm *bprm)
if (bprm->file->f_op == NULL)
return -EACCES;
- bprm->e_uid = current->euid;
- bprm->e_gid = current->egid;
+ /* clear any previous set[ug]id data from a previous binary */
+ bprm->cred->euid = current_euid();
+ bprm->cred->egid = current_egid();
- if(!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
+ if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
/* Set-uid? */
if (mode & S_ISUID) {
- current->personality &= ~PER_CLEAR_ON_SETID;
- bprm->e_uid = inode->i_uid;
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+ bprm->cred->euid = inode->i_uid;
}
/* Set-gid? */
@@ -1064,52 +1095,23 @@ int prepare_binprm(struct linux_binprm *bprm)
* executable.
*/
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
- current->personality &= ~PER_CLEAR_ON_SETID;
- bprm->e_gid = inode->i_gid;
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
+ bprm->cred->egid = inode->i_gid;
}
}
/* fill in binprm security blob */
- retval = security_bprm_set(bprm);
+ retval = security_bprm_set_creds(bprm);
if (retval)
return retval;
+ bprm->cred_prepared = 1;
- memset(bprm->buf,0,BINPRM_BUF_SIZE);
- return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+ memset(bprm->buf, 0, BINPRM_BUF_SIZE);
+ return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}
EXPORT_SYMBOL(prepare_binprm);
-static int unsafe_exec(struct task_struct *p)
-{
- 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)
- unsafe |= LSM_UNSAFE_SHARE;
-
- return unsafe;
-}
-
-void compute_creds(struct linux_binprm *bprm)
-{
- int unsafe;
-
- if (bprm->e_uid != current->uid) {
- suid_keys(current);
- current->pdeath_signal = 0;
- }
- 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);
-
/*
* Arguments are '\0' separated strings found at the location bprm->p
* points to; chop off the first by relocating brpm->p to right after
@@ -1159,43 +1161,10 @@ EXPORT_SYMBOL(remove_arg_zero);
*/
int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
{
+ unsigned int depth = bprm->recursion_depth;
int try,retval;
struct linux_binfmt *fmt;
-#ifdef __alpha__
- /* handle /sbin/loader.. */
- {
- struct exec * eh = (struct exec *) bprm->buf;
-
- if (!bprm->loader && eh->fh.f_magic == 0x183 &&
- (eh->fh.f_flags & 0x3000) == 0x3000)
- {
- struct file * file;
- unsigned long loader;
- allow_write_access(bprm->file);
- fput(bprm->file);
- bprm->file = NULL;
-
- loader = bprm->vma->vm_end - sizeof(void *);
-
- file = open_exec("/sbin/loader");
- retval = PTR_ERR(file);
- if (IS_ERR(file))
- return retval;
-
- /* Remember if the application is TASO. */
- bprm->taso = eh->ah.entry < 0x100000000UL;
-
- bprm->file = file;
- bprm->loader = loader;
- retval = prepare_binprm(bprm);
- if (retval<0)
- return retval;
- /* should call search_binary_handler recursively here,
- but it does not matter */
- }
- }
-#endif
retval = security_bprm_check(bprm);
if (retval)
return retval;
@@ -1219,8 +1188,15 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
continue;
read_unlock(&binfmt_lock);
retval = fn(bprm, regs);
+ /*
+ * Restore the depth counter to its starting value
+ * in this call, so we don't have to rely on every
+ * load_binary function to restore it on return.
+ */
+ bprm->recursion_depth = depth;
if (retval >= 0) {
- tracehook_report_exec(fmt, bprm, regs);
+ if (depth == 0)
+ tracehook_report_exec(fmt, bprm, regs);
put_binfmt(fmt);
allow_write_access(bprm->file);
if (bprm->file)
@@ -1262,6 +1238,8 @@ EXPORT_SYMBOL(search_binary_handler);
void free_bprm(struct linux_binprm *bprm)
{
free_arg_pages(bprm);
+ if (bprm->cred)
+ abort_creds(bprm->cred);
kfree(bprm);
}
@@ -1287,10 +1265,20 @@ int do_execve(char * filename,
if (!bprm)
goto out_files;
+ retval = mutex_lock_interruptible(&current->cred_exec_mutex);
+ if (retval < 0)
+ goto out_free;
+
+ retval = -ENOMEM;
+ bprm->cred = prepare_exec_creds();
+ if (!bprm->cred)
+ goto out_unlock;
+ check_unsafe_exec(bprm);
+
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
- goto out_kfree;
+ goto out_unlock;
sched_exec();
@@ -1304,14 +1292,10 @@ int do_execve(char * filename,
bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
- goto out_mm;
+ goto out;
bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
- goto out_mm;
-
- retval = security_bprm_alloc(bprm);
- if (retval)
goto out;
retval = prepare_binprm(bprm);
@@ -1333,21 +1317,18 @@ int do_execve(char * filename,
current->flags &= ~PF_KTHREAD;
retval = search_binary_handler(bprm,regs);
- if (retval >= 0) {
- /* execve success */
- security_bprm_free(bprm);
- acct_update_integrals(current);
- free_bprm(bprm);
- if (displaced)
- put_files_struct(displaced);
- return retval;
- }
+ if (retval < 0)
+ goto out;
-out:
- if (bprm->security)
- security_bprm_free(bprm);
+ /* execve succeeded */
+ mutex_unlock(&current->cred_exec_mutex);
+ acct_update_integrals(current);
+ free_bprm(bprm);
+ if (displaced)
+ put_files_struct(displaced);
+ return retval;
-out_mm:
+out:
if (bprm->mm)
mmput (bprm->mm);
@@ -1356,7 +1337,11 @@ out_file:
allow_write_access(bprm->file);
fput(bprm->file);
}
-out_kfree:
+
+out_unlock:
+ mutex_unlock(&current->cred_exec_mutex);
+
+out_free:
free_bprm(bprm);
out_files:
@@ -1388,6 +1373,7 @@ EXPORT_SYMBOL(set_binfmt);
*/
static int format_corename(char *corename, long signr)
{
+ const struct cred *cred = current_cred();
const char *pat_ptr = core_pattern;
int ispipe = (*pat_ptr == '|');
char *out_ptr = corename;
@@ -1424,7 +1410,7 @@ static int format_corename(char *corename, long signr)
/* uid */
case 'u':
rc = snprintf(out_ptr, out_end - out_ptr,
- "%d", current->uid);
+ "%d", cred->uid);
if (rc > out_end - out_ptr)
goto out;
out_ptr += rc;
@@ -1432,7 +1418,7 @@ static int format_corename(char *corename, long signr)
/* gid */
case 'g':
rc = snprintf(out_ptr, out_end - out_ptr,
- "%d", current->gid);
+ "%d", cred->gid);
if (rc > out_end - out_ptr)
goto out;
out_ptr += rc;
@@ -1700,7 +1686,7 @@ int get_dumpable(struct mm_struct *mm)
return (ret >= 2) ? 2 : ret;
}
-int do_coredump(long signr, int exit_code, struct pt_regs * regs)
+void do_coredump(long signr, int exit_code, struct pt_regs *regs)
{
struct core_state core_state;
char corename[CORENAME_MAX_SIZE + 1];
@@ -1708,8 +1694,9 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
struct linux_binfmt * binfmt;
struct inode * inode;
struct file * file;
+ const struct cred *old_cred;
+ struct cred *cred;
int retval = 0;
- int fsuid = current->fsuid;
int flag = 0;
int ispipe = 0;
unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
@@ -1722,12 +1709,20 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
binfmt = current->binfmt;
if (!binfmt || !binfmt->core_dump)
goto fail;
+
+ cred = prepare_creds();
+ if (!cred) {
+ retval = -ENOMEM;
+ goto fail;
+ }
+
down_write(&mm->mmap_sem);
/*
* If another thread got here first, or we are not dumpable, bail out.
*/
if (mm->core_state || !get_dumpable(mm)) {
up_write(&mm->mmap_sem);
+ put_cred(cred);
goto fail;
}
@@ -1738,12 +1733,16 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
*/
if (get_dumpable(mm) == 2) { /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
- current->fsuid = 0; /* Dump root private */
+ cred->fsuid = 0; /* Dump root private */
}
retval = coredump_wait(exit_code, &core_state);
- if (retval < 0)
+ if (retval < 0) {
+ put_cred(cred);
goto fail;
+ }
+
+ old_cred = override_creds(cred);
/*
* Clear any false indication of pending signals that might
@@ -1771,6 +1770,11 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
if (ispipe) {
helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc);
+ if (!helper_argv) {
+ printk(KERN_WARNING "%s failed to allocate memory\n",
+ __func__);
+ goto fail_unlock;
+ }
/* Terminate the string before the first option */
delimit = strchr(corename, ' ');
if (delimit)
@@ -1815,7 +1819,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
* Dont allow local users get cute and trick others to coredump
* into their pre-created files:
*/
- if (inode->i_uid != current->fsuid)
+ if (inode->i_uid != current_fsuid())
goto close_fail;
if (!file->f_op)
goto close_fail;
@@ -1834,8 +1838,9 @@ fail_unlock:
if (helper_argv)
argv_free(helper_argv);
- current->fsuid = fsuid;
+ revert_creds(old_cred);
+ put_cred(cred);
coredump_finish(mm);
fail:
- return retval;
+ return;
}