diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/main.c | 18 | ||||
-rw-r--r-- | src/spawn.c | 18 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rwxr-xr-x | tests/monkey-parse.apl | 23 | ||||
-rwxr-xr-x | tests/monkey-test.apl | 63 |
6 files changed, 129 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac index 959afbc..870b7b0 100644 --- a/configure.ac +++ b/configure.ac @@ -148,6 +148,10 @@ case $host in ;; esac +# Optional support for sandboxing via FreeBSD's Capsicum. +AC_CHECK_FUNCS([cap_enter cap_getmode]) +AC_CHECK_HEADERS([sys/capsicum.h]) + # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_INLINE @@ -29,6 +29,10 @@ #include <glib/gprintf.h> #include <glib/gstdio.h> +#ifdef HAVE_SYS_CAPSICUM_H +#include <sys/capsicum.h> +#endif + #include "sciteco.h" #include "file-utils.h" #include "cmdline.h" @@ -106,6 +110,7 @@ static gchar *teco_eval_macro = NULL; static gboolean teco_mung_file = FALSE; static gboolean teco_mung_profile = TRUE; static gchar *teco_fake_cmdline = NULL; +static gboolean teco_sandbox = FALSE; static gboolean teco_8bit_clean = FALSE; static gchar * @@ -125,6 +130,9 @@ teco_process_options(gchar ***argv) {"fake-cmdline", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &teco_fake_cmdline, "Emulate key presses in batch mode (for debugging)", "keys"}, + {"sandbox", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &teco_sandbox, + "Sandbox application (for debugging)"}, {"8bit", '8', 0, G_OPTION_ARG_NONE, &teco_8bit_clean, "Use ANSI encoding by default and disable automatic EOL conversion"}, {NULL} @@ -336,6 +344,16 @@ main(int argc, char **argv) * to the macro or munged file. */ +#ifdef HAVE_CAP_ENTER + /* + * In the sandbox, we cannot access files or execute external processes. + * Effectively, munging won't work, so you can pass macros only via + * --eval or --fake-cmdline. + */ + if (G_UNLIKELY(teco_sandbox)) + cap_enter(); +#endif + if (teco_8bit_clean) /* equivalent to 16,4ED but executed earlier */ teco_ed = (teco_ed & ~TECO_ED_AUTOEOL) | TECO_ED_DEFAULT_ANSI; diff --git a/src/spawn.c b/src/spawn.c index 7a5736c..9816975 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -30,6 +30,10 @@ #include <windows.h> #endif +#ifdef HAVE_SYS_CAPSICUM_H +#include <sys/capsicum.h> +#endif + #include "sciteco.h" #include "interface.h" #include "undo.h" @@ -268,6 +272,20 @@ teco_state_execute_done(teco_machine_main_t *ctx, const teco_string_t *str, GErr g_autoptr(GIOChannel) stdin_chan = NULL, stdout_chan = NULL; g_auto(GStrv) argv = NULL, envp = NULL; +#ifdef HAVE_CAP_GETMODE + /* + * If we don't explicitly check for sandboxing, glib could assert + * internally and we want to detect all unexpected assertions + * in "infinite monkey"-style tests. + */ + u_int sandbox_mode; + if (G_UNLIKELY(cap_getmode(&sandbox_mode) || sandbox_mode)) { + g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, + "Forbidden in Capsicum sandbox"); + goto gerror; + } +#endif + if (!str->len || teco_string_contains(str, '\0')) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, "Command line must not be empty or contain null-bytes"); diff --git a/tests/Makefile.am b/tests/Makefile.am index eea6b67..96ec672 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -39,3 +39,6 @@ AUTOTEST = $(AUTOM4TE) --language=autotest $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at mv $@.tmp $@ + +# For manually running "infinite monkey"-style tests. +EXTRA_DIST += monkey-parse.apl monkey-test.apl diff --git a/tests/monkey-parse.apl b/tests/monkey-parse.apl new file mode 100755 index 0000000..9273962 --- /dev/null +++ b/tests/monkey-parse.apl @@ -0,0 +1,23 @@ +#!/usr/local/bin/apl --script -- +⍝ Calculate frequency distribution of the characters/glyphs in a given number of input files. +⍝ This will be the input for monkey-test.apl. +Files ← 4↓⎕ARG +Data ← 19 ⎕CR ∊⎕FIO['read_file']¨Files + +∇ Dist←GetDist Data; i; C + ⍝Dist ← 38 ⌷CR ↑⍴Data ⍝ associative array + Dist.X ← 0 + i ← 0 +Loop: + C ← ,Data[i←i+1] + ~∨/∊C=Dist[;1] →→ Dist[C] ← 0 ←← + Dist[C] ← Dist[C] + 1 +→(i<⍴Data)/Loop +∇ + +Dist ← GetDist Data +Freq ← {Dist[⍵]}¨Dist[;1] +⎕ ← "Chars ←", ⎕UCS ∊Dist[;1][Sort←⍋Freq] ⍝ character codepoints +⎕ ← "Freq ←", Freq[Sort] ⍝ character frequencies + +)OFF diff --git a/tests/monkey-test.apl b/tests/monkey-test.apl new file mode 100755 index 0000000..a8cac1c --- /dev/null +++ b/tests/monkey-test.apl @@ -0,0 +1,63 @@ +#!/usr/local/bin/apl --script -- +⍝ "Monkey on a typewriter"-style test. +⍝ This generates an infinite number of fake TECO macros, trying to crash +⍝ the parser. +⍝ The generated scripts are not entirely random, but follow the frequency +⍝ distribution calculated over the entire SciTECO "corpus", as generated by +⍝ monkey-parse.apl. +⍝ This requires a recent version of GNU APL and support for sandboxing +⍝ (currently only on FreeBSD). +5≤⍴⎕ARG →→ SciTECO←∊4↓⎕ARG ←→ SciTECO←"sciteco" ←← + +⍝ Sorted character-to-frequency distribution, +Chars ← 8 63 7 86 38 9045 8594 8592 106 96 30 43 89 113 37 31 90 126 75 55 66 57 95 87 124 54 88 122 11 5 36 92 61 41 20 59 120 56 118 52 35 72 71 60 62 80 40 74 125 123 53 70 82 119 67 107 76 68 121 65 21 34 94 51 78 79 47 117 50 64 58 39 102 42 73 112 109 45 84 77 104 33 100 83 85 27 103 49 98 81 48 44 69 105 110 116 93 97 91 115 99 46 10 114 108 101 111 32 +Freq ← 1 1 2 2 3 4 4 4 4 6 7 8 8 11 14 15 15 15 17 18 18 19 25 26 26 28 28 28 30 30 30 31 32 32 33 34 37 40 40 41 41 44 46 47 48 51 52 59 60 62 63 63 74 74 78 80 82 89 90 93 95 107 107 113 115 116 118 123 126 127 128 133 134 142 146 146 158 163 168 173 173 176 180 198 200 210 224 233 257 264 295 319 348 402 411 476 478 480 481 494 576 672 726 751 756 860 1033 2664 + +CumFreq ← +\Freq +Sum ← +/Freq + +⍝ Random seed +⊣⎕RL ← +/⎕TS + +⍝ Codepoints ← GenTECO Len +GenTECO ← {Chars[{(CumFreq ≥ ⍵) ⍳ 1}¨⍵ ? Sum]~0} +GenArg ← {(⍵ ? $3000)~0 27} + +⍝ Escape as shell argument +Escape ← {"'",(∊{⍺,"'\\''",⍵}/(⍵≠"'") ⊂ ⍵),"'"} + +⍝ File WriteFile Contents +WriteFile ← {⊣⍵ ⎕FIO['fwrite_UNI'] Hnd ← "w" ⎕FIO['fopen'] ⍺ ⋄ ⊣⎕FIO['fclose'] Hnd; Hnd} + +∇ Sig←Arg Exec Macro; Hnd; Data; Stdout + ⍝ FIXME: We cannot currently mung files in --sandbox mode. + ⍝ To support that, we would effectively have to add a test compilation unit, + ⍝ in which case we could just generate the test macro in C code as well. + Macro ← "EI",Arg,(⎕UCS 27),"J ",Macro + ⍝ We can generate loops by accident, but they should all be interruptible with SIGINT. + ⍝ If the process needs to be killed, this is also considered a fatal issue. + Hnd ← ⎕FIO['popen'] "timeout --signal SIGINT --kill-after 2 2 ",SciTECO," --sandbox --eval ",(Escape Macro)," 2>&1" + + Stdout ← "" +ReadStdout: + Stdout ← Stdout,Data ← ⎕UCS (⎕FIO['fread'] Hnd) +→(0≤↑Data)/ReadStdout + +Cleanup: + ⍝⎕ ← Stdout + ⍝ Extract WTERMSIG() + ⍝ FIXME: This may work only on FreeBSD + Sig ← $7F ⊤∧ (⎕FIO['pclose'] Hnd) + Sig≠0 →→ "monkey-bug.tec" WriteFile Macro ⋄ ⍞ ← Stdout ←← +∇ + +∇ RunBatch; Sig +Loop: + Sig ← (⎕UCS GenArg ?100) Exec (⎕UCS GenTECO ?100) +→(Sig=0)/Loop + ⎕ ← "Script in monkey-bug.tec terminated with signal: " Sig +∇ + +RunBatch + +)OFF |