Beispiel #1
0
int copy_to_user(void *uaddr, const void *kaddr, size_t nbytes)
{
        if (!range_perm(curproc, uaddr, nbytes, PROT_WRITE)) {
                return -EFAULT;
        }
        return vmmap_write(curproc->p_vmmap, uaddr, kaddr, nbytes);
}
Beispiel #2
0
/* Helper function for the ELF loader. Maps the specified segment
 * of the program header from the given file in to the given address
 * space with the given memory offset (in pages). On success returns 0, otherwise
 * returns a negative error code for the ELF loader to return.
 * Note that since any error returned by this function should
 * cause the ELF loader to give up, it is acceptable for the
 * address space to be modified after returning an error.
 * Note that memoff can be negative */
static int _elf32_map_segment(vmmap_t *map, vnode_t *file, int32_t memoff, const Elf32_Phdr *segment)
{
        uintptr_t addr;
        if (memoff < 0) {
                KASSERT(ADDR_TO_PN(segment->p_vaddr) > (uint32_t) -memoff);
                addr = (uintptr_t)segment->p_vaddr - (uintptr_t)PN_TO_ADDR(-memoff);
        } else {
                addr = (uintptr_t)segment->p_vaddr + (uintptr_t)PN_TO_ADDR(memoff);
        }
        uint32_t off = segment->p_offset;
        uint32_t memsz = segment->p_memsz;
        uint32_t filesz = segment->p_filesz;

        dbg(DBG_ELF, "Mapping program segment: type %#x, offset %#08x,"
            " vaddr %#08x, filesz %#x, memsz %#x, flags %#x, align %#x\n",
            segment->p_type, segment->p_offset, segment->p_vaddr,
            segment->p_filesz, segment->p_memsz, segment->p_flags,
            segment->p_align);

        /* check for bad data in the segment header */
        if (PAGE_SIZE != segment->p_align) {
                dbg(DBG_ELF, "ERROR: segment does not have correct alignment\n");
                return -ENOEXEC;
        } else if (filesz > memsz) {
                dbg(DBG_ELF, "ERROR: segment file size is greater than memory size\n");
                return -ENOEXEC;
        } else if (PAGE_OFFSET(addr) != PAGE_OFFSET(off)) {
                dbg(DBG_ELF, "ERROR: segment address and offset are not aligned correctly\n");
                return -ENOEXEC;
        }

        int perms = 0;
        if (PF_R & segment->p_flags) {
                perms |= PROT_READ;
        }
        if (PF_W & segment->p_flags) {
                perms |= PROT_WRITE;
        }
        if (PF_X & segment->p_flags) {
                perms |= PROT_EXEC;
        }

        if (0 < filesz) {
                /* something needs to be mapped from the file */
                /* start from the starting address and include enough pages to
                 * map all filesz bytes of the file */
                uint32_t lopage = ADDR_TO_PN(addr);
                uint32_t npages = ADDR_TO_PN(addr + filesz - 1) - lopage + 1;
                off_t fileoff = (off_t)PAGE_ALIGN_DOWN(off);

                int ret;
                if (!vmmap_is_range_empty(map, lopage, npages)) {
                        dbg(DBG_ELF, "ERROR: ELF file contains overlapping segments\n");
                        return -ENOEXEC;
                } else if (0 > (ret = vmmap_map(map, file, lopage, npages, perms,
                                                MAP_PRIVATE | MAP_FIXED, fileoff,
                                                0, NULL))) {
                        return ret;
                }
        }

        if (memsz > filesz) {
                /* there is left over memory in the segment which must
                 * be initialized to 0 (anonymously mapped) */
                uint32_t lopage = ADDR_TO_PN(addr + filesz);
                uint32_t npages = ADDR_TO_PN(PAGE_ALIGN_UP(addr + memsz)) - lopage;

                int ret;
                if (npages > 1 && !vmmap_is_range_empty(map, lopage + 1, npages - 1)) {
                        dbg(DBG_ELF, "ERROR: ELF file contains overlapping segments\n");
                        return -ENOEXEC;
                } else if (0 > (ret = vmmap_map(map, NULL, lopage, npages, perms,
                                                MAP_PRIVATE | MAP_FIXED, 0, 0, NULL))) {
                        return ret;
                } else if (!PAGE_ALIGNED(addr + filesz) && filesz > 0) {
                        /* In this case, we have accidentally zeroed too much of memory, as
                         * we zeroed all memory in the page containing addr + filesz.
                         * However, the remaining part of the data is not a full page, so we
                         * should not just map in another page (as there could be garbage
                         * after addr+filesz). For instance, consider the data-bss boundary
                         * (c.f. Intel x86 ELF supplement pp. 82).
                         * To fix this, we need to read in the contents of the file manually
                         * and put them at that user space addr in the anon map we just
                         * added. */
                        void *buf;
                        if (NULL == (buf = page_alloc()))
                                return -ENOMEM;
                        if (!(0 > (ret = file->vn_ops->read(file, (off_t) PAGE_ALIGN_DOWN(off + filesz),
                                                            buf, PAGE_OFFSET(addr + filesz))))) {
                                ret = vmmap_write(map, PAGE_ALIGN_DOWN(addr + filesz),
                                                  buf, PAGE_OFFSET(addr + filesz));
                        }
                        page_free(buf);
                        return ret;
                }
        }
        return 0;
}
Beispiel #3
0
/* Copies the arguments that must be on the stack prior to execution onto the
 * user stack. This should never fail.
 * arglow:   low address on the user stack where we should start the copying
 * argsize:  total size of everything to go on the stack
 * buf:      a kernel buffer at least as big as argsize (for convenience)
 * argv, envp, auxv: various vectors of stuff (to go on the stack)
 * argc, envc, auxc: number of non-NULL entries in argv, envp, auxv,
 *                   respectively (to avoid recomputing them)
 * phtsize: the size of the program header table (to avoid recomputing)
 * c.f. Intel i386 ELF supplement pp 54-59
 */
