~ chicken-core (master) d3f97228a1403e69f894cddeb05ef38067457976
commit d3f97228a1403e69f894cddeb05ef38067457976
Author: Peter Bex <peter@more-magic.net>
AuthorDate: Fri Mar 24 10:42:49 2023 +0100
Commit: felix <felix@call-with-current-continuation.org>
CommitDate: Sat Apr 8 11:39:40 2023 +0200
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 "-:".
Signed-off-by: felix <felix@call-with-current-continuation.org>
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
<parameter>(command-line-arguments)</parameter>
-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:;
}
Trap