[1/4] ldso: add option to rewrite the argv block

Submitted by rcombs on March 29, 2020, 12:19 a.m.

Details

Message ID 1585441168-23444-1-git-send-email-rcombs@rcombs.me
State New
Series "Series without cover letter"
Headers show

Commit Message

rcombs March 29, 2020, 12:19 a.m.
---
 ldso/dynlink.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

Patch hide | download patch | download mbox

diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 6468f20..c378f00 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -1698,6 +1698,7 @@  void __dls3(size_t *sp, size_t *auxv)
 	char **argv = (void *)(sp+1);
 	char **argv_orig = argv;
 	char **envp = argv+argc+1;
+	int replace_argv = 0;
 
 	/* Find aux vector just past environ[] and use it to initialize
 	 * global data that may be needed before we can make syscalls. */
@@ -1771,6 +1772,8 @@  void __dls3(size_t *sp, size_t *auxv)
 				if (opt[5]=='=') replace_argv0 = opt+6;
 				else if (opt[5]) *argv = 0;
 				else if (*argv) replace_argv0 = *argv++;
+			} else if (!memcmp(opt, "replace-argv", 12)) {
+				replace_argv = 1;
 			} else {
 				argv[0] = 0;
 			}
@@ -1949,6 +1952,22 @@  void __dls3(size_t *sp, size_t *auxv)
 	debug.state = 0;
 	_dl_debug_state();
 
+	if (replace_argv) {
+		char *argv_end = argv_orig[0];
+		char *orig_ptr = argv_orig[0];
+		int i;
+		for (i = 0; i < (int)(argc - (argv-argv_orig)); i++) {
+			char *src = (i == 0 && replace_argv0) ? replace_argv0 : argv[i];
+			int len = strlen(src) + 1;
+			memmove(orig_ptr, src, len);
+			argv_end = argv[i] + strlen(argv[i]);
+			argv[i] = orig_ptr;
+			orig_ptr += len;
+		}
+		for (; orig_ptr < argv_end; orig_ptr++)
+			*orig_ptr = 0;
+	}
+
 	if (replace_argv0) argv[0] = replace_argv0;
 
 	errno = 0;

Comments

Rich Felker March 29, 2020, 2:54 a.m.
On Sat, Mar 28, 2020 at 07:19:25PM -0500, rcombs wrote:
> ---
>  ldso/dynlink.c | 19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
> 
> diff --git a/ldso/dynlink.c b/ldso/dynlink.c
> index 6468f20..c378f00 100644
> --- a/ldso/dynlink.c
> +++ b/ldso/dynlink.c
> @@ -1698,6 +1698,7 @@ void __dls3(size_t *sp, size_t *auxv)
>  	char **argv = (void *)(sp+1);
>  	char **argv_orig = argv;
>  	char **envp = argv+argc+1;
> +	int replace_argv = 0;
>  
>  	/* Find aux vector just past environ[] and use it to initialize
>  	 * global data that may be needed before we can make syscalls. */
> @@ -1771,6 +1772,8 @@ void __dls3(size_t *sp, size_t *auxv)
>  				if (opt[5]=='=') replace_argv0 = opt+6;
>  				else if (opt[5]) *argv = 0;
>  				else if (*argv) replace_argv0 = *argv++;
> +			} else if (!memcmp(opt, "replace-argv", 12)) {
> +				replace_argv = 1;
>  			} else {
>  				argv[0] = 0;
>  			}
> @@ -1949,6 +1952,22 @@ void __dls3(size_t *sp, size_t *auxv)
>  	debug.state = 0;
>  	_dl_debug_state();
>  
> +	if (replace_argv) {
> +		char *argv_end = argv_orig[0];
> +		char *orig_ptr = argv_orig[0];
> +		int i;
> +		for (i = 0; i < (int)(argc - (argv-argv_orig)); i++) {
> +			char *src = (i == 0 && replace_argv0) ? replace_argv0 : argv[i];
> +			int len = strlen(src) + 1;
> +			memmove(orig_ptr, src, len);
> +			argv_end = argv[i] + strlen(argv[i]);
> +			argv[i] = orig_ptr;
> +			orig_ptr += len;
> +		}
> +		for (; orig_ptr < argv_end; orig_ptr++)
> +			*orig_ptr = 0;
> +	}
> +
>  	if (replace_argv0) argv[0] = replace_argv0;
>  
>  	errno = 0;
> -- 
> 2.7.4

Can you clarify what the purpose of this patch/option is? It seems
unrelated to the rest of the series and looks like it's doing
something really sketchy. It looks like it's making assumption about
the layout of the original strings, which is not an interface
contract, and like it happily overflows and clobbers unrelated memory
if replace_argv0 is longer than the original string pointed to by
argv[0].

Rich