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); }
/* 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; }
/* 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); }