📄 exec.c
字号:
/*
* linux/fs/exec.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
/*
* #!-checking implemented by tytso.
*/
/*
* Demand-loading implemented 01.12.91 - no need to read anything but
* the header into memory. The inode of the executable is put into
* "current->executable", and page faults do the actual loading. Clean.
*
* Once more I can proudly say that linux stood up to being changed: it
* was less than 2 hours work to get demand-loading completely implemented.
*
* Demand loading changed July 1993 by Eric Youngdale. Use mmap instead,
* current->executable is only used by the procfs. This allows a dispatch
* table to check for several different types of binary formats. We keep
* trying until we recognize the file or we run out of supported binary
* formats.
*/
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/file.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/init.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/spinlock.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
static struct linux_binfmt *formats;
static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED;
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;
write_unlock(&binfmt_lock);
return 0;
}
int 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;
}
write_unlock(&binfmt_lock);
return -EINVAL;
}
static inline void put_binfmt(struct linux_binfmt * fmt)
{
if (fmt->module)
__MOD_DEC_USE_COUNT(fmt->module);
}
/*
* Note that a shared library must be both readable and executable due to
* security reasons.
*
* Also note that we take the address to load from from the file itself.
*/
asmlinkage long sys_uselib(const char * library)
{
struct file * file;
struct nameidata nd;
int error;
error = user_path_walk(library, &nd);
if (error)
goto out;
error = -EINVAL;
if (!S_ISREG(nd.dentry->d_inode->i_mode))
goto exit;
error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC);
if (error)
goto exit;
file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
error = PTR_ERR(file);
if (IS_ERR(file))
goto out;
error = -ENOEXEC;
if(file->f_op && file->f_op->read) {
struct linux_binfmt * fmt;
read_lock(&binfmt_lock);
for (fmt = formats ; fmt ; fmt = fmt->next) {
if (!fmt->load_shlib)
continue;
if (!try_inc_mod_count(fmt->module))
continue;
read_unlock(&binfmt_lock);
error = fmt->load_shlib(file);
read_lock(&binfmt_lock);
put_binfmt(fmt);
if (error != -ENOEXEC)
break;
}
read_unlock(&binfmt_lock);
}
fput(file);
out:
return error;
exit:
path_release(&nd);
goto out;
}
/*
* count() counts the number of arguments/envelopes
*/
static int count(char ** argv, int max)
{
int i = 0;
if (argv != NULL) {
for (;;) {
char * p;
int error;
error = get_user(p,argv);
if (error)
return error;
if (!p)
break;
argv++;
if(++i > max)
return -E2BIG;
}
}
return i;
}
/*
* 'copy_strings()' copies argument/envelope 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.
*/
int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
{
while (argc-- > 0) {
char *str;
int len;
unsigned long pos;
if (get_user(str, argv+argc) || !str || !(len = strnlen_user(str, bprm->p)))
return -EFAULT;
if (bprm->p < len)
return -E2BIG;
bprm->p -= len;
/* XXX: add architecture specific overflow check here. */
pos = bprm->p;
while (len > 0) {
char *kaddr;
int i, new, err;
struct page *page;
int offset, bytes_to_copy;
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 (!page)
return -ENOMEM;
new = 1;
}
kaddr = kmap(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);
kunmap(page);
if (err)
return -EFAULT;
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
}
}
return 0;
}
/*
* Like copy_strings, but get argv and its values from kernel memory.
*/
int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm)
{
int r;
mm_segment_t oldfs = get_fs();
set_fs(KERNEL_DS);
r = copy_strings(argc, argv, bprm);
set_fs(oldfs);
return r;
}
/*
* This routine is used to map in a page into an address space: needed by
* execve() for the initial stack and environment pages.
*/
void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address)
{
pgd_t * pgd;
pmd_t * pmd;
pte_t * pte;
if (page_count(page) != 1)
printk("mem_map disagrees with %p at %08lx\n", page, address);
pgd = pgd_offset(tsk->mm, address);
pmd = pmd_alloc(pgd, address);
if (!pmd) {
__free_page(page);
force_sig(SIGKILL, tsk);
return;
}
pte = pte_alloc(pmd, address);
if (!pte) {
__free_page(page);
force_sig(SIGKILL, tsk);
return;
}
if (!pte_none(*pte)) {
pte_ERROR(*pte);
__free_page(page);
return;
}
flush_dcache_page(page);
flush_page_to_ram(page);
set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, PAGE_COPY))));
/* no need for flush_tlb */
}
int setup_arg_pages(struct linux_binprm *bprm)
{
unsigned long stack_base;
struct vm_area_struct *mpnt;
int i;
stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
bprm->p += stack_base;
if (bprm->loader)
bprm->loader += stack_base;
bprm->exec += stack_base;
mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
if (!mpnt)
return -ENOMEM;
down(¤t->mm->mmap_sem);
{
mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = STACK_TOP;
mpnt->vm_page_prot = PAGE_COPY;
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_ops = NULL;
mpnt->vm_pgoff = 0;
mpnt->vm_file = NULL;
mpnt->vm_private_data = (void *) 0;
insert_vm_struct(current->mm, mpnt);
current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
}
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page *page = bprm->page[i];
if (page) {
bprm->page[i] = NULL;
current->mm->rss++;
put_dirty_page(current,page,stack_base);
}
stack_base += PAGE_SIZE;
}
up(¤t->mm->mmap_sem);
return 0;
}
struct file *open_exec(const char *name)
{
struct nameidata nd;
struct inode *inode;
struct file *file;
int err = 0;
if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd))
err = path_walk(name, &nd);
file = ERR_PTR(err);
if (!err) {
inode = nd.dentry->d_inode;
file = ERR_PTR(-EACCES);
if (!IS_NOEXEC(inode) && S_ISREG(inode->i_mode)) {
int err = permission(inode, MAY_EXEC);
file = ERR_PTR(err);
if (!err) {
file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
if (!IS_ERR(file)) {
err = deny_write_access(file);
if (err) {
fput(file);
file = ERR_PTR(err);
}
}
out:
return file;
}
}
path_release(&nd);
}
goto out;
}
int kernel_read(struct file *file, unsigned long offset,
char * addr, unsigned long count)
{
mm_segment_t old_fs;
loff_t pos = offset;
int result = -ENOSYS;
if (!file->f_op->read)
goto fail;
old_fs = get_fs();
set_fs(get_ds());
result = file->f_op->read(file, addr, count, &pos);
set_fs(old_fs);
fail:
return result;
}
static int exec_mmap(void)
{
struct mm_struct * mm, * old_mm;
old_mm = current->mm;
if (old_mm && atomic_read(&old_mm->mm_users) == 1) {
flush_cache_mm(old_mm);
mm_release();
exit_mmap(old_mm);
flush_tlb_mm(old_mm);
return 0;
}
mm = mm_alloc();
if (mm) {
struct mm_struct *active_mm = current->active_mm;
if (init_new_context(current, mm)) {
mmdrop(mm);
return -ENOMEM;
}
/* Add it to the list of mm's */
spin_lock(&mmlist_lock);
list_add(&mm->mmlist, &init_mm.mmlist);
spin_unlock(&mmlist_lock);
task_lock(current);
current->mm = mm;
current->active_mm = mm;
task_unlock(current);
activate_mm(active_mm, mm);
mm_release();
if (old_mm) {
if (active_mm != old_mm) BUG();
mmput(old_mm);
return 0;
}
mmdrop(active_mm);
return 0;
}
return -ENOMEM;
}
/*
* This function makes sure the current process has its own signal table,
* so that flush_signal_handlers can later reset the handlers without
* disturbing other processes. (Other processes might share the signal
* table via the CLONE_SIGNAL option to clone().)
*/
static inline int make_private_signals(void)
{
struct signal_struct * newsig;
if (atomic_read(¤t->sig->count) <= 1)
return 0;
newsig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL);
if (newsig == NULL)
return -ENOMEM;
spin_lock_init(&newsig->siglock);
atomic_set(&newsig->count, 1);
memcpy(newsig->action, current->sig->action, sizeof(newsig->action));
spin_lock_irq(¤t->sigmask_lock);
current->sig = newsig;
spin_unlock_irq(¤t->sigmask_lock);
return 0;
}
/*
* If make_private_signals() made a copy of the signal table, decrement the
* refcount of the original table, and free it if necessary.
* We don't do that in make_private_signals() so that we can back off
* in flush_old_exec() if an error occurs after calling make_private_signals().
*/
static inline void release_old_signals(struct signal_struct * oldsig)
{
if (current->sig == oldsig)
return;
if (atomic_dec_and_test(&oldsig->count))
kmem_cache_free(sigact_cachep, oldsig);
}
/*
* These functions flushes out all traces of the currently running executable
* so that a new one can be started
*/
static inline void flush_old_files(struct files_struct * files)
{
long j = -1;
write_lock(&files->file_lock);
for (;;) {
unsigned long set, i;
j++;
i = j * __NFDBITS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -