Fossil SCM

Created fossil_spawn() as a dumb-as-rocks wrapper for posix_spawnp(2) and created tests to prove that it doesn't double-interpret its args as fossil_system() does. This isn't portable yet; it's just a seed we can use to bring this branch to a PoC state.

wyoung 2021-06-21 22:37 trunk
Commit fb7c5797365ee55dbeb1a70fde8fc5b8c3169be1c90fdf54f723435506abcea8
+47
--- src/util.c
+++ src/util.c
@@ -34,10 +34,11 @@
3434
# include <sys/time.h>
3535
# include <sys/resource.h>
3636
# include <unistd.h>
3737
# include <fcntl.h>
3838
# include <errno.h>
39
+# include <spawn.h>
3940
#endif
4041
4142
4243
/*
4344
** Exit. Take care to close the database first.
@@ -362,10 +363,56 @@
362363
fflush(stdout);
363364
rc = fossil_system(zLine);
364365
printf("result: %d\n", rc);
365366
}
366367
}
368
+
369
+/*
370
+** A cross-platform "spawn" type function: search for zProgram in PATH,
371
+** passing the given argument list through without interpretation. Due
372
+** to Windows platform limitations — see the definition of WinMain() —
373
+** this is an ideal we can achieve only on POSIX platforms.
374
+*/
375
+int fossil_spawn(const char* zProgram, char* const azArgv[])
376
+{
377
+ extern char **environ;
378
+ int status = -1;
379
+ pid_t pid;
380
+ if(posix_spawnp(&pid, zProgram, NULL, NULL, azArgv, environ)==0){
381
+ waitpid(pid, &status, 0);
382
+ }else{
383
+ fossil_fatal("posix_spawn(%s, ...) failed: %s", zProgram, sys_errlist[errno]);
384
+ }
385
+ return status;
386
+}
387
+
388
+/*
389
+** COMMAND: test-fossil-spawn
390
+**
391
+** Calls test-echo, passing each argument as-given. Used by the test
392
+** suite to verify that this platform can pass problematic arguments
393
+** (quotes, spaces, shell-specific escaping characters...) through
394
+** fossil_spawn() without mangling or reinterpretation.
395
+**
396
+** To an outsider, this function has the same effect as test-echo,
397
+** but the way it achieves that exercises a different subset of
398
+** Fossil's functionality.
399
+*/
400
+void test_fossil_spawn_cmd(void){
401
+ int i, j=0;
402
+ char** azArgv = fossil_malloc(sizeof(char*) * (g.argc + 1));
403
+ azArgv[j++] = g.nameOfExe;
404
+ azArgv[j++] = "test-echo";
405
+ if( find_option("hex",0,0) ){
406
+ azArgv[j++] = "--hex";
407
+ }
408
+ for( i=2; i<g.argc; ++i ){
409
+ azArgv[j++] = g.argv[i];
410
+ }
411
+ azArgv[j] = 0;
412
+ fossil_spawn(g.nameOfExe, azArgv);
413
+}
367414
368415
/*
369416
** Like strcmp() except that it accepts NULL pointers. NULL sorts before
370417
** all non-NULL string pointers. Also, this strcmp() is a binary comparison
371418
** that does not consider locale.
372419
--- src/util.c
+++ src/util.c
@@ -34,10 +34,11 @@
34 # include <sys/time.h>
35 # include <sys/resource.h>
36 # include <unistd.h>
37 # include <fcntl.h>
38 # include <errno.h>
 
39 #endif
40
41
42 /*
43 ** Exit. Take care to close the database first.
@@ -362,10 +363,56 @@
362 fflush(stdout);
363 rc = fossil_system(zLine);
364 printf("result: %d\n", rc);
365 }
366 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
368 /*
369 ** Like strcmp() except that it accepts NULL pointers. NULL sorts before
370 ** all non-NULL string pointers. Also, this strcmp() is a binary comparison
371 ** that does not consider locale.
372
--- src/util.c
+++ src/util.c
@@ -34,10 +34,11 @@
34 # include <sys/time.h>
35 # include <sys/resource.h>
36 # include <unistd.h>
37 # include <fcntl.h>
38 # include <errno.h>
39 # include <spawn.h>
40 #endif
41
42
43 /*
44 ** Exit. Take care to close the database first.
@@ -362,10 +363,56 @@
363 fflush(stdout);
364 rc = fossil_system(zLine);
365 printf("result: %d\n", rc);
366 }
367 }
368
369 /*
370 ** A cross-platform "spawn" type function: search for zProgram in PATH,
371 ** passing the given argument list through without interpretation. Due
372 ** to Windows platform limitations — see the definition of WinMain() —
373 ** this is an ideal we can achieve only on POSIX platforms.
374 */
375 int fossil_spawn(const char* zProgram, char* const azArgv[])
376 {
377 extern char **environ;
378 int status = -1;
379 pid_t pid;
380 if(posix_spawnp(&pid, zProgram, NULL, NULL, azArgv, environ)==0){
381 waitpid(pid, &status, 0);
382 }else{
383 fossil_fatal("posix_spawn(%s, ...) failed: %s", zProgram, sys_errlist[errno]);
384 }
385 return status;
386 }
387
388 /*
389 ** COMMAND: test-fossil-spawn
390 **
391 ** Calls test-echo, passing each argument as-given. Used by the test
392 ** suite to verify that this platform can pass problematic arguments
393 ** (quotes, spaces, shell-specific escaping characters...) through
394 ** fossil_spawn() without mangling or reinterpretation.
395 **
396 ** To an outsider, this function has the same effect as test-echo,
397 ** but the way it achieves that exercises a different subset of
398 ** Fossil's functionality.
399 */
400 void test_fossil_spawn_cmd(void){
401 int i, j=0;
402 char** azArgv = fossil_malloc(sizeof(char*) * (g.argc + 1));
403 azArgv[j++] = g.nameOfExe;
404 azArgv[j++] = "test-echo";
405 if( find_option("hex",0,0) ){
406 azArgv[j++] = "--hex";
407 }
408 for( i=2; i<g.argc; ++i ){
409 azArgv[j++] = g.argv[i];
410 }
411 azArgv[j] = 0;
412 fossil_spawn(g.nameOfExe, azArgv);
413 }
414
415 /*
416 ** Like strcmp() except that it accepts NULL pointers. NULL sorts before
417 ** all non-NULL string pointers. Also, this strcmp() is a binary comparison
418 ** that does not consider locale.
419
--- test/cmdline.test
+++ test/cmdline.test
@@ -46,9 +46,28 @@
4646
#
4747
# NOTE: Use an --args file on Windows to avoid unwanted glob expansion
4848
# from MinGW and/or the MSVCRT.
4949
#
5050
cmd-line $is_windows 101 * * *.* *.*
51
+
52
+
53
+###############################################################################
54
+# Now do similar things with the new "spawn" mechanism, checking whether
55
+# args passed through a potential double interpretation call sequence do
56
+# not in fact get double-interpreted.
57
+
58
+proc spawn {args} {
59
+ set i 1
60
+ foreach arg $args {
61
+ fossil test-fossil-spawn $arg
62
+ test spawn-echo.$i \
63
+ {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$arg\]\}"}
64
+ incr i
65
+ }
66
+}
67
+
68
+spawn "a" "bx" {$SHELL} {$(ls)} "%COMSPEC%" "Jörg's “Development” Job Offer"
69
+
5170
5271
###############################################################################
5372
5473
test_cleanup
5574
--- test/cmdline.test
+++ test/cmdline.test
@@ -46,9 +46,28 @@
46 #
47 # NOTE: Use an --args file on Windows to avoid unwanted glob expansion
48 # from MinGW and/or the MSVCRT.
49 #
50 cmd-line $is_windows 101 * * *.* *.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
52 ###############################################################################
53
54 test_cleanup
55
--- test/cmdline.test
+++ test/cmdline.test
@@ -46,9 +46,28 @@
46 #
47 # NOTE: Use an --args file on Windows to avoid unwanted glob expansion
48 # from MinGW and/or the MSVCRT.
49 #
50 cmd-line $is_windows 101 * * *.* *.*
51
52
53 ###############################################################################
54 # Now do similar things with the new "spawn" mechanism, checking whether
55 # args passed through a potential double interpretation call sequence do
56 # not in fact get double-interpreted.
57
58 proc spawn {args} {
59 set i 1
60 foreach arg $args {
61 fossil test-fossil-spawn $arg
62 test spawn-echo.$i \
63 {[lrange [split $::RESULT \n] 3 end]=="\{argv\[2\] = \[$arg\]\}"}
64 incr i
65 }
66 }
67
68 spawn "a" "bx" {$SHELL} {$(ls)} "%COMSPEC%" "Jörg's “Development” Job Offer"
69
70
71 ###############################################################################
72
73 test_cleanup
74

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button