Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: t...@mirbsd.de

Please unblock package mksh

Give it a few days in sid first, please, but I’m filing the
unblock request right now already so it has more time for review.

[ Reason ]
These are cherry-picks, reduced to the minimum necessary (e.g.
less RCS ID churn, some changes that aren’t _that_ crucial removed
or modified in a way to make the diff smaller), that address the
following things:
• some memory leaks
• harden conversion of imported variables to integer, similar to
  Perl’s “taint”; this is basically what ksh93 had as CVE-2019-14868
• fix lexer token state corruption when switching back and forth
  between command line editing and lexing (could be triggered by an
  editing command reliably)
• fix escaping (typeset -p; ${var@Q}) and globbing (tab completion,
  pattern expansion) for pathnames which contain escaped or unescaped
  curly braces, tilde, \x02
Furthermore because this is a new sourceful upload the buildds will
rebuild this against latest linux-libc-dev and klibc (I checked, the
unblock on the latter was already granted) which is a good thing,
especially given klibc’s malloc fixes.

The attached diff is a debdiff in which the single-debian-patch
was replaced by a diff between the patched extracted trees to
increase legibility, as usual. To aid in mapping the patch hunks
to the topics above I will comment on them here:

 mksh-59c/debian/changelog |   25 +++++++++++++

- change documentation

 mksh_59c-6/check.t        |   37 +++++++++++++++++++

- changed version date to 2021/05/03
- new test for the “taint” checks

 mksh_59c-6/edit.c         |   73 ++++++++++++++++++++++++++-------------

- only escaping-related changes

 mksh_59c-6/eval.c         |    6 +--

- memory leak fixes: s/global(arrayname(x))/arraybase(x)/

 mksh_59c-6/misc.c         |    1 

- another memory leak, in command line option -T processing

 mksh_59c-6/mksh.1         |    4 +-

- documentation for the escaping-related changes

 mksh_59c-6/mksh.faq       |   18 ++++++++-

- remove note about evaluate-region being broken (lexer state thing)
- documentation for the “taint” thing

 mksh_59c-6/sh.h           |   14 +++----

- version number
- escaping-related changes
- arrayname ⇒ arraybase (prototype)

 mksh_59c-6/shf.c          |    2 -

- escaping-related

 mksh_59c-6/syn.c          |    4 +-

- lexer state issue

 mksh_59c-6/var.c          |   86 ++++++++++++++++++++++++++++++----------------

- the @@ -938,7 +944,7 @@ hunk and the last one are memory
  leak-related (arrayname ⇒ arraybase; arraybase implementation
  free()s the temporary variable after calling global() properly
  and uses a stack buffer for small values to reduce memory
  allocator pressure)
- all others: “taint”: factor out checking a string for being
  a valid integer from getint() to getnum() and call getnum()
  when converting a variable that was imported from the environment
  to integer to check if it is purely numeric

 11 files changed, 200 insertions(+), 70 deletions(-)


[ Impact ]
- the evaluate-region editing command is still broken
  (I can’t think of any other obvious codepath that triggers
  the lexer issue)
- environment variables COLUMNS, LINES, SECONDS, etc. can be
  used for shell DoS, possibly command execution
- memory leaks (minor, most “lost” blocks are cleaned up when
  a scope is left… if it is even left)
- files with \x02 in them possibly can’t be tab-completed
- pathnames with tildes in them tabcomplete to user homedirs
  instead; same with pattern expansion: 'echo \~bar/*'
- variables with {} or ~ in their values are escaped without
  quoting these characters making them not safe for re-entry
  into the shell (possibly changing their value); this also
  affects internal pass-escaped-values-around like typeset -f
  or even $(…)

[ Tests ]
- the “taint” thing has an automated test
- I wrote excessive code to test the changes to the character
  classes, this is about 600 LoC, which I omitted here to keep
  the diff small, as it’d be called manually while developing
  anyway
- most nōn-interactive functionality is covered by the testsuite
  (which is also run via autopkgtests)
- I tested the escaping and tab-completion things as well as
  the lexer / evaluate-region thing manually

I also did a full rebuild of MirBSD itself with the changes
applied which exercises the shell quite a bit.

[ Risks ]
- the “taint” thing changes the way some scripts may operate,
  but the previous behaviour was unsafe and other shells are
  the same; mksh is de-facto leaf in Debian (I looked at shunit2
  for the previous unblock, and it’ll basically ignore mksh);
  I guess this is medium risk for mksh (which is why this should
  simmer in sid for a while first, though my users tend to find
  bugs rather quickly) but low risk for Debian or the release
  overall considering its virtually-leafness
