From dce61eb2d7caf7f570701161ee1cada07a862835 Mon Sep 17 00:00:00 2001 From: Peter Bex Date: Fri, 24 Mar 2023 10:42:49 +0100 Subject: [PATCH] Stop run-time option processing after "-:" or the first non-runtime option This means that runtime options after non-runtime options will no longer be processed. Programs using command-line-arguments will see all options starting at the first non-runtime option, even if later options start with "-:". Processing can also be stopped explicitly with a single "-:" option, which will not be seen by programs using command-line-arguments. Every argument following this "-:" will be seen, even if they start with "-:". --- NEWS | 4 + library.scm | 22 +- manual/Module (chicken process-context) | 22 +- manual/Using the compiler | 15 +- runtime.c | 258 ++++++++++++------------ 5 files changed, 179 insertions(+), 142 deletions(-) diff --git a/NEWS b/NEWS index 23661fc2..6ecbbad2 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,10 @@ to avoid arbitrary OS command injection during egg installation, reported by Vasilij Schneidermann who also provided the necessary patches to mitigate this problem. + - The runtime option "-:b" has been removed, as it was deemed too + insecure to be able to drop to a REPL from the CLI of any program. + - Runtime option processing has been hardened: processing now stops on + the first non-runtime option or after "-:", whichever comes first. - Core libraries - Added "locative-index", kindly contributed by John Croisant. diff --git a/library.scm b/library.scm index 827666d5..819dacfe 100644 --- a/library.scm +++ b/library.scm @@ -6024,17 +6024,23 @@ static C_word C_fcall C_setenv(C_word x, C_word y) { (define command-line-arguments (make-parameter - (let ([args (argv)]) + (let ((args (argv))) (if (pair? args) - (let loop ([args (##sys#slot args 1)]) + (let loop ((args (##sys#slot args 1))) ; Skip over program name (argv[0]) (if (null? args) '() - (let ([arg (##sys#slot args 0)] - [r (##sys#slot args 1)] ) - (if (and (fx>= (##sys#size arg) 3) - (string=? "-:" (##sys#substring arg 0 2))) - (loop r) - (cons arg (loop r)) ) ) ) ) + (let ((arg (##sys#slot args 0)) + (rest (##sys#slot args 1)) ) + (cond + ((string=? "-:" arg) ; Consume first "empty" runtime options list, return rest + rest) + + ((and (fx>= (##sys#size arg) 3) + (string=? "-:" (##sys#substring arg 0 2))) + (loop rest)) + + ;; First non-runtime option and everything following it is returned as-is + (else args) ) ) ) ) args) ) (lambda (x) (##sys#check-list x 'command-line-arguments) diff --git a/manual/Module (chicken process-context) b/manual/Module (chicken process-context) index 5299905e..87165187 100644 --- a/manual/Module (chicken process-context) +++ b/manual/Module (chicken process-context) @@ -13,7 +13,7 @@ This module provides access to the current process context. Returns two values: an integer and a foreign-pointer object representing the {{argc}} and {{argv}} arguments passed to the current -process. +process. See also {{argv}} below. ==== argv @@ -24,13 +24,27 @@ the list is a string containing the name of the executing program. The other items are the arguments passed to the application. It depends on the host-shell whether arguments are expanded ('globbed') or not. +NOTE: This is the "raw" unprocessed argument list, including runtime +options (starting with {{-:}}) which are consumed by the runtime +library. + ==== command-line-arguments (command-line-arguments) -Contains the list of arguments passed to this program, with the name of -the program and any runtime options (all options starting with {{-:}}) -removed. +Contains the list of arguments passed to this program. + +This ''excludes'' the name of the program, as well as any runtime +options (options starting with {{-:}}) up until the first empty +runtime option (just {{"-:"}}) or non-runtime option, whichever +comes first. + +In other words, this method returns every option ''after'' the first +list of unbroken runtime options, which are all skipped. If an empty +runtime option is present, that is the last of this list of unbroken +runtime options and everything after it is returned by this method. +If a non-runtime option is present, that also breaks up the runtime +options and this method returns that and every following option. ==== executable-pathname diff --git a/manual/Using the compiler b/manual/Using the compiler index 30e5ed2a..e9eb3672 100644 --- a/manual/Using the compiler +++ b/manual/Using the compiler @@ -220,10 +220,17 @@ to see a list of all supported options and short aliases to basic options. === Runtime options After successful compilation a C source file is generated and can be -compiled with a C compiler. Executables generated with CHICKEN (and the -compiler itself) accept a small set of runtime options. These are filtered out -by the startup code and will not be contained in the result of -{{(command-line-arguments)}}. +compiled with a C compiler. Executables generated with CHICKEN (and +the compiler itself) accept a small set of runtime options. +Processing of these options stops at the first non-runtime option, or +''after'' the empty runtime option (just "{{-:}}"), whichever comes +first. + +In other words, the program sees every option after the first list of +unbroken runtime options, which are all consumed. If an empty runtime +option is present, that is the last of this list of unbroken runtime +options. If a non-runtime option is present, that also breaks up the +runtime options. ; {{-:?}} : Shows a list of the available runtime options and exits the program. diff --git a/runtime.c b/runtime.c index aa64092b..ac7f2392 100644 --- a/runtime.c +++ b/runtime.c @@ -1348,153 +1348,159 @@ void CHICKEN_parse_command_line(int argc, char *argv[], C_word *heap, C_word *st *stack = DEFAULT_STACK_SIZE; *symbols = DEFAULT_SYMBOL_TABLE_SIZE; - for(i = 1; i < C_main_argc; ++i) - if(!strncmp(C_main_argv[ i ], C_text("-:"), 2)) { - for(ptr = &C_main_argv[ i ][ 2 ]; *ptr != '\0';) { - switch(*(ptr++)) { - case '?': - C_dbg("Runtime options", "\n\n" - " -:? display this text\n" - " -:c always treat stdin as console\n" - " -:d enable debug output\n" - " -:D enable more debug output\n" - " -:g show GC information\n" - " -:o disable stack overflow checks\n" - " -:hiSIZE set initial heap size\n" - " -:hmSIZE set maximal heap size\n" - " -:hfSIZE set minimum unused heap size\n" - " -:hgPERCENTAGE set heap growth percentage\n" - " -:hsPERCENTAGE set heap shrink percentage\n" - " -:huPERCENTAGE set percentage of memory used at which heap will be shrunk\n" - " -:hSIZE set fixed heap size\n" - " -:r write trace output to stderr\n" - " -:RSEED initialize rand() seed with SEED (helpful for benchmark stability)\n" - " -:p collect statistical profile and write to file at exit\n" - " -:PFREQUENCY like -:p, specifying sampling frequency in us (default: 10000)\n" - " -:sSIZE set nursery (stack) size\n" - " -:tSIZE set symbol-table size\n" - " -:fSIZE set maximal number of pending finalizers\n" - " -:x deliver uncaught exceptions of other threads to primordial one\n" - " -:B sound bell on major GC\n" - " -:G force GUI mode\n" - " -:aSIZE set trace-buffer/call-chain size\n" - " -:ASIZE set fixed temporary stack size\n" - " -:H dump heap state on exit\n" - " -:S do not handle segfaults or other serious conditions\n" - "\n SIZE may have a `k' (`K'), `m' (`M') or `g' (`G') suffix, meaning size\n" - " times 1024, 1048576, and 1073741824, respectively.\n\n"); - C_exit_runtime(C_fix(0)); - - case 'h': - switch(*ptr) { - case 'i': - *heap = arg_val(ptr + 1); - heap_size_changed = 1; - goto next; - case 'f': - C_heap_half_min_free = arg_val(ptr + 1); - goto next; - case 'g': - C_heap_growth = arg_val(ptr + 1); - goto next; - case 'm': - C_maximal_heap_size = arg_val(ptr + 1); - goto next; - case 's': - C_heap_shrinkage = arg_val(ptr + 1); - goto next; - case 'u': - C_heap_shrinkage_used = arg_val(ptr + 1); - goto next; - default: - *heap = arg_val(ptr); - heap_size_changed = 1; - C_heap_size_is_fixed = 1; - goto next; - } + for(i = 1; i < C_main_argc; ++i) { + if (strncmp(C_main_argv[ i ], C_text("-:"), 2)) + break; /* Stop parsing on first non-runtime option */ - case 'o': - C_disable_overflow_check = 1; - break; + ptr = &C_main_argv[ i ][ 2 ]; + if (*ptr == '\0') + break; /* Also stop parsing on first "empty" option (i.e. "-:") */ - case 'B': - gc_bell = 1; - break; + do { + switch(*(ptr++)) { + case '?': + C_dbg("Runtime options", "\n\n" + " -:? display this text\n" + " -:c always treat stdin as console\n" + " -:d enable debug output\n" + " -:D enable more debug output\n" + " -:g show GC information\n" + " -:o disable stack overflow checks\n" + " -:hiSIZE set initial heap size\n" + " -:hmSIZE set maximal heap size\n" + " -:hfSIZE set minimum unused heap size\n" + " -:hgPERCENTAGE set heap growth percentage\n" + " -:hsPERCENTAGE set heap shrink percentage\n" + " -:huPERCENTAGE set percentage of memory used at which heap will be shrunk\n" + " -:hSIZE set fixed heap size\n" + " -:r write trace output to stderr\n" + " -:RSEED initialize rand() seed with SEED (helpful for benchmark stability)\n" + " -:p collect statistical profile and write to file at exit\n" + " -:PFREQUENCY like -:p, specifying sampling frequency in us (default: 10000)\n" + " -:sSIZE set nursery (stack) size\n" + " -:tSIZE set symbol-table size\n" + " -:fSIZE set maximal number of pending finalizers\n" + " -:x deliver uncaught exceptions of other threads to primordial one\n" + " -:B sound bell on major GC\n" + " -:G force GUI mode\n" + " -:aSIZE set trace-buffer/call-chain size\n" + " -:ASIZE set fixed temporary stack size\n" + " -:H dump heap state on exit\n" + " -:S do not handle segfaults or other serious conditions\n" + "\n SIZE may have a `k' (`K'), `m' (`M') or `g' (`G') suffix, meaning size\n" + " times 1024, 1048576, and 1073741824, respectively.\n\n"); + C_exit_runtime(C_fix(0)); + + case 'h': + switch(*ptr) { + case 'i': + *heap = arg_val(ptr + 1); + heap_size_changed = 1; + goto next; + case 'f': + C_heap_half_min_free = arg_val(ptr + 1); + goto next; + case 'g': + C_heap_growth = arg_val(ptr + 1); + goto next; + case 'm': + C_maximal_heap_size = arg_val(ptr + 1); + goto next; + case 's': + C_heap_shrinkage = arg_val(ptr + 1); + goto next; + case 'u': + C_heap_shrinkage_used = arg_val(ptr + 1); + goto next; + default: + *heap = arg_val(ptr); + heap_size_changed = 1; + C_heap_size_is_fixed = 1; + goto next; + } - case 'G': - C_gui_mode = 1; - break; + case 'o': + C_disable_overflow_check = 1; + break; - case 'H': - dump_heap_on_exit = 1; - break; + case 'B': + gc_bell = 1; + break; - case 'S': - pass_serious_signals = 1; - break; + case 'G': + C_gui_mode = 1; + break; - case 's': - *stack = arg_val(ptr); - stack_size_changed = 1; - goto next; + case 'H': + dump_heap_on_exit = 1; + break; - case 'f': - C_max_pending_finalizers = arg_val(ptr); - goto next; + case 'S': + pass_serious_signals = 1; + break; - case 'a': - C_trace_buffer_size = arg_val(ptr); - goto next; + case 's': + *stack = arg_val(ptr); + stack_size_changed = 1; + goto next; - case 'A': - fixed_temporary_stack_size = arg_val(ptr); - goto next; + case 'f': + C_max_pending_finalizers = arg_val(ptr); + goto next; - case 't': - *symbols = arg_val(ptr); - goto next; + case 'a': + C_trace_buffer_size = arg_val(ptr); + goto next; - case 'c': - fake_tty_flag = 1; - break; + case 'A': + fixed_temporary_stack_size = arg_val(ptr); + goto next; - case 'd': - debug_mode = 1; - break; + case 't': + *symbols = arg_val(ptr); + goto next; - case 'D': - debug_mode = 2; - break; + case 'c': + fake_tty_flag = 1; + break; - case 'g': - gc_report_flag = 2; - break; + case 'd': + debug_mode = 1; + break; - case 'P': - profiling = 1; - profile_frequency = arg_val(ptr); - goto next; + case 'D': + debug_mode = 2; + break; - case 'p': - profiling = 1; - break; + case 'g': + gc_report_flag = 2; + break; - case 'r': - show_trace = 1; - break; + case 'P': + profiling = 1; + profile_frequency = arg_val(ptr); + goto next; - case 'R': - srand((unsigned int)arg_val(ptr)); - random_state_initialized = 1; - goto next; + case 'p': + profiling = 1; + break; - case 'x': - C_abort_on_thread_exceptions = 1; - break; + case 'r': + show_trace = 1; + break; - default: panic(C_text("illegal runtime option")); - } + case 'R': + srand((unsigned int)arg_val(ptr)); + random_state_initialized = 1; + goto next; + + case 'x': + C_abort_on_thread_exceptions = 1; + break; + + default: panic(C_text("illegal runtime option")); } + } while(*ptr != '\0'); next:; } -- 2.38.4