Skip to content

Commit

Permalink
linux-user: remove MAX_ARG_PAGES limit
Browse files Browse the repository at this point in the history
Instead of creating a temporary copy for the whole environment and
the arguments, directly copy everything to the target stack.

For this to work, we have to change the order of stack creation and
copying the arguments.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Stefan Brüns <stefan.bruens@rwth-aachen.de>
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
  • Loading branch information
StefanBruens authored and Riku Voipio committed Sep 28, 2015
1 parent 84646ee commit 59baae9
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 76 deletions.
110 changes: 55 additions & 55 deletions linux-user/elfload.c
Original file line number Diff line number Diff line change
Expand Up @@ -1373,66 +1373,69 @@ static bool elf_check_ehdr(struct elfhdr *ehdr)
* to be put directly into the top of new user memory.
*
*/
static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
abi_ulong p)
static abi_ulong copy_elf_strings(int argc, char **argv, char *scratch,
abi_ulong p, abi_ulong stack_limit)
{
char *tmp, *tmp1, *pag = NULL;
int len, offset = 0;
char *tmp;
int len, offset;
abi_ulong top = p;

if (!p) {
return 0; /* bullet-proofing */
}

offset = ((p - 1) % TARGET_PAGE_SIZE) + 1;

while (argc-- > 0) {
tmp = argv[argc];
if (!tmp) {
fprintf(stderr, "VFS: argc is wrong");
exit(-1);
}
tmp1 = tmp;
while (*tmp++);
len = tmp - tmp1;
if (p < len) { /* this shouldn't happen - 128kB */
len = strlen(tmp) + 1;
tmp += len;

if (len > (p - stack_limit)) {
return 0;
}
while (len) {
--p; --tmp; --len;
if (--offset < 0) {
offset = p % TARGET_PAGE_SIZE;
pag = (char *)page[p/TARGET_PAGE_SIZE];
if (!pag) {
pag = g_try_malloc0(TARGET_PAGE_SIZE);
page[p/TARGET_PAGE_SIZE] = pag;
if (!pag)
return 0;
}
}
if (len == 0 || offset == 0) {
*(pag + offset) = *tmp;
}
else {
int bytes_to_copy = (len > offset) ? offset : len;
tmp -= bytes_to_copy;
p -= bytes_to_copy;
offset -= bytes_to_copy;
len -= bytes_to_copy;
memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
int bytes_to_copy = (len > offset) ? offset : len;
tmp -= bytes_to_copy;
p -= bytes_to_copy;
offset -= bytes_to_copy;
len -= bytes_to_copy;

memcpy_fromfs(scratch + offset, tmp, bytes_to_copy);

if (offset == 0) {
memcpy_to_target(p, scratch, top - p);
top = p;
offset = TARGET_PAGE_SIZE;
}
}
}
if (offset) {
memcpy_to_target(p, scratch + offset, top - p);
}

return p;
}

static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
/* Older linux kernels provide up to MAX_ARG_PAGES (default: 32) of
* argument/environment space. Newer kernels (>2.6.33) allow more,
* dependent on stack size, but guarantee at least 32 pages for
* backwards compatibility.
*/
#define STACK_LOWER_LIMIT (32 * TARGET_PAGE_SIZE)

static abi_ulong setup_arg_pages(struct linux_binprm *bprm,
struct image_info *info)
{
abi_ulong stack_base, size, error, guard;
int i;
abi_ulong size, error, guard;

/* Create enough stack to hold everything. If we don't use
it for args, we'll use it for something else. */
size = guest_stack_size;
if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) {
size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
if (size < STACK_LOWER_LIMIT) {
size = STACK_LOWER_LIMIT;
}
guard = TARGET_PAGE_SIZE;
if (guard < qemu_real_host_page_size) {
Expand All @@ -1450,18 +1453,8 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
target_mprotect(error, guard, PROT_NONE);

info->stack_limit = error + guard;
stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
p += stack_base;

for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
if (bprm->page[i]) {
/* FIXME - check return value of memcpy_to_target() for failure */
memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
g_free(bprm->page[i]);
}
stack_base += TARGET_PAGE_SIZE;
}
return p;

return info->stack_limit + size - sizeof(void *);
}

/* Map and zero the bss. We need to explicitly zero any fractional pages
Expand Down Expand Up @@ -2203,6 +2196,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
struct image_info interp_info;
struct elfhdr elf_ex;
char *elf_interpreter = NULL;
char *scratch;

info->start_mmap = (abi_ulong)ELF_START_MMAP;

Expand All @@ -2214,18 +2208,24 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
when we load the interpreter. */
elf_ex = *(struct elfhdr *)bprm->buf;

bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
bprm->p = setup_arg_pages(bprm, info);

scratch = g_new0(char, TARGET_PAGE_SIZE);
bprm->p = copy_elf_strings(1, &bprm->filename, scratch,
bprm->p, info->stack_limit);
bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch,
bprm->p, info->stack_limit);
bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch,
bprm->p, info->stack_limit);
g_free(scratch);

if (!bprm->p) {
fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG));
exit(-1);
}

/* Do this so that we can load the interpreter, if need be. We will
change some of these later */
bprm->p = setup_arg_pages(bprm->p, bprm, info);

if (elf_interpreter) {
load_elf_interp(elf_interpreter, &interp_info, bprm->buf);

Expand Down
2 changes: 1 addition & 1 deletion linux-user/flatload.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ static int load_flat_shared_library(int id, struct lib_info *libs)
int load_flt_binary(struct linux_binprm *bprm, struct image_info *info)
{
struct lib_info libinfo[MAX_SHARED_LIBS];
abi_ulong p = bprm->p;
abi_ulong p;
abi_ulong stack_len;
abi_ulong start_addr;
abi_ulong sp;
Expand Down
7 changes: 0 additions & 7 deletions linux-user/linuxload.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
struct linux_binprm *bprm)
{
int retval;
int i;

bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
memset(bprm->page, 0, sizeof(bprm->page));
bprm->fd = fdexec;
bprm->filename = (char *)filename;
bprm->argc = count(argv);
Expand Down Expand Up @@ -172,9 +169,5 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
return retval;
}

/* Something went wrong, return the inode and free the argument pages*/
for (i=0 ; i<MAX_ARG_PAGES ; i++) {
g_free(bprm->page[i]);
}
return(retval);
}
7 changes: 0 additions & 7 deletions linux-user/qemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,6 @@ extern const char *qemu_uname_release;
extern unsigned long mmap_min_addr;

/* ??? See if we can avoid exposing so much of the loader internals. */
/*
* MAX_ARG_PAGES defines the number of pages allocated for arguments
* and envelope for the new program. 32 should suffice, this gives
* a maximum env+arg of 128kB w/4KB pages!
*/
#define MAX_ARG_PAGES 33

/* Read a good amount of data initially, to hopefully get all the
program headers loaded. */
Expand All @@ -160,7 +154,6 @@ extern unsigned long mmap_min_addr;
*/
struct linux_binprm {
char buf[BPRM_BUF_SIZE] __attribute__((aligned));
void *page[MAX_ARG_PAGES];
abi_ulong p;
int fd;
int e_uid, e_gid;
Expand Down
6 changes: 0 additions & 6 deletions linux-user/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -5808,12 +5808,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
}
*q = NULL;

/* This case will not be caught by the host's execve() if its
page size is bigger than the target's. */
if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) {
ret = -TARGET_E2BIG;
goto execve_end;
}
if (!(p = lock_user_string(arg1)))
goto execve_efault;
ret = get_errno(execve(p, argp, envp));
Expand Down

0 comments on commit 59baae9

Please sign in to comment.