- the escaping changes only add backslashes that are safe (extra
  backslashes in those cases can’t hurt anyway, eg. = is already
  almost needlessly escaped)
- the memory leaks are low-risk as “obvious”
- same for the lexer change

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]
(let it simmer in sid for a few days first)

unblock mksh/59c-6
diff -Nru mksh-59c/debian/changelog mksh-59c/debian/changelog
--- mksh-59c/debian/changelog   2021-03-13 19:09:48.000000000 +0100
+++ mksh-59c/debian/changelog   2021-05-03 03:26:28.000000000 +0200
@@ -1,3 +1,28 @@
+mksh (59c-6) unstable; urgency=medium
+
+  * Clear “taint” on most actions mutating a variable
+
+ -- Thorsten Glaser <t...@mirbsd.de>  Mon, 03 May 2021 03:26:28 +0200
+
+mksh (59c-5) unstable; urgency=medium
+
+  * Apply targeted fixes, intended for bullseye:
+    - [tg] Plug some memory leaks
+    - [tg] Harden conversion of imported variables to integer, like
+      Perl “taint”: imported variables will now lose the value when
+      converting to integer but they are not purely numeric
+      (CVE-2019-14868 was a similar issue in AT&T ksh); honour
+      -o posix for leading-zero as octal, though (but continue not
+      when importing array indicēs)
+    - [tg] Fix lexer token state corruption when reading new input;
+      makes evaluate-region editing command actually useful
+    - [tg] Fix proper escaping/quoting and tab completion for tilde,
+      curly braces and \x02, either escaped or not
+    Note: patches are reduced to minimum change for bullseye
+  * Rebuild for outdated Built-Using
+
+ -- Thorsten Glaser <t...@mirbsd.de>  Sun, 02 May 2021 23:52:52 +0200
+
 mksh (59c-4) unstable; urgency=low
 
   * Update to upstream CVS HEAD
diff -pruN mksh_59c-4/check.t mksh_59c-6/check.t
--- mksh_59c-4/check.t  2021-05-03 00:29:50.000000000 +0200
+++ mksh_59c-6/check.t  2021-05-03 03:57:57.000000000 +0200
@@ -31,7 +31,7 @@
 # (2013/12/02 20:39:44) 
http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
 
 expected-stdout:
-       KSH R59 2021/03/13
+       KSH R59 2021/05/03
 description:
        Check base version of full shell
 stdin:
@@ -13230,6 +13230,41 @@ expected-stdout:
        2=\x7Cfoo-e \x4B
        3=\x7Cfoo-e \x4B
 ---