static void _elf32_load_args(vmmap_t *map, void *arglow, size_t argsize, char *buf,
                             char *const argv[], char *const envp[], Elf32_auxv_t *auxv,
                             int argc, int envc, int auxc, int phtsize)
{
        int i;

        /* Copy argc */
        *((int *) buf) = argc;

        /* Calculate where the strings / tables pointed to by the vectors start */
        size_t veclen = (argc + 1 + envc + 1) * sizeof(char *) + (auxc + 1) * sizeof(Elf32_auxv_t);

        char *vecstart = buf + sizeof(int) + 3 * sizeof(void *); /* Beginning of argv (in kernel buffer) */

        char *vvecstart = ((char *)arglow) + sizeof(int) + 3 * sizeof(void *); /* Beginning of argv (in user space) */

        char *strstart = vecstart + veclen; /* Beginning of first string pointed to by argv
                                                                                   (in kernel buffer) */

        /* Beginning of first string pointed to by argv (in user space) */
        char *vstrstart = vvecstart + veclen;

        /* Copy over pointer to argv */
        *(char **)(buf + 4) = vvecstart;
        /* Copy over pointer to envp */
        *(char **)(buf + 8) = vvecstart + (argc + 1) * sizeof(char *);
        /* Copy over pointer to auxv */
        *(char **)(buf + 12) = vvecstart + (argc + 1 + envc + 1) * sizeof(char *);

        /* Copy over argv along with every string in it */
        for (i = 0; i < argc; i++) {
                size_t len = strlen(argv[i]) + 1;
                strcpy(strstart, argv[i]);
                /* Remember that we need to use the virtual address of the string */
                *(char **) vecstart = vstrstart;
                strstart += len;
                vstrstart += len;
                vecstart += sizeof(char *);
        }
        /* null terminator of argv */
        *(char **) vecstart = NULL;
        vecstart += sizeof(char *);

        /* Copy over envp along with every string in it */
        for (i = 0; i < envc; i++) {
                size_t len = strlen(envp[i]) + 1;
                strcpy(strstart, envp[i]);
                /* Remember that we need to use the virtual address of the string */
                *(char **) vecstart = vstrstart;
                strstart += len;
                vstrstart += len;
                vecstart += sizeof(char *);
        }
        /* null terminator of envp */
        *(char **) vecstart = NULL;
        vecstart += sizeof(char *);

        /* Copy over auxv along with the program header (if we find it) */
        for (i = 0; i < auxc; i++) {
                /* Copy over the auxv entry */
                memcpy(vecstart, &auxv[i], sizeof(Elf32_auxv_t));
                /* Check if it points to the program header */
                if (auxv[i].a_type == AT_PHDR) {
                        /* Copy over the program header table */
                        memcpy(strstart, auxv[i].a_un.a_ptr, phtsize);
                        /* And modify the address */
                        ((Elf32_auxv_t *)vecstart)->a_un.a_ptr = vstrstart;
                }
                vecstart += sizeof(Elf32_auxv_t);
        }
        /* null terminator of auxv */
        ((Elf32_auxv_t *)vecstart)->a_type = NULL;

        /* Finally, we're done copying into the kernel buffer. Now just copy the
         * kernel buffer into user space */
        int ret = vmmap_write(map, arglow, buf, argsize);
        /* If this failed, we must have set up the address space wrong... */
        KASSERT(0 == ret);
}