📄 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>#include <linux/personality.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>#endifint core_uses_pid;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; if (get_user(p, argv)) return -EFAULT; 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) || !(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. * * tsk->mmap_sem is held for writing. */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(KERN_ERR "mem_map disagrees with %p at %08lx\n", page, address); pgd = pgd_offset(tsk->mm, address); spin_lock(&tsk->mm->page_table_lock); pmd = pmd_alloc(tsk->mm, pgd, address); if (!pmd) goto out; pte = pte_alloc(tsk->mm, pmd, address); if (!pte) goto out; if (!pte_none(*pte)) goto out; lru_cache_add(page); flush_dcache_page(page); flush_page_to_ram(page); set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, PAGE_COPY)))); tsk->mm->rss++; spin_unlock(&tsk->mm->page_table_lock); /* no need for flush_tlb */ memc_update_addr(tsk->mm, *pte, address); return;out: spin_unlock(&tsk->mm->page_table_lock); __free_page(page); force_sig(SIGKILL, tsk); return;}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_write(¤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; put_dirty_page(current,page,stack_base); } stack_base += PAGE_SIZE; } up_write(¤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 (!(nd.mnt->mnt_flags & MNT_NOEXEC) && S_ISREG(inode->i_mode)) { int err = permission(inode, MAY_EXEC); if (!err && !(inode->i_mode & 0111)) err = -EACCES; 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) { mm_release(); exit_mmap(old_mm); return 0; } mm = mm_alloc(); if (mm) { struct mm_struct *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); mmlist_nr++; spin_unlock(&mmlist_lock); task_lock(current); active_mm = current->active_mm; 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; if (i >= files->max_fds || i >= files->max_fdset)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -