diff --git a/executer/include/uprotocol.h b/executer/include/uprotocol.h index c21353af..e0ef63e1 100644 --- a/executer/include/uprotocol.h +++ b/executer/include/uprotocol.h @@ -146,7 +146,6 @@ struct program_load_desc { int uti_use_last_cpu; /* Work-around not to share CPU with OpenMP thread */ int nr_processes; int process_rank; - char shell_path[SHELL_PATH_MAX_LEN]; __cpu_set_unit cpu_set[PLD_CPU_SET_SIZE]; int profile; struct program_image_section sections[0]; diff --git a/executer/user/mcexec.c b/executer/user/mcexec.c index 685a24d3..5412c783 100644 --- a/executer/user/mcexec.c +++ b/executer/user/mcexec.c @@ -300,7 +300,6 @@ struct program_load_desc *load_elf(FILE *fp, char **interp_pathp) + sizeof(struct program_image_section) * nhdrs); memset(desc, '\0', sizeof(struct program_load_desc) + sizeof(struct program_image_section) * nhdrs); - desc->shell_path[0] = '\0'; fseek(fp, hdr.e_phoff, SEEK_SET); j = 0; desc->num_sections = nhdrs; @@ -603,8 +602,10 @@ retry: /* Check whether the resolved path is a symlink */ if (lstat(path, &sb) == -1) { - __eprintf("lookup_exec_path(): error stat\n"); - return errno; + error = errno; + __dprintf("lookup_exec_path(): error stat for %s: %d\n", + path, error); + return error; } if ((sb.st_mode & S_IFMT) == S_IFLNK) { @@ -651,13 +652,13 @@ retry: } int load_elf_desc(char *filename, struct program_load_desc **desc_p, - char **shell_p) + char **shebang_p) { FILE *fp; FILE *interp = NULL; char *interp_path; - char *shell = NULL; - size_t shell_len = 0; + char *shebang = NULL; + size_t shebang_len = 0; struct program_load_desc *desc; int ret = 0; struct stat sb; @@ -692,16 +693,21 @@ int load_elf_desc(char *filename, struct program_load_desc **desc_p, } if (!strncmp(header, "#!", 2)) { - - if (getline(&shell, &shell_len, fp) == -1) { - fprintf(stderr, "Error: reading shell path %s\n", filename); + if (getline(&shebang, &shebang_len, fp) == -1) { + fprintf(stderr, "Error: reading shebang path %s\n", + filename); } fclose(fp); - /* Delete new line character */ - shell[strlen(shell) - 1] = 0; - *shell_p = shell; + /* Delete new line character and any trailing spaces */ + shebang_len = strlen(shebang) - 1; + shebang[shebang_len] = '\0'; + while (strpbrk(shebang + shebang_len - 1, " \t")) { + shebang_len--; + shebang[shebang_len] = '\0'; + } + *shebang_p = shebang; return 0; } @@ -787,6 +793,77 @@ int load_elf_desc(char *filename, struct program_load_desc **desc_p, return 0; } +/* recursively resolve shebangs + * + * Note: shebang_argv_p must point to reallocable memory or be NULL + */ +int load_elf_desc_shebang(char *shebang_argv0, + struct program_load_desc **desc_p, + char ***shebang_argv_p) +{ + char path[PATH_MAX]; + char *shebang = NULL; + int ret; + + if ((ret = lookup_exec_path(shebang_argv0, path, sizeof(path), 1)) + != 0) { + __dprintf("error: finding file: %s\n", shebang_argv0); + return ret; + } + + if ((ret = load_elf_desc(path, desc_p, &shebang)) != 0) { + __eprintf("error: loading file: %s\n", shebang_argv0); + return ret; + } + + if (shebang) { + char *shebang_params; + size_t shebang_param_count = 1; + size_t shebang_argv_count = 0; + char **shebang_argv; + + if (!shebang_argv_p) + return load_elf_desc_shebang(shebang, desc_p, NULL); + + shebang_argv = *shebang_argv_p; + + /* if there is a space, add whatever follows as extra arg */ + shebang_params = strchr(shebang, ' '); + if (shebang_params) { + shebang_params[0] = '\0'; + shebang_params++; + shebang_param_count++; + } + + if (shebang_argv == NULL) { + shebang_argv_count = shebang_param_count + 1; + shebang_argv = malloc(shebang_argv_count * + sizeof(void *)); + shebang_argv[shebang_param_count] = 0; + } else { + while (shebang_argv[shebang_argv_count++]) + ; + + shebang_argv_count += shebang_param_count + 1; + shebang_argv = realloc(shebang_argv, + shebang_argv_count * sizeof(void *)); + memmove(shebang_argv + shebang_param_count, + shebang_argv, + (shebang_argv_count - shebang_param_count) + * sizeof(void *)); + } + shebang_argv[0] = shebang; + if (shebang_params) + shebang_argv[1] = shebang_params; + + *shebang_argv_p = shebang_argv; + + return load_elf_desc_shebang(shebang, desc_p, shebang_argv_p); + } + + return 0; +} + int transfer_image(int fd, struct program_load_desc *desc) { struct remote_transfer pt; @@ -936,55 +1013,66 @@ unsigned long dma_buf_pa; void print_flat(char *flat) { - char **string; - - __dprintf("counter: %d\n", *((int *)flat)); + long i, count; + long *_flat = (long *)flat; - string = (char **)(flat + sizeof(int)); - while (*string) { - - __dprintf("%s\n", (flat + (unsigned long)(*string))); + count = _flat[0]; + __dprintf("counter: %ld\n", count); - ++string; + for (i = 0; i < count; i++) { + __dprintf("%s\n", (flat + _flat[i + 1])); } } /* * Flatten out a (char **) string array into the following format: - * [nr_strings][char *offset of string_0]...[char *offset of string_n-1][NULL][string0]...[stringn_1] + * [nr_strings][char *offset of string_0]...[char *offset of string_n-1][char *offset of end of string][string0]...[stringn_1] * if nr_strings == -1, we assume the last item is NULL * + * sizes all are longs. + * * NOTE: copy this string somewhere, add the address of the string to each offset * and we get back a valid argv or envp array. * + * pre_strings is already flattened, so we just need to manage counts and copy + * the string part appropriately. + * * returns the total length of the flat string and updates flat to * point to the beginning. */ -int flatten_strings(int nr_strings, char *first, char **strings, char **flat) +int flatten_strings(char *pre_strings, char **strings, char **flat) { - int full_len, string_i; - unsigned long flat_offset; - char *_flat; + int full_len, i; + int nr_strings; + int pre_strings_count = 0; + int pre_strings_len = 0; + long *_flat; + long *pre_strings_flat; + char *p; - /* How many strings do we have? */ - if (nr_strings == -1) { - for (nr_strings = 0; strings[nr_strings]; ++nr_strings); - } + for (nr_strings = 0; strings[nr_strings]; ++nr_strings) + ; /* Count full length */ full_len = sizeof(long) + sizeof(char *); // Counter and terminating NULL - if (first) { - full_len += sizeof(char *) + strlen(first) + 1; + if (pre_strings) { + pre_strings_flat = (long *)pre_strings; + pre_strings_count = pre_strings_flat[0]; + + pre_strings_len = pre_strings_flat[pre_strings_count + 1]; + pre_strings_len -= sizeof(long) * (pre_strings_count + 2); + + full_len += pre_strings_count * sizeof(long) + pre_strings_len; } - for (string_i = 0; string_i < nr_strings; ++string_i) { + for (i = 0; strings[i]; ++i) { // Pointer + actual value - full_len += sizeof(char *) + strlen(strings[string_i]) + 1; + full_len += sizeof(char *) + strlen(strings[i]) + 1; } full_len = (full_len + sizeof(long) - 1) & ~(sizeof(long) - 1); - _flat = (char *)malloc(full_len); + _flat = malloc(full_len); if (!_flat) { return 0; } @@ -992,28 +1080,32 @@ int flatten_strings(int nr_strings, char *first, char **strings, char **flat) memset(_flat, 0, full_len); /* Number of strings */ - *((long *)_flat) = nr_strings + (first ? 1 : 0); + _flat[0] = nr_strings + pre_strings_count; // Actual offset - flat_offset = sizeof(long) + sizeof(char *) * (nr_strings + 1 + - (first ? 1 : 0)); + p = (char *)(_flat + nr_strings + pre_strings_count + 2); - if (first) { - *((char **)(_flat + sizeof(long))) = (void *)flat_offset; - memcpy(_flat + flat_offset, first, strlen(first) + 1); - flat_offset += strlen(first) + 1; + if (pre_strings) { + for (i = 0; i < pre_strings_count; i++) { + _flat[i + 1] = pre_strings_flat[i + 1] + + nr_strings * sizeof(long); + } + memcpy(p, pre_strings + pre_strings_flat[1], + pre_strings_len); + p += pre_strings_len; } - for (string_i = 0; string_i < nr_strings; ++string_i) { - - /* Fabricate the string */ - *((char **)(_flat + sizeof(long) + (string_i + (first ? 1 : 0)) - * sizeof(char *))) = (void *)flat_offset; - memcpy(_flat + flat_offset, strings[string_i], strlen(strings[string_i]) + 1); - flat_offset += strlen(strings[string_i]) + 1; - } + for (i = 0; i < nr_strings; ++i) { + int len = strlen(strings[i]) + 1; - *flat = _flat; + _flat[i + pre_strings_count + 1] = p - (char *)_flat; + + memcpy(p, strings[i], len); + p += len; + } + _flat[nr_strings + pre_strings_count + 1] = p - (char *)_flat; + + *flat = (char *)_flat; return full_len; } @@ -1325,7 +1417,7 @@ static int reduce_stack(struct rlimit *orig_rlim, char *argv[]) { int n; char newval[40]; - char path[1024]; + char path[PATH_MAX]; int error; struct rlimit new_rlim; @@ -1477,8 +1569,7 @@ static int find_mount_prefix(char *prefix) } } - if (line) - free(line); + free(line); return ret; } @@ -2013,7 +2104,6 @@ int main(int argc, char **argv) struct program_load_desc *desc; int envs_len; char *envs; - char *args; char *p; int i; int error; @@ -2021,9 +2111,8 @@ int main(int argc, char **argv) unsigned long lmax; int target_core = 0; int opt; - char path[1024]; - char *shell = NULL; - char shell_path[1024]; + char **shebang_argv = NULL; + char *shebang_argv_flat = NULL; int num = 0; int persona; #ifdef ADD_ENVS_OPTION @@ -2056,6 +2145,8 @@ int main(int argc, char **argv) /* Disable address space layout randomization */ __dprintf("persona=%08x\n", persona); if ((persona & (PER_LINUX | ADDR_NO_RANDOMIZE)) == 0) { + char path[PATH_MAX]; + CHKANDJUMP(getenv("MCEXEC_ADDR_NO_RANDOMIZE"), 1, "personality() and then execv() failed\n"); persona = personality(persona | PER_LINUX | ADDR_NO_RANDOMIZE); @@ -2209,7 +2300,7 @@ int main(int argc, char **argv) #ifdef ADD_ENVS_OPTION #else /* ADD_ENVS_OPTION */ /* Collect environment variables */ - envs_len = flatten_strings(-1, NULL, environ, &envs); + envs_len = flatten_strings(NULL, environ, &envs); #endif /* ADD_ENVS_OPTION */ #ifdef ENABLE_MCOVERLAYFS @@ -2329,32 +2420,8 @@ int main(int argc, char **argv) __dprintf("mcoverlay disable\n"); #endif // ENABLE_MCOVERLAYFS - if (lookup_exec_path(argv[optind], path, sizeof(path), 1) != 0) { - fprintf(stderr, "error: finding file: %s\n", argv[optind]); + if (load_elf_desc_shebang(argv[optind], &desc, &shebang_argv)) return 1; - } - - if (load_elf_desc(path, &desc, &shell) != 0) { - fprintf(stderr, "error: loading file: %s\n", argv[optind]); - return 1; - } - - /* Check whether shell script */ - if (shell) { - if (lookup_exec_path(shell, shell_path, sizeof(shell_path), 0) != 0) { - fprintf(stderr, "error: finding file: %s\n", shell); - return 1; - } - - if (load_elf_desc(shell_path, &desc, &shell) != 0) { - fprintf(stderr, "error: loading file: %s\n", shell); - return 1; - } - } - - if (shell) { - argv[optind] = path; - } #ifdef ADD_ENVS_OPTION /* Collect environment variables */ @@ -2362,7 +2429,7 @@ int main(int argc, char **argv) add_env_list(&extra_env, environ[i]); } local_env = create_local_environ(extra_env); - envs_len = flatten_strings(-1, NULL, local_env, &envs); + envs_len = flatten_strings(NULL, local_env, &envs); destroy_local_environ(local_env); local_env = NULL; destroy_env_list(extra_env); @@ -2375,9 +2442,14 @@ int main(int argc, char **argv) desc->envs = envs; //print_flat(envs); - desc->args_len = flatten_strings(-1, shell, argv + optind, &args); - desc->args = args; - //print_flat(args); + if (shebang_argv) + flatten_strings(NULL, shebang_argv, &shebang_argv_flat); + + desc->args_len = flatten_strings(shebang_argv_flat, argv + optind, + &desc->args); + //print_flat(desc->args); + free(shebang_argv); + free(shebang_argv_flat); desc->cpu = target_core; desc->enable_vdso = enable_vdso; @@ -3641,85 +3713,80 @@ fork_err: switch (w.sr.args[0]) { struct program_load_desc *desc; struct remote_transfer trans; - char path[1024]; char *filename; + char **shebang_argv; + char *shebang_argv_flat; + char *buffer; + size_t size; int ret; - char *shell; - char shell_path[1024]; /* Load descriptor phase */ case 1: - - shell = NULL; + shebang_argv = NULL; + buffer = NULL; filename = (char *)w.sr.args[1]; - if ((ret = lookup_exec_path(filename, path, sizeof(path), 0)) - != 0) { + if ((ret = load_elf_desc_shebang(filename, &desc, + &shebang_argv)) != 0) { goto return_execve1; } - if ((ret = load_elf_desc(path, &desc, &shell)) != 0) { - fprintf(stderr, - "execve(): error loading ELF for file %s\n", path); - goto return_execve1; - } - - /* Check whether shell script */ - if (shell) { - if ((ret = lookup_exec_path(shell, shell_path, - sizeof(shell_path), 0)) != 0) { - fprintf(stderr, "execve(): error: finding file: %s\n", shell); - goto return_execve1; - } - - if ((ret = load_elf_desc(shell_path, &desc, &shell)) - != 0) { - fprintf(stderr, "execve(): error: loading file: %s\n", shell); - goto return_execve1; - } - - if (strlen(shell) >= SHELL_PATH_MAX_LEN) { - fprintf(stderr, "execve(): error: shell path too long: %s\n", shell_path); - ret = ENAMETOOLONG; - goto return_execve1; - } - - /* Let the LWK know the shell interpreter */ - strcpy(desc->shell_path, shell); - } - desc->enable_vdso = enable_vdso; __dprintf("execve(): load_elf_desc() for %s OK, num sections: %d\n", - path, desc->num_sections); + filename, desc->num_sections); desc->rlimit[MCK_RLIMIT_STACK].rlim_cur = rlim_stack.rlim_cur; desc->rlimit[MCK_RLIMIT_STACK].rlim_max = rlim_stack.rlim_max; desc->stack_premap = stack_premap; + buffer = (char *)desc; + size = sizeof(struct program_load_desc) + + sizeof(struct program_image_section) * + desc->num_sections; + if (shebang_argv) { + desc->args_len = flatten_strings(NULL, shebang_argv, + &shebang_argv_flat); + buffer = malloc(size + desc->args_len); + if (!buffer) { + fprintf(stderr, + "execve(): could not alloc transfer buffer for file %s\n", + filename); + free(shebang_argv_flat); + ret = ENOMEM; + goto return_execve1; + } + memcpy(buffer, desc, size); + memcpy(buffer + size, shebang_argv_flat, + desc->args_len); + free(shebang_argv_flat); + size += desc->args_len; + } + /* Copy descriptor to co-kernel side */ - trans.userp = (void*)desc; + trans.userp = buffer; trans.rphys = w.sr.args[2]; - trans.size = sizeof(struct program_load_desc) + - sizeof(struct program_image_section) * - desc->num_sections; + trans.size = size; trans.direction = MCEXEC_UP_TRANSFER_TO_REMOTE; if (ioctl(fd, MCEXEC_UP_TRANSFER, &trans) != 0) { fprintf(stderr, "execve(): error transfering ELF for file %s\n", - (char *)w.sr.args[1]); + filename); + ret = -errno; goto return_execve1; } __dprintf("execve(): load_elf_desc() for %s OK\n", - path); + filename); - /* We can't be sure next phase will succeed */ - /* TODO: what shall we do with fp in desc?? */ - free(desc); - ret = 0; return_execve1: + /* We can't be sure next phase will succeed */ + /* TODO: what shall we do with fp in desc?? */ + if (buffer != (char *)desc) + free(buffer); + free(desc); + do_syscall_return(fd, cpu, ret, 0, 0, 0, 0); break; diff --git a/kernel/host.c b/kernel/host.c index 3ec76f96..92247b01 100644 --- a/kernel/host.c +++ b/kernel/host.c @@ -78,7 +78,6 @@ int prepare_process_ranges_args_envs(struct thread *thread, unsigned long args_envs_p, args_envs_rp; unsigned long s, e, up; char **argv; - char **a; int i, n, argc, envc, args_envs_npages; char **env; int range_npages; @@ -370,21 +369,18 @@ int prepare_process_ranges_args_envs(struct thread *thread, __FUNCTION__, proc->saved_cmdline); - for (a = argv; *a; a++) { - *a = (char *)addr + (unsigned long)*a; // Process' address space! + for (i = 0; i < argc; i++) { + // Process' address space! + argv[i] = (char *)addr + (unsigned long)argv[i]; } envc = *((long *)(args_envs + p->args_len)); dkprintf("envc: %d\n", envc); env = (char **)(args_envs + p->args_len + sizeof(long)); - while (*env) { - char **_env = env; - //dkprintf("%s\n", args_envs + p->args_len + (unsigned long)*env); - *env = (char *)addr + p->args_len + (unsigned long)*env; - env = ++_env; + for (i = 0; i < envc; i++) { + env[i] = addr + p->args_len + env[i]; } - env = (char **)(args_envs + p->args_len + sizeof(long)); dkprintf("env OK\n"); diff --git a/kernel/include/syscall.h b/kernel/include/syscall.h index 2a3323ac..158599e2 100644 --- a/kernel/include/syscall.h +++ b/kernel/include/syscall.h @@ -204,7 +204,6 @@ struct program_load_desc { int uti_use_last_cpu; /* Work-around not to share CPU with OpenMP thread */ int nr_processes; int process_rank; - char shell_path[SHELL_PATH_MAX_LEN]; __cpu_set_unit cpu_set[PLD_CPU_SET_SIZE]; int profile; struct program_image_section sections[0]; diff --git a/kernel/syscall.c b/kernel/syscall.c index 45f26b97..f1af54f6 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -2496,13 +2496,16 @@ SYSCALL_DECLARE(execve) dkprintf("execve(): ELF desc received, num sections: %d\n", desc->num_sections); - if (desc->shell_path[0]) { - dkprintf("execve(): shell interpreter: %s\n", desc->shell_path); + /* for shebang script we get extra argvs from mcexec */ + if (desc->args_len) { + desc->args = ((char *)desc) + sizeof(struct program_load_desc) + + sizeof(struct program_image_section) * + desc->num_sections; } /* Flatten argv and envp into kernel-space buffers */ - argv_flat_len = flatten_strings_from_user(-1, (desc->shell_path[0] ? - desc->shell_path : NULL), argv, &argv_flat); + argv_flat_len = flatten_strings_from_user(desc->args, argv, + &argv_flat); if (argv_flat_len < 0) { char *kfilename; int len = strlen_user(filename); @@ -2516,8 +2519,10 @@ SYSCALL_DECLARE(execve) ret = argv_flat_len; goto end; } + desc->args = NULL; + desc->args_len = 0; - envp_flat_len = flatten_strings_from_user(-1, NULL, envp, &envp_flat); + envp_flat_len = flatten_strings_from_user(NULL, envp, &envp_flat); if (envp_flat_len < 0) { char *kfilename; int len = strlen_user(filename); diff --git a/lib/include/string.h b/lib/include/string.h index ae537ef5..455561d8 100644 --- a/lib/include/string.h +++ b/lib/include/string.h @@ -47,7 +47,6 @@ extern int sscanf(const char * buf, const char * fmt, ...); extern int scnprintf(char * buf, size_t size, const char *fmt, ...); unsigned long strtol(const char *cp, char **endp, unsigned int base); -int flatten_strings(int nr_strings, char *first, char **strings, char **flat); -int flatten_strings_from_user(int nr_strings, char *first, char **strings, char **flat); +int flatten_strings_from_user(char *pre_strings, char **strings, char **flat); #endif diff --git a/lib/string.c b/lib/string.c index 6089121e..83e19330 100644 --- a/lib/string.c +++ b/lib/string.c @@ -219,79 +219,30 @@ int memcmp(const void *s1, const void *s2, size_t n) /* * Flatten out a (char **) string array into the following format: - * [nr_strings][char *offset of string_0]...[char *offset of string_n-1][NULL][string0]...[stringn_1] - * if nr_strings == -1, we assume the last item is NULL + * [nr_strings][char *offset of string_0]...[char *offset of string_n-1][char *offset of end of string][string0]...[stringn_1] + * + * sizes all are longs. * * NOTE: copy this string somewhere, add the address of the string to each offset * and we get back a valid argv or envp array. * + * pre_strings is already flattened, so we just need to manage counts and copy + * the string parts appropriately. + * * returns the total length of the flat string and updates flat to * point to the beginning. */ -int flatten_strings(int nr_strings, char *first, char **strings, char **flat) +int flatten_strings_from_user(char *pre_strings, char **strings, char **flat) { - int full_len, string_i; - unsigned long flat_offset; - char *_flat; - - /* How many strings do we have? */ - if (nr_strings == -1) { - for (nr_strings = 0; strings[nr_strings]; ++nr_strings); - } - - /* Count full length */ - full_len = sizeof(long) + sizeof(char *); // Counter and terminating NULL - if (first) { - full_len += sizeof(char *) + strlen(first) + 1; - } - - for (string_i = 0; string_i < nr_strings; ++string_i) { - // Pointer + actual value - full_len += sizeof(char *) + strlen(strings[string_i]) + 1; - } - - full_len = (full_len + sizeof(long) - 1) & ~(sizeof(long) - 1); - - _flat = (char *)kmalloc(full_len, IHK_MC_AP_NOWAIT); - if (!_flat) { - return 0; - } - - memset(_flat, 0, full_len); - - /* Number of strings */ - *((long *)_flat) = nr_strings + (first ? 1 : 0); - - // Actual offset - flat_offset = sizeof(long) + sizeof(char *) * (nr_strings + 1 + - (first ? 1 : 0)); - - if (first) { - *((char **)(_flat + sizeof(long))) = (void *)flat_offset; - memcpy(_flat + flat_offset, first, strlen(first) + 1); - flat_offset += strlen(first) + 1; - } - - for (string_i = 0; string_i < nr_strings; ++string_i) { - - /* Fabricate the string */ - *((char **)(_flat + sizeof(long) + (string_i + (first ? 1 : 0)) - * sizeof(char *))) = (void *)flat_offset; - memcpy(_flat + flat_offset, strings[string_i], strlen(strings[string_i]) + 1); - flat_offset += strlen(strings[string_i]) + 1; - } - - *flat = _flat; - return full_len; -} - -int flatten_strings_from_user(int nr_strings, char *first, char **strings, char **flat) -{ - int full_len, string_i; + int full_len, i; + int nr_strings = 0; + int pre_strings_count = 0; + int pre_strings_len = 0; long *_flat; + long *pre_strings_flat; char *p; long r; - int n, ret; + int ret; /* When strings is NULL, make array one NULL */ if (!strings) { @@ -306,35 +257,34 @@ int flatten_strings_from_user(int nr_strings, char *first, char **strings, char } /* How many strings do we have? */ - if (nr_strings == -1) { - nr_strings = 0; - for (;;) { - ret = getlong_user(&r, (void *)(strings + nr_strings)); - if (ret < 0) - return ret; + for (;;) { + ret = getlong_user(&r, (void *)(strings + nr_strings)); + if (ret < 0) + return ret; - if (r == 0) - break; + if (r == 0) + break; - ++nr_strings; - } + ++nr_strings; } /* Count full length */ full_len = sizeof(long) + sizeof(char *); // Counter and terminating NULL - if (first) { - int len = strlen(first); + if (pre_strings) { + pre_strings_flat = (long *)pre_strings; + pre_strings_count = pre_strings_flat[0]; - if(len < 0) - return len; - full_len += sizeof(char *) + len + 1; + pre_strings_len = pre_strings_flat[pre_strings_count + 1]; + pre_strings_len -= sizeof(long) * (pre_strings_count + 2); + + full_len += pre_strings_count * sizeof(long) + pre_strings_len; } - for (string_i = 0; string_i < nr_strings; ++string_i) { + for (i = 0; i < nr_strings; ++i) { char *userp; int len; - ret = getlong_user((long *)&userp, (void *)(strings + string_i)); + ret = getlong_user((long *)&userp, (void *)(strings + i)); if (ret < 0) return ret; @@ -354,31 +304,33 @@ int flatten_strings_from_user(int nr_strings, char *first, char **strings, char } /* Number of strings */ - n = first? 1: 0; - _flat[0] = nr_strings + n; - - // Actual offset - p = (char *)(_flat + nr_strings + 2 + n); + _flat[0] = nr_strings + pre_strings_count; - n = 1; - if (first) { - _flat[n++] = p - (char *)_flat; - strcpy(p, first); - p = strchr(p, '\0') + 1; + // Actual offset + p = (char *)(_flat + nr_strings + pre_strings_count + 2); + + if (pre_strings) { + for (i = 0; i < pre_strings_count; i++) { + _flat[i + 1] = pre_strings_flat[i + 1] + + nr_strings * sizeof(long); + } + memcpy(p, pre_strings + pre_strings_flat[1], + pre_strings_len); + p += pre_strings_len; } - for (string_i = 0; string_i < nr_strings; ++string_i) { + for (i = 0; i < nr_strings; ++i) { char *userp; - _flat[n++] = p - (char *)_flat; + _flat[i + pre_strings_count + 1] = p - (char *)_flat; - ret = getlong_user((long *)&userp, (void *)(strings + string_i)); + ret = getlong_user((long *)&userp, (void *)(strings + i)); if (ret < 0) return ret; strcpy_from_user(p, userp); p = strchr(p, '\0') + 1; } - _flat[n] = 0; + _flat[nr_strings + pre_strings_count + 1] = p - (char *)_flat; *flat = (char *)_flat; return full_len;