4
0
mirror of https://github.com/RT-Thread/rt-thread.git synced 2025-01-19 23:13:30 +08:00
Shell e5cf86267b
[smart] fixup of script execution (#9009)
This patch fixup the script execution capabilities on argv passing
and adds support for arguments larger than 4k.

According to POSIX, the script parameter is quiet different
from the current implementation. Especially on the way it inserts
the path of executables. At the end, when you execute a script
from `$PATH`, it always fails.

For the script, interpreter will be invoked with the following
arguments: `{interpreter [optional-arg] pathname arg...}`
where pathname is the pathname of the file specified as the first
argument of execve(), and arg...  is the series of words pointed
to by the argv argument of execve(), starting at argv[1].  Note that
there is no way to get the argv[0] that was passed to the
execve() call.

The changes include:

- Separating argument, environment variable, and auxiliary vector
  processing into a new lwp_args.c file.
- Fixing bugs in script argument processing and supporting arguments
  larger than 4k.
- Updating lwp_execve to use the new argscopy function and removing
  the old lwp_argscopy function.
- Making various modifications to lwp_load and elf_aux_fill to work
  with the new argument processing.
- Removing unnecessary code related to dynamic loading and interpreter
  scripts.

Signed-off-by: Shell <smokewood@qq.com>
2024-05-31 17:32:19 +08:00

782 lines
20 KiB
C

/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-12 Shell separate argv, envp, aux processing to lwp_args.c
* Bugs fix for script arguments processing.
* support args larger than 4k
*/
#include "lwp_args.h"
#include "lwp_internal.h"
#include "mm_page.h"
static void _strvec_init(struct lwp_string_vector *sv)
{
#define DEFAUTL_ARGV_BUFLEN 4
sv->strvec = rt_malloc(DEFAUTL_ARGV_BUFLEN * sizeof(char *));
sv->strvec_buflen = DEFAUTL_ARGV_BUFLEN;
sv->string_count = 0;
}
static void _strvec_detach(struct lwp_string_vector *sv)
{
if (sv->strvec)
{
rt_free(sv->strvec);
}
}
static rt_err_t _strvec_append(struct lwp_string_vector *sv, const char *string)
{
if (sv->string_count == sv->strvec_buflen)
{
void *newptr;
newptr = rt_realloc(sv->strvec, sv->strvec_buflen * 2 * sizeof(char *));
if (!newptr)
return -RT_ENOMEM;
sv->strvec = newptr;
sv->strvec_buflen *= 2;
}
sv->strvec[sv->string_count++] = string;
return RT_EOK;
}
static rt_err_t args_append(struct lwp_args_info *ai, const char *str_addr,
size_t str_len, enum lwp_args_type atype)
{
rt_err_t error;
char *str_bufaddr;
if (ai->strings_length + str_len + 1 > ai->str_buf_size)
{
/* reallocate buffer for this */
void *newptr;
newptr = rt_realloc(ai->str_buf, ai->str_buf_size * 2);
if (!newptr)
return -RT_ENOMEM;
ai->str_buf = newptr;
ai->str_buf_size *= 2;
}
/* append new string to string buffer and update strings_length */
str_bufaddr = &ai->str_buf[ai->strings_length];
if (atype == LWP_ARGS_TYPE_KARG || atype == LWP_ARGS_TYPE_KENVP)
{
strcpy(str_bufaddr, str_addr);
ai->strings_length += str_len + 1;
}
else
{
lwp_get_from_user(str_bufaddr, (void *)str_addr, str_len);
ai->strings_length += str_len;
ai->str_buf[ai->strings_length++] = '\0';
}
/* append new argument or environment */
switch (atype)
{
case LWP_ARGS_TYPE_ARG:
case LWP_ARGS_TYPE_KARG:
error = _strvec_append(&ai->argv, str_bufaddr);
if (!error && ai->argv.string_count == 1)
{
ai->argv0_strlen = str_len;
}
break;
case LWP_ARGS_TYPE_ENVP:
case LWP_ARGS_TYPE_KENVP:
error = _strvec_append(&ai->envp, str_bufaddr);
break;
default:
break;
}
return error;
}
/**
* @brief Override arguments 0 for script interpreter.
*
* Manual: interpreter will be invoked with the following arguments:
* {interpreter [optional-arg] pathname arg...}
* where pathname is the pathname of the file specified as the first
* argument of execve(), and arg... is the series of words pointed
* to by the argv argument of execve(), starting at argv[1]. Note
* that there is no way to get the argv[0] that was passed to the
* execve() call.
*/
static rt_err_t _args_override_argv0(struct lwp_args_info *ai, struct lwp_args_info *ow_ai)
{
rt_err_t error = 0;
int i, new_argc, new_strbuf_size, ai_bytes_tobe_copied;
char **new_argv, *new_strbuf, *base;
rt_base_t off;
if (ow_ai == 0 || ow_ai->argv.string_count == 0)
{
return -RT_EINVAL;
}
/* for new argument vector */
new_argc = ai->argv.string_count - 1 + ow_ai->argv.string_count;
new_argv = rt_malloc(new_argc * sizeof(char *));
if (!new_argv)
{
return -RT_ENOMEM;
}
/* for new string buffer */
ai_bytes_tobe_copied = ai->strings_length - (ai->argv0_strlen + 1);
new_strbuf_size = ai_bytes_tobe_copied + ow_ai->strings_length;
new_strbuf = rt_malloc(new_strbuf_size);
if (!new_argv)
{
rt_free(new_argv);
return -RT_ENOMEM;
}
base = new_strbuf;
off = base - ow_ai->str_buf;
/* copy overriding argument strings and argv */
memcpy(base, ow_ai->str_buf, ow_ai->strings_length);
for (i = 0; i < ow_ai->argv.string_count; i++)
{
/* base + ow_ai->argv.strvec[i] - ow_ai->str_buf */
new_argv[i] = (char *)ow_ai->argv.strvec[i] + off;
}
base += ow_ai->strings_length;
off = base - (ai->str_buf + ai->argv0_strlen + 1);
/* copy old argument strings starting from argv[1] and setup new_argv */
memcpy(base, ai->str_buf + ai->argv0_strlen + 1, ai_bytes_tobe_copied);
for (size_t j = 1; j < ai->argv.string_count; i++, j++)
{
/* base + ai->argv->strvec[j] - ai->str_buf */
new_argv[i] = (char *)ai->argv.strvec[j] + off;
}
/* setup envp for ai */
for (i = 0; i < ai->envp.string_count; i++)
{
/* base + ai->envp->strvec[i] - ai->str_buf */
ai->envp.strvec[i] += off;
}
/* replace strings buffer and argv buffer */
ai->str_buf = new_strbuf;
ai->strings_length = new_strbuf_size;
ai->str_buf_size = new_strbuf_size;
ai->argv.string_count = new_argc;
ai->argv.strvec = (void *)new_argv;
ai->argv.strvec_buflen = new_argc;
ai->argv0_strlen = ow_ai->argv0_strlen;
return error;
}
const char *lwp_args_get_argv_0(struct lwp_args_info *ai)
{
return ai->str_buf;
}
static rt_err_t args_init(struct lwp_args_info *ai, size_t str_buf_size)
{
void *str_buf;
str_buf = rt_malloc(str_buf_size);
if (!str_buf)
return -RT_ENOMEM;
memset(ai, 0, sizeof(*ai));
_strvec_init(&ai->argv);
if (!ai->argv.strvec)
{
rt_free(str_buf);
return -RT_ENOMEM;
}
_strvec_init(&ai->envp);
if (!ai->envp.strvec)
{
rt_free(str_buf);
_strvec_detach(&ai->argv);
return -RT_ENOMEM;
}
ai->str_buf_size = str_buf_size;
ai->str_buf = str_buf;
return RT_EOK;
}
#define STR_BUF_DEFAULT_SIZE 2048
rt_err_t lwp_args_init(struct lwp_args_info *ai)
{
return args_init(ai, STR_BUF_DEFAULT_SIZE);
}
void lwp_args_detach(struct lwp_args_info *ai)
{
_strvec_detach(&ai->argv);
_strvec_detach(&ai->envp);
rt_free(ai->str_buf);
}
#ifdef ARCH_MM_MMU
struct process_aux *lwp_argscopy(struct rt_lwp *lwp, struct lwp_args_info *ai)
{
int size = sizeof(rt_base_t) * 4; /* store argc, argv_NULL, envp_NULL, aux_NULL */
char *str_ua;
const char **args_ua;
const char **iter;
rt_base_t off;
struct process_aux_item pa_item;
struct process_aux *aux_ua;
size_t prot = PROT_READ | PROT_WRITE;
size_t flags = MAP_FIXED | MAP_PRIVATE;
rt_base_t argc = ai->argv.string_count;
rt_base_t envc = ai->envp.string_count;
/**
* counts the bytes to storage the args
*/
size += argc * sizeof(char *) + envc * sizeof(char *)
+ ai->strings_length + sizeof(struct process_aux);
args_ua = lwp_mmap2(lwp, (void *)(USER_STACK_VEND), size, prot, flags, -1, 0);
if (args_ua == RT_NULL)
{
return RT_NULL;
}
/**
* @brief Put data from args info to user space
* argc, argv[], NULL, envp[], NULL, aux[], NULL, strings
*/
iter = args_ua;
/* argc */
lwp_data_put(lwp, iter++, &argc, sizeof(char *));
/* strings */
str_ua = (char *)((rt_ubase_t)args_ua +
(1 + argc + 1 + envc + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(rt_base_t));
lwp_data_put(lwp, str_ua, ai->str_buf, ai->strings_length);
/* argv */
off = str_ua - ai->str_buf;
for (size_t i = 0; i < argc; i++)
{
/* str_ua + ai->argv.strvec[i] - ai->str_buf */
ai->argv.strvec[i] += off;
}
lwp_data_put(lwp, iter, ai->argv.strvec, sizeof(char *) * ai->argv.string_count);
iter += ai->argv.string_count;
/* NULL */
lwp_data_set(lwp, iter++, 0, sizeof(char *));
/* envp */
for (size_t i = 0; i < envc; i++)
{
/* str_ua + ai->envp.strvec[i] - ai->str_buf */
ai->envp.strvec[i] += off;
}
lwp_data_put(lwp, iter, ai->envp.strvec, sizeof(char *) * ai->envp.string_count);
iter += ai->envp.string_count;
/* NULL */
lwp_data_set(lwp, iter++, 0, sizeof(char *));
/* aux */
aux_ua = (struct process_aux *)iter;
pa_item.key = AT_EXECFN;
pa_item.value = (size_t)str_ua;
lwp_data_put(lwp, iter, &pa_item, sizeof(pa_item));
iter += AUX_ARRAY_ITEMS_NR * 2;
/* NULL */
lwp_data_set(lwp, iter++, 0, sizeof(char *));
lwp->args = args_ua;
return aux_ua;
}
#else
static struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp)
{
#ifdef ARCH_MM_MMU
int size = sizeof(int) * 5; /* store argc, argv, envp, aux, NULL */
struct process_aux *aux;
#else
int size = sizeof(int) * 4; /* store argc, argv, envp, NULL */
#endif /* ARCH_MM_MMU */
int *args;
char *str;
char **new_argve;
int i;
int len;
for (i = 0; i < argc; i++)
{
size += (rt_strlen(argv[i]) + 1);
}
size += (sizeof(int) * argc);
i = 0;
if (envp)
{
while (envp[i] != 0)
{
size += (rt_strlen(envp[i]) + 1);
size += sizeof(int);
i++;
}
}
#ifdef ARCH_MM_MMU
/* for aux */
size += sizeof(struct process_aux);
args = (int *)rt_malloc(size);
if (args == RT_NULL)
{
return RT_NULL;
}
/* argc, argv[], 0, envp[], 0 */
str = (char *)((size_t)args + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(int));
#else
args = (int *)rt_malloc(size);
if (args == RT_NULL)
{
return RT_NULL;
}
str = (char*)((int)args + (argc + 2 + i + 1) * sizeof(int));
#endif /* ARCH_MM_MMU */
new_argve = (char **)&args[1];
args[0] = argc;
for (i = 0; i < argc; i++)
{
len = rt_strlen(argv[i]) + 1;
new_argve[i] = str;
lwp_memcpy(str, argv[i], len);
str += len;
}
new_argve[i] = 0;
i++;
new_argve[i] = 0;
if (envp)
{
int j;
for (j = 0; envp[j] != 0; j++)
{
len = rt_strlen(envp[j]) + 1;
new_argve[i] = str;
lwp_memcpy(str, envp[j], len);
str += len;
i++;
}
new_argve[i] = 0;
}
#ifdef ARCH_MM_MMU
/* aux */
aux = (struct process_aux *)(new_argve + i);
aux->item[0].key = AT_EXECFN;
aux->item[0].value = (uint32_t)(size_t)new_argve[0];
i += AUX_ARRAY_ITEMS_NR * 2;
new_argve[i] = 0;
lwp->args = args;
return aux;
#else
lwp->args = args;
lwp->args_length = size;
return (struct process_aux *)(new_argve + i);
#endif /* ARCH_MM_MMU */
}
#endif
rt_err_t lwp_args_put(struct lwp_args_info *args, const char **strv_addr, enum lwp_args_type atype)
{
rt_err_t error;
int iter = 0;
int len;
const char *arg_ptr;
while (1)
{
if (atype == LWP_ARGS_TYPE_ARG || atype == LWP_ARGS_TYPE_ENVP)
{
len = lwp_get_from_user(&arg_ptr, strv_addr + iter++, sizeof(char *));
if (len != sizeof(char *))
{
return -EFAULT;
}
if (arg_ptr == NULL)
{
break;
}
len = lwp_user_strlen(arg_ptr);
if (len < 0)
{
return -EFAULT;
}
}
else
{
arg_ptr = strv_addr[iter++];
if (arg_ptr == NULL)
{
break;
}
len = strlen(arg_ptr);
}
error = args_append(args, arg_ptr, len, atype);
if (error)
{
return error;
}
}
return 0;
}
/**
* @brief Put argument vector to args object
*/
rt_err_t lwp_args_put_argv(struct lwp_args_info *args, const char **argv_uaddr)
{
return lwp_args_put(args, argv_uaddr, LWP_ARGS_TYPE_ARG);
}
/**
* @brief Put argument vector to args object
*/
rt_err_t lwp_args_put_envp(struct lwp_args_info *args, const char **envp_uaddr)
{
return lwp_args_put(args, envp_uaddr, LWP_ARGS_TYPE_ENVP);
}
/**
* read words until reach nextline or EOF.
* words copied into buffer is never truncated.
*/
#define READFILE_STAT_EOF_REACHED 0
#define READFILE_STAT_NEXTLINE_REACHED 0
#define READFILE_STAT_TRUNCATED 1
#define READFILE_STAT_CAN_READMORE(stat) (stat)
static int _readfile(int fd, size_t maxbytes, char *buffer, int *p_readlen)
{
int readlen;
int stat;
char *nlp;
readlen = read(fd, buffer, maxbytes - 1);
if (readlen <= 0)
{
/* eof, failed */
stat = READFILE_STAT_EOF_REACHED;
buffer[0] = '\0';
}
else
{
if ((nlp = strchr(buffer, '\n')) == NULL)
{
if (readlen == maxbytes - 1)
{
int tailing_wordlen = 0;
char *cp = buffer + readlen - 1;
for (; *cp && *cp != ' ' && *cp != '\t'; cp--, tailing_wordlen++)
;
if (tailing_wordlen)
{
lseek(fd, -tailing_wordlen, SEEK_CUR);
readlen -= tailing_wordlen;
stat = READFILE_STAT_TRUNCATED;
}
else
{
stat = READFILE_STAT_EOF_REACHED;
}
}
else
{
stat = READFILE_STAT_EOF_REACHED;
}
}
else
{
stat = READFILE_STAT_NEXTLINE_REACHED;
readlen = nlp - buffer;
}
buffer[readlen] = '\0';
}
if (p_readlen)
*p_readlen = readlen;
return stat;
}
static char *_find_word(char *cp)
{
for (; (*cp == ' ') || (*cp == '\t'); cp++)
;
return cp;
}
static char *_seperate_and_get_nextword(char *cp)
{
/* find next whitespace */
for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
;
/* seperate words */
while ((*cp == ' ') || (*cp == '\t'))
{
*cp++ = '\0';
}
return cp;
}
#define INTERP_BUF_SIZE 128
rt_err_t lwp_args_load_script(struct lwp_args_info *ai, const char *filename)
{
rt_err_t error = -1;
int fd = -RT_ERROR;
int len;
int rf_stat;
char interp[INTERP_BUF_SIZE];
char *cp, *nextword;
char script_magic[2];
struct lwp_args_info ow_ai = {0};
fd = open(filename, O_BINARY | O_RDONLY, 0);
if (fd < 0)
{
goto quit;
}
/**
* verify an interpreter script by matching script file magic
* eg: #!/bin/sh
*/
len = read(fd, script_magic, sizeof(script_magic));
if (len != 2 || memcmp(script_magic, "#!", sizeof(script_magic)))
{
goto quit;
}
/* setup a new args struct to save script arguments */
if (args_init(&ow_ai, INTERP_BUF_SIZE))
{
goto quit;
}
while (1)
{
/* read file to buffer (avoid any truncated words in buffer) */
rf_stat = _readfile(fd, INTERP_BUF_SIZE, interp, &len);
if (len <= 0)
{
goto quit;
}
/* find first word until reaching nil */
cp = _find_word(interp);
if (*cp == '\0')
{
if (READFILE_STAT_CAN_READMORE(rf_stat))
continue;
else
break;
}
do
{
nextword = _seperate_and_get_nextword(cp);
args_append(&ow_ai, cp, strlen(cp), LWP_ARGS_TYPE_KARG);
cp = nextword;
}
while (*cp);
if (READFILE_STAT_CAN_READMORE(rf_stat))
continue;
else
break;
}
if (ow_ai.argv.string_count == 0)
{
goto quit; /* No interpreter name found */
}
args_append(&ow_ai, filename, strlen(filename), LWP_ARGS_TYPE_KARG);
error = _args_override_argv0(ai, &ow_ai);
if (error)
{
goto quit;
}
quit:
lwp_args_detach(&ow_ai);
if (fd >= 0)
{
close(fd);
}
return error;
}
char** lwp_get_command_line_args(struct rt_lwp *lwp)
{
size_t argc = 0;
char** argv = NULL;
int ret;
size_t i;
size_t len;
if (lwp)
{
ret = lwp_data_get(lwp, &argc, lwp->args, sizeof(argc));
if (ret == 0)
{
return RT_NULL;
}
argv = (char**)rt_malloc((argc + 1) * sizeof(char*));
if (argv)
{
for (i = 0; i < argc; i++)
{
char *argvp = NULL;
ret = lwp_data_get(lwp, &argvp, &((char **)lwp->args)[1 + i], sizeof(argvp));
if (ret == 0)
{
lwp_free_command_line_args(argv);
return RT_NULL;
}
len = lwp_user_strlen_ext(lwp, argvp);
if (len > 0)
{
argv[i] = (char*)rt_malloc(len + 1);
ret = lwp_data_get(lwp, argv[i], argvp, len);
if (ret == 0)
{
lwp_free_command_line_args(argv);
return RT_NULL;
}
argv[i][len] = '\0';
}
else
{
argv[i] = NULL;
}
}
argv[argc] = NULL;
}
}
return argv;
}
void lwp_print_envp(struct rt_lwp *lwp)
{
rt_size_t envp_counts;
char **kenvp_array = lwp_get_envp(lwp, &envp_counts);
if (kenvp_array)
{
rt_kprintf("envp_counts: %d\n", envp_counts);
for (size_t i = 0; i < envp_counts; i++)
{
rt_kprintf("envp[%d]: %s\n", i, kenvp_array[i]);
}
}
lwp_free_command_line_args(kenvp_array);
return ;
}
char** lwp_get_envp(struct rt_lwp *lwp, rt_size_t *penvp_counts)
{
int ret, len;
rt_base_t argc;
char **p_kenvp = RT_NULL;
char *envp, **p_envp;
size_t envp_counts = 0;
if (lwp)
{
ret = lwp_data_get(lwp, &argc, lwp->args, sizeof(argc));
if (ret == 0)
{
return RT_NULL;
}
p_envp = (char **)lwp->args + 1 + argc + 1;
/* counts envp */
while (lwp_data_get(lwp, &envp, p_envp, sizeof(void *)) == sizeof(void *)
&& envp != NULL)
{
p_envp++;
envp_counts++;
}
p_kenvp = (char **)rt_malloc((envp_counts + 1) * sizeof(char *));
if (p_kenvp)
{
/* copy env from envp array */
p_envp = (char **)lwp->args + 1 + argc + 1;
for (size_t i = 0; i < envp_counts; i++)
{
ret = lwp_data_get(lwp, &envp, &p_envp[i], sizeof(char *));
if (ret != sizeof(char **))
{
lwp_free_command_line_args(p_kenvp);
return RT_NULL;
}
len = lwp_user_strlen_ext(lwp, envp);
if (len > 0)
{
p_kenvp[i] = (char*)rt_malloc(len + 1);
ret = lwp_data_get(lwp, p_kenvp[i], envp, len + 1);
if (ret != len + 1)
{
lwp_free_command_line_args(p_kenvp);
return RT_NULL;
}
}
else
{
p_kenvp[i] = NULL;
}
}
if (penvp_counts)
*penvp_counts = envp_counts;
p_kenvp[envp_counts] = NULL;
}
}
return p_kenvp;
}
void lwp_free_command_line_args(char** argv)
{
size_t i;
if (argv)
{
for (i = 0; argv[i]; i++)
{
rt_free(argv[i]);
}
rt_free(argv);
}
}