+name: env-intvars
+description:
+       Check that importing integers fails except for numbers
+stdin:
+       unset foo bar
+       print 1 $foo , $(typeset -p bar) .
+       print 2 $(foo=123 "$__progname" -c 'integer foo; print -- $foo' 2>&1) , 
\
+           $(env 'bar[123]=baz' "$__progname" -c 'typeset -p bar') .
+       print 3 $(foo='abc[$(echo >&2 fowled)0]' "$__progname" -c 'integer foo; 
print -- $foo' 2>&1) , \
+           $(env 'bar[$(echo >&2 fowled)0]=baz' "$__progname" -c 'typeset -p 
bar') .
+       print 4 $(foo=0123 "$__progname" +o posix -c 'integer foo; print -- 
$foo' 2>&1) , \
+           $(env 'bar[0123]=baz' "$__progname" +o posix -c 'typeset -p bar') .
+       # ksh93 does not do this:
+       print 5 $(foo=0123 "$__progname" -o posix -c 'integer foo; print -- 
$foo' 2>&1) .
+       # at import time FPOSIX is not yet set
+       print 6 $(foo=0x123 "$__progname" -c 'integer foo; print -- $foo' 2>&1) 
, \
+           $(env 'bar[0x123]=baz' "$__progname" -c 'typeset -p bar') .
+       print 7 $(foo=12#123 "$__progname" -c 'integer foo; print -- $foo' 
2>&1) , \
+           $(env 'bar[12#123]=baz' "$__progname" -c 'typeset -p bar') .
+       print 8 $(foo=1+1 "$__progname" -c 'integer foo; print -- $foo' 2>&1) , 
\
+           $(env 'bar[1+1]=baz' "$__progname" -c 'typeset -p bar') .
+       print 9 $(a=1 b=2 c=a "$__progname" -c 'typeset -p c; c=b; typeset -p 
c; integer c; typeset -p c') .
+       print 0 $(a=1 b=2 c=a "$__progname" -c 'typeset -p c;      typeset -p 
c; integer c; typeset -p c') .
+expected-stdout:
+       1 , .
+       2 123 , set -A bar typeset -x bar[123]=baz .
+       3 0 , .
+       4 123 , set -A bar typeset -x bar[123]=baz .
+       5 8#123 .
+       6 16#123 , .
+       7 12#123 , .
+       8 0 , .
+       9 typeset -x c=a typeset -x c=b typeset -i -x c=2 .
+       0 typeset -x c=a typeset -x c=a typeset -i -x c=0 .
+---
 name: utilities-getopts-1
 description:
        getopts sets OPTIND correctly for unparsed option
diff -pruN mksh_59c-4/edit.c mksh_59c-6/edit.c
--- mksh_59c-4/edit.c   2021-05-03 00:29:50.000000000 +0200
+++ mksh_59c-6/edit.c   2021-05-03 03:57:57.000000000 +0200
@@ -74,6 +74,7 @@ static struct {
 #define XCF_COMMAND_FILE (XCF_COMMAND | XCF_FILE)
 #define XCF_IS_COMMAND BIT(3)  /* return flag: is command */
 #define XCF_IS_NOSPACE BIT(4)  /* return flag: do not append a space */
+#define XCF_IS_HOMEDIR BIT(5)  /* return flag: tilde needs slash */
 
 static char editmode;
 static int xx_cols;                    /* for Emacs mode */
@@ -92,7 +93,7 @@ static int x_do_comment(char *, ssize_t,
 static void x_print_expansions(int, char * const *, bool);
 static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
 static size_t x_longest_prefix(int, char * const *);
-static void x_glob_hlp_add_qchar(char *);
+static char *x_glob_hlp_add_qchar(char *);
 static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
 static size_t x_basename(const char *, const char *);
 static void x_free_words(int, char **);
@@ -297,21 +298,32 @@ x_print_expansions(int nwords, char * co
 
 /*
  * Convert backslash-escaped string to QCHAR-escaped
- * string useful for globbing; loses QCHAR unless it
- * can squeeze in, eg. by previous loss of backslash
+ * string useful for globbing
  */
-static void
+static char *
 x_glob_hlp_add_qchar(char *cp)
 {
-       char ch, *dp = cp;
+       char ch, *dp;
        bool escaping = false;
+       XString xs;
+       size_t n;
+
+       if (memchr(cp, QCHAR, (n = strlen(cp)))) {
+               Xinit(xs, dp, n, ATEMP);
+       } else {
+               xs.len = n + 1;
+               xs.areap = NULL; /* won’t be used */
+               xs.beg = dp = cp;
+               xs.end = xs.beg + xs.len;
+       }
 
        while ((ch = *cp++)) {
                if (ch == '\\' && !escaping) {
                        escaping = true;
                        continue;
                }
-               if (escaping || (ch == QCHAR && (cp - dp) > 1)) {
+               XcheckN(xs, dp, 2);
+               if (escaping || ch == QCHAR) {
                        /*
                         * empirically made list of chars to escape
                         * for globbing as well as QCHAR itself
@@ -324,6 +336,7 @@ x_glob_hlp_add_qchar(char *cp)
                        case ORD('['):
                        case ORD('\\'):
                        case ORD('`'):
+                       case ORD('~'):
                                *dp++ = QCHAR;
                                break;
                        }
@@ -332,6 +345,7 @@ x_glob_hlp_add_qchar(char *cp)
                *dp++ = ch;
        }
        *dp = '\0';
+       return (Xstring(xs, dp));
 }
 
 /*
@@ -367,8 +381,12 @@ x_glob_hlp_tilde_and_rem_qchar(char *s,
        }
 
        /* ... convert it from backslash-escaped via QCHAR-escaped... */
-       if (magic_flag)
-               x_glob_hlp_add_qchar(s);
+       if (magic_flag) {
+               cp = x_glob_hlp_add_qchar(s);
+               if (cp != s)
+                       afree(s, ATEMP);
+               s = cp;
+       }
        /* ... to unescaped, for comparison with the matches */
        cp = dp = s;
 
@@ -391,25 +409,26 @@ x_glob_hlp_tilde_and_rem_qchar(char *s,
 static int
 x_file_glob(int *flagsp, char *toglob, char ***wordsp)
 {
-       char **words, *cp;
+       char **words, *cp, *qglob;
        int nwords;
        XPtrV w;
        struct source *s, *sold;
 
        /* remove all escaping backward slashes */
-       x_glob_hlp_add_qchar(toglob);
+       qglob = x_glob_hlp_add_qchar(toglob);
 
        /*
         * Convert "foo*" (toglob) to an array of strings (words)
         */
        sold = source;
        s = pushs(SWSTR, ATEMP);
-       s->start = s->str = toglob;
+       s->start = s->str = qglob;
        source = s;
        if (yylex(ONEWORD | LQCHAR) != LWORD) {
                source = sold;
                internal_warningf(Tfg_badsubst);
-               return (0);
+               nwords = 0;
+               goto out;
        }
        source = sold;
        afree(s, ATEMP);
@@ -434,7 +453,7 @@ x_file_glob(int *flagsp, char *toglob, c
                struct stat statb;
 
                /* Expand any tilde and drop all QCHAR for comparison */
-               toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false);
+               qglob = x_glob_hlp_tilde_and_rem_qchar(qglob, false);
 
                /*
                 * Check if globbing failed (returned glob pattern),
@@ -444,7 +463,7 @@ x_file_glob(int *flagsp, char *toglob, c
                 * to glob something which evaluated to an empty
                 * string (e.g., "$FOO" when there is no FOO, etc).
                 */
-               if ((strcmp(words[0], toglob) == 0 &&
+               if ((strcmp(words[0], qglob) == 0 &&
                    stat(words[0], &statb) < 0) ||
                    words[0][0] == '\0') {
                        x_free_words(nwords, words);
@@ -456,6 +475,9 @@ x_file_glob(int *flagsp, char *toglob, c
        if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
                x_free_words(nwords, words);
 
+ out:
+       if (qglob != toglob)
+               afree(qglob, ATEMP);
        return (nwords);
 }
 
@@ -671,7 +693,7 @@ x_cf_glob(int *flagsp, const char *buf,
 
                if (*toglob == '~' && /* not vdirsep */ !vstrchr(toglob, '/')) {
                        /* neither for '~foo' (but '~foo/bar') */
-                       *flagsp |= XCF_IS_NOSPACE;
+                       *flagsp |= XCF_IS_HOMEDIR;
                        goto dont_add_glob;
                }
 
@@ -2884,11 +2906,13 @@ do_complete(
        x_adjust();
        /*
         * append a space if this is a single non-directory match
-        * and not a parameter or homedir substitution
+        * and not a parameter substitution, slash for homedir
         */
-       if (nwords == 1 && !mksh_cdirsep(words[0][nlen - 1]) &&
-           !(flags & XCF_IS_NOSPACE)) {
-               x_ins(T1space);
+       if (nwords == 1 && !mksh_cdirsep(words[0][nlen - 1])) {
+               if (flags & XCF_IS_HOMEDIR)
+                       x_ins("/");
+               else if (!(flags & XCF_IS_NOSPACE))
+                       x_ins(T1space);
        }
 
        x_free_words(nwords, words);
@@ -5536,11 +5560,14 @@ complete_word(int cmd, int count)
 
                /*
                 * append a space if this is a non-directory match
-                * and not a parameter or homedir substitution
+                * and not a parameter substitution, slash for homedir
                 */
-               if (match_len > 0 && !mksh_cdirsep(match[match_len - 1]) &&
-                   !(flags & XCF_IS_NOSPACE))
-                       rval = putbuf(T1space, 1, false);
+               if (match_len > 0 && !mksh_cdirsep(match[match_len - 1])) {
+                       if (flags & XCF_IS_HOMEDIR)
+                               rval = putbuf("/", 1, false);
+                       else if (!(flags & XCF_IS_NOSPACE))
+                               rval = putbuf(T1space, 1, false);
+               }
        }
        x_free_words(nwords, words);
 
diff -pruN mksh_59c-4/eval.c mksh_59c-6/eval.c
--- mksh_59c-4/eval.c   2020-05-05 23:34:54.000000000 +0200
+++ mksh_59c-6/eval.c   2021-05-03 03:57:57.000000000 +0200
@@ -1300,7 +1300,7 @@ varsub(Expand *xp, const char *sp, const
                        if (sc & 2) {
                                stype = 0;
                                XPinit(wv, 32);
-                               vp = global(arrayname(sp));
+                               vp = arraybase(sp);
                                do {
                                        if (vp->flag & ISSET)
                                                XPput(wv, shf_smprintf(Tf_lu,
@@ -1347,7 +1347,7 @@ varsub(Expand *xp, const char *sp, const
                case ORD('#'):
                          switch (sc & 3) {
                        case 3:
-                               vp = global(arrayname(sp));
+                               vp = arraybase(sp);
                                if (vp->flag & (ISSET|ARRAY))
                                        zero_ok = true;
                                sc = 0;
@@ -1458,7 +1458,7 @@ varsub(Expand *xp, const char *sp, const
                /* do what we can */
                if (sc & 2) {
                        XPinit(wv, 32);
-                       vp = global(arrayname(sp));
+                       vp = arraybase(sp);
                        do {
                                if (vp->flag & ISSET)
                                        XPput(wv, str_val(vp));
diff -pruN mksh_59c-4/misc.c mksh_59c-6/misc.c
--- mksh_59c-4/misc.c   2021-05-03 00:29:50.000000000 +0200
+++ mksh_59c-6/misc.c   2021-05-03 03:57:57.000000000 +0200
@@ -2398,6 +2398,7 @@ chvt(const Getopt *go)
                        errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
                }
        }
+       afree(cp, ATEMP);
        if (go->optarg[0] != '!') {
                switch (fork()) {
                case -1:
diff -pruN mksh_59c-4/mksh.1 mksh_59c-6/mksh.1
--- mksh_59c-4/mksh.1   2021-05-03 00:29:50.000000000 +0200
+++ mksh_59c-6/mksh.1   2021-05-03 03:57:57.000000000 +0200
@@ -84,7 +84,7 @@
 .\" with -mandoc, it might implement .Mx itself, but we want to
 .\" use our own definition. And .Dd must come *first*, always.
 .\"
-.Dd March 13, 2021
+.Dd $Mdocdate: May 2 2021 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -6324,7 +6324,7 @@ replaces the inserted text string with t
 .Pp
 The tab completion escapes characters the same way as the following code:
 .Bd -literal
-print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`\e{\-\e}${IFS\-$\*(aq 
\et\en\*(aq}]/\e\e$KSH_MATCH}"
+print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`\e{\-\e\*(TI${IFS\-$\*(aq 
\et\en\*(aq}]/\e\e$KSH_MATCH}"
 .Ed
 .Ss Vi editing mode
 .Em Note:
diff -pruN mksh_59c-4/mksh.faq mksh_59c-6/mksh.faq
--- mksh_59c-4/mksh.faq 2021-05-03 00:29:50.000000000 +0200
+++ mksh_59c-6/mksh.faq 2021-05-03 03:57:57.000000000 +0200
@@ -1,4 +1,4 @@
-RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.17+locale-tracking 2021/03/11 
14:16:08 tg Exp $
+RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.19+locale-tracking 2021/05/02 
08:26:03 tg Exp $
 ToC: spelling
 Title: How do you spell <tt>mksh</tt>? How do you pronounce it?
 
@@ -412,7 +412,7 @@ Title: What about programmable tab compl
 The shell itself provides static deterministic tab completion.
 However, you can use hooks like reprogramming the Tab key to a
 command line editor macro, and using the <tt>evaluate-region</tt>
-editor command (modulo a bugfix) together with <tt>quote-region</tt> and shell 
functions to
+editor command together with <tt>quote-region</tt> and shell functions to
 implement a programmable completion engine. Multiple people have
 been considering doing so in our IRC channel; we’ll hyperlink to
 these engines when they are available.
@@ -612,6 +612,20 @@ Title: Didn’t there used to be a sleep
  In that case, GNU coreutils’ sleep, which is built on older syscalls,
  may work if the copyleft licence isn’t a showstopper for you.</p>
 ----
+ToC: arith-import
+Title: Some integer variables are 0?
+
+<p class="boxhead">To mitigate potential exploits, variables imported
+ from the environment are not trusted in arithmetic context; that is…</p>
+<div class="boxtext">
+ <pre>
+       foo=1+1 mksh -c 'integer foo; print $foo'
+       foo=1+1 mksh -c 'integer foo=$foo; print $foo'
+ </pre>
+</div><p class="boxfoot">… will lose the value in the first line,
+ while the second line explicitly “untaints”, to use a Perl term,
+ the content. Purely numeric values will pass, though.</p>
+----
 ToC: string-concat
 Title: “+=” behaves differently from other shells
 
diff -pruN mksh_59c-4/sh.h mksh_59c-6/sh.h
--- mksh_59c-4/sh.h     2021-05-03 00:29:50.000000000 +0200
+++ mksh_59c-6/sh.h     2021-05-03 03:57:57.000000000 +0200
@@ -195,7 +195,7 @@
 #ifdef EXTERN
 __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.906 2021/01/24 19:37:31 tg Exp $");
 #endif
-#define MKSH_VERSION "R59 2021/03/13"
+#define MKSH_VERSION "R59 2021/05/03"
 
 /* arithmetic types: C implementation */
 #if !HAVE_CAN_INTTYPES
@@ -1411,9 +1411,9 @@ EXTERN bool really_exit;
 #define CiOCTAL        BIT(5)  /* 0‥7                          */
 #define CiQCL  BIT(6)  /* &();|                        */
 #define CiALIAS        BIT(7)  /* !,.@                         */
-#define CiQCX  BIT(8)  /* *[\\                         */
+#define CiQCX  BIT(8)  /* *[\\~                        */
 #define CiVAR1 BIT(9)  /* !*@                          */
-#define CiQCM  BIT(10) /* /^~                          */
+#define CiQCM  BIT(10) /* /^                           */
 #define CiDIGIT        BIT(11) /* 89                           */
 #define CiQC   BIT(12) /* "'                           */
 #define CiSPX  BIT(13) /* \x0B\x0C                     */
@@ -1470,7 +1470,7 @@ EXTERN char ifs0;
 #define C_EDCMD        (CiGRAVE | CiQCL)
 /* \x09\x0A\x20"&'():;<=>`|    editor non-word characters */
 #define C_EDNWC        (CiANGLE | CiCOLON | CiEQUAL | CiGRAVE | CiNL | CiQC | 
CiQCL | CiSP | CiTAB)
-/* "#$&'()*:;<=>?[\\`{|}       editor quotes for tab completion */
+/* "#$&'()*:;<=>?[\\`{|}~      editor quotes for tab completion */
 #define C_EDQ  (CiANGLE | CiCOLON | CiCURLY | CiEQUAL | CiGRAVE | CiHASH | 
CiQC | CiQCL | CiQCX | CiQUEST | CiSS)
 /* !‥~                 POSIX graphical (alphanumerical plus punctuation) */
 #define C_GRAPH        (C_PUNCT | CiDIGIT | CiLOWER | CiOCTAL | CiUPPER)
@@ -1494,8 +1494,8 @@ EXTERN char ifs0;
 #define C_PRINT        (C_GRAPH | CiSP)
 /* !"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~   POSIX punctuation */
 #define C_PUNCT        (CiALIAS | CiANGLE | CiBRACK | CiCOLON | CiCURLY | 
CiEQUAL | CiGRAVE | CiHASH | CiMINUS | CiPERCT | CiPLUS | CiQC | CiQCL | CiQCM 
| CiQCX | CiQUEST | CiSS | CiUNDER)
-/* \x09\x0A"#$&'()*;<=>?[\\]`| characters requiring quoting, minus space */
-#define C_QUOTE        (CiANGLE | CiBRACK | CiEQUAL | CiGRAVE | CiHASH | CiNL 
| CiQC | CiQCL | CiQCX | CiQUEST | CiSS | CiTAB)
+/* \x09\x0A"#$&'()*;<=>?[\\]`{|}~      characters requiring quoting, minus 
space */
+#define C_QUOTE        (CiANGLE | CiBRACK | CiCURLY | CiEQUAL | CiGRAVE | 
CiHASH | CiNL | CiQC | CiQCL | CiQCX | CiQUEST | CiSS | CiTAB)
 /* 0‥9A‥Fa‥f           hexadecimal digit */
 #define C_SEDEC        (CiDIGIT | CiHEXLT | CiOCTAL)
 /* \x09‥\x0D\x20       POSIX space class */
@@ -2749,7 +2749,7 @@ struct tbl *arraysearch(struct tbl *, ui
 char **makenv(void);
 void change_winsz(void);
 size_t array_ref_len(const char *) MKSH_A_PURE;
-char *arrayname(const char *);
+struct tbl *arraybase(const char *);
 mksh_uari_t set_array(const char *, bool, const char **);
 uint32_t hash(const void *) MKSH_A_PURE;
 uint32_t chvt_rndsetup(const void *, size_t) MKSH_A_PURE;
diff -pruN mksh_59c-4/shf.c mksh_59c-6/shf.c
--- mksh_59c-4/shf.c    2020-06-22 19:11:30.000000000 +0200
+++ mksh_59c-6/shf.c    2021-05-03 03:57:57.000000000 +0200
@@ -1206,7 +1206,7 @@ const uint32_t tpl_ctypes[128] = {
        CiLOWER,        CiLOWER,        CiLOWER,        CiLOWER,
        CiLOWER,        CiLOWER,        CiLOWER,        CiLOWER,
        CiLOWER,        CiLOWER,        CiLOWER,        CiCURLY,
-       CiQCL,          CiCURLY,        CiQCM,          CiCNTRL
+       CiQCL,          CiCURLY,        CiQCX,          CiCNTRL
 };
 
 void
diff -pruN mksh_59c-4/syn.c mksh_59c-6/syn.c
--- mksh_59c-4/syn.c    2020-10-31 02:22:25.000000000 +0100
+++ mksh_59c-6/syn.c    2021-05-03 03:57:57.000000000 +0200
@@ -74,8 +74,8 @@ static int symbol;                    /* yylex value */
 
 #define REJECT         (reject = true)
 #define ACCEPT         (reject = false)
-#define token(cf)      ((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
-#define tpeek(cf)      ((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
+#define token(cf)      ((reject ? 0 : (symbol = yylex(cf))), ACCEPT, symbol)
+#define tpeek(cf)      ((reject ? 0 : (symbol = yylex(cf))), REJECT, symbol)
 #define musthave(c,cf) do {                                    \
        if ((unsigned int)token(cf) != (unsigned int)(c))       \
                syntaxerr(NULL);                                \
diff -pruN mksh_59c-4/var.c mksh_59c-6/var.c
--- mksh_59c-4/var.c    2020-06-22 19:11:30.000000000 +0200
+++ mksh_59c-6/var.c    2021-05-03 03:57:57.000000000 +0200
@@ -3,7 +3,7 @@
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
  *              2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
- *              2019
+ *              2019, 2021
  *     mirabilos <m...@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -29,7 +29,7 @@
 #include <sys/sysctl.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.237 2020/06/22 17:11:03 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.237+deb 2020/06/22 17:11:03 tg Exp $");
 
 /*-
  * Variables
@@ -57,6 +57,7 @@ static void getspec(struct tbl *);
 static void setspec(struct tbl *);
 static void unsetspec(struct tbl *, bool);
 static int getint(struct tbl *, mksh_ari_u *, bool);
+static int getnum(const char *, mksh_ari_u *, bool, bool);
 static const char *array_index_calc(const char *, bool *, uint32_t *);
 static struct tbl *vtypeset(int *, const char *, uint32_t, uint32_t, int, int);
 
@@ -494,6 +495,7 @@ setstr(struct tbl *vq, const char *s, in
                        vq->flag |= ALLOC;
                        vq->type = 0;
                }
+               vq->flag &= ~IMPORT;
                afree(salloc, ATEMP);
        } else {
                /* integer dest */
@@ -527,10 +529,6 @@ setint(struct tbl *vq, mksh_ari_t n)
 static int
 getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
 {
-       mksh_uari_t c, num = 0, base = 10;
-       const char *s;
-       bool have_base = false, neg = false;
-
        if (vp->flag & SPECIAL)
                getspec(vp);
        /* XXX is it possible for ISSET to be set and val.s to be NULL? */
@@ -540,7 +538,15 @@ getint(struct tbl *vp, mksh_ari_u *nump,
                nump->i = vp->val.i;
                return (vp->type);
        }
-       s = vp->val.s + vp->type;
+       return (getnum(vp->val.s + vp->type, nump, arith,
+           Flag(FPOSIX) && !(vp->flag & ZEROFIL)));
+}
+
+static int
+getnum(const char *s, mksh_ari_u *nump, bool arith, bool psxoctal)
+{
+       mksh_uari_t c, num = 0, base = 10;
+       bool have_base = false, neg = false;
 
        do {
                c = (unsigned char)*s++;
@@ -561,8 +567,7 @@ getint(struct tbl *vp, mksh_ari_u *nump,
                        base = 16;
                        ++s;
                        goto getint_c_style_base;
-               } else if (Flag(FPOSIX) && ctype(s[0], C_DIGIT) &&
-                   !(vp->flag & ZEROFIL)) {
+               } else if (psxoctal && ctype(s[0], C_DIGIT)) {
                        /* interpret as octal (deprecated) */
                        base = 8;
  getint_c_style_base:
@@ -641,10 +646,11 @@ setint_v(struct tbl *vq, struct tbl *vp,
 void
 setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
 {
-       if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
-               vq->flag &= ~ALLOC;
+       if (!(vq->flag & INTEGER)) {
+               if (vq->flag & ALLOC)
+                       afree(vq->val.s, vq->areap);
+               vq->flag &= ~(ALLOC | IMPORT);
                vq->type = 0;
-               afree(vq->val.s, vq->areap);
        }
        vq->val.i = num;
        if (newbase != 0)
@@ -938,7 +944,7 @@ vtypeset(int *ep, const char *var, uint3
 
        set &= ~(LOCAL|LOCAL_COPY);
 
-       vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
+       vpbase = (vp->flag & ARRAY) ? arraybase(tvar) : vp;
 
        /*
         * only allow export and readonly flag to be set; AT&T ksh
@@ -961,7 +967,7 @@ vtypeset(int *ep, const char *var, uint3
                 */
                for (t = vpbase; t; t = t->u.array) {
                        bool fake_assign;
-                       char *s = NULL;
+                       const char *s = NULL;
                        char *free_me = NULL;
 
                        fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
@@ -983,13 +989,27 @@ vtypeset(int *ep, const char *var, uint3
                                t->type = 0;
                                t->flag &= ~ALLOC;
                        }
-                       t->flag = (t->flag | set) & ~clr;
                        /*
                         * Don't change base if assignment is to be
                         * done, in case assignment fails.
                         */
-                       if ((set & INTEGER) && base > 0 && (!val || t != vp))
-                               t->type = base;
+                       if (set & INTEGER) {
+                               if (base > 0 && (!val || t != vp))
+                                       t->type = base;
+                               /*
+                                * Do not permit content from the
+                                * environment to e.g. execute commands.
+                                */
+                               if ((t->flag & IMPORT) && fake_assign) {
+                                       mksh_ari_u num;
+
+                                       if (getnum(s, &num, true,
+                                           tobool(Flag(FPOSIX))) == -1)
+                                               s = "0";
+                                       clr |= IMPORT;
+                               }
+                       }
+                       t->flag = (t->flag | set) & ~clr;
                        if (set & (LJUST|RJUST|ZEROFIL))
                                t->u2.field = field;
                        if (fake_assign) {
@@ -1019,7 +1039,7 @@ vtypeset(int *ep, const char *var, uint3
 
        if (vappend) {
                size_t tlen;
-               if ((vp->flag & 
(ISSET|ALLOC|SPECIAL|INTEGER|UCASEV_AL|LCASEV|LJUST|RJUST)) != (ISSET|ALLOC)) {
+               if ((vp->flag & 
(ISSET|ALLOC|SPECIAL|INTEGER|UCASEV_AL|LCASEV|LJUST|RJUST|IMPORT)) != 
(ISSET|ALLOC)) {
                        /* cannot special-case this */
                        strdup2x(tvar, str_val(vp), val);
                        val = tvar;
@@ -1038,9 +1058,11 @@ vtypeset(int *ep, const char *var, uint3
                        /* done after assignment to override default */
                        if (base > 0)
                                vp->type = base;
-               } else
+               } else {
                        /* setstr can't fail (readonly check already done) */
                        setstr(vp, val, KSH_RETURN_ERROR | 0x4);
+                       vp->flag |= (set & IMPORT);
+               }
 
                /* came here from vappend? need to free temp val */
                if (vappend)
@@ -1610,19 +1632,25 @@ array_ref_len(const char *cp)
 }
 
 /*
- * Make a copy of the base of an array name
+ * same effect as global(copy of the base of an array name)
  */
-char *
-arrayname(const char *str)
+struct tbl *
+arraybase(const char *str)
 {
        const char *p;
-       char *rv;
-
-       if (!(p = cstrchr(str, '[')))
-               /* Shouldn't happen, but why worry? */
-               strdupx(rv, str, ATEMP);
-       else
-               strndupx(rv, str, p - str, ATEMP);
+       char *s, sbuf[32];
+       size_t n;
+       struct tbl *rv;
+
+       n = strlen(str);
+       if ((p = memchr(str, '[', n)))
+               n = p - str;
+       s = n < sizeof(sbuf) ? sbuf : alloc(n + 1, ATEMP);
+       memcpy(s, str, n);
+       s[n] = '\0';
+       rv = global(s);
+       if (s != sbuf)
+               afree(s, ATEMP);
 
        return (rv);
 }

Reply via email to