summaryrefslogtreecommitdiff
blob: d5c80566bafc8d0f899aa489d7b58a84a7c717ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
From c7de8b641266bac7c77942239ac659edfee9ecd2 Mon Sep 17 00:00:00 2001
From: Kurtis Rader <krader@skepticism.us>
Date: Thu, 12 Dec 2019 18:46:50 -0800
Subject: [PATCH] Harden env var imports

---
 src/cmd/ksh93/sh/arith.c        | 37 ++++++++++++++++++++++-----------
 src/cmd/ksh93/tests/subshell.sh | 23 ++++++++++++++++++++

diff --git a/src/cmd/ksh93/sh/arith.c b/src/cmd/ksh93/sh/arith.c
index 30b3067590a2..8e68cbdc868a 100644
--- a/src/cmd/ksh93/sh/arith.c
+++ b/src/cmd/ksh93/sh/arith.c
@@ -567,19 +567,32 @@ Sfdouble_t sh_strnum(Shell_t *shp, const char *str, char **ptr, int mode) {
     char *last;
 
     if (*str == 0) {
-        if (ptr) *ptr = (char *)str;
-        return 0;
-    }
-    errno = 0;
-    d = number(str, &last, shp->inarith ? 0 : 10, NULL);
-    if (*last) {
-        if (*last != '.' || last[1] != '.') {
-            d = strval(shp, str, &last, arith, mode);
-            Varsubscript = true;
+        d = 0.0;
+        last = (char *)str;
+    } else {
+        d = number(str, &last, shp->inarith ? 0 : 10, NULL);
+        if (*last && !shp->inarith && sh_isstate(shp, SH_INIT)) {
+            // This call is to handle "base#value" literals if we're importing untrusted env vars.
+            d = number(str, &last, 0, NULL);
+        }
+        if (*last) {
+            if (sh_isstate(shp, SH_INIT)) {
+                // Initializing means importing untrusted env vars. Since the string does not appear
+                // to be a recognized numeric literal give up. We can't safely call strval() since
+                // that allows arbitrary expressions which would create a security vulnerability.
+                d = 0.0;
+            } else {
+                if (*last != '.' || last[1] != '.') {
+                    d = strval(shp, str, &last, arith, mode);
+                    Varsubscript = true;
+                }
+                if (!ptr && *last && mode > 0) {
+                    errormsg(SH_DICT, ERROR_exit(1), e_lexbadchar, *last, str);
+                }
+            }
+        } else if (d == 0.0 && *str == '-') {
+            d = -0.0;
         }
-        if (!ptr && *last && mode > 0) errormsg(SH_DICT, ERROR_exit(1), e_lexbadchar, *last, str);
-    } else if (!d && *str == '-') {
-        d = -0.0;
     }
     if (ptr) *ptr = last;
     return d;
diff --git a/src/cmd/ksh93/tests/subshell.sh b/src/cmd/ksh93/tests/subshell.sh
index b63a8051ed5c..3faba475d6de 100644
--- a/src/cmd/ksh93/tests/subshell.sh
+++ b/src/cmd/ksh93/tests/subshell.sh
@@ -856,3 +856,26 @@ for exp in 65535 65536
 do    got=$($SHELL -c 'x=$(printf "%.*c" '$exp' x); print ${#x}' 2>&1)
     [[ $got == $exp ]] || log_error "large command substitution failed" "$exp" "$got"
 done
+
+# ==========
+# Verify that importing untrusted env vars does not allow evaluating arbitrary expressions but does
+# recognize all integer literals recognized by ksh.
+expect=8
+actual=$(env SHLVL='7' $SHELL -c 'echo $SHLVL')
+[[ $actual == $expect ]] || log_error "decimal int literal not recognized" "$expect" "$actual"
+
+expect=14
+actual=$(env SHLVL='013' $SHELL -c 'echo $SHLVL')
+[[ $actual == $expect ]] || log_error "leading zeros int literal not recognized" "$expect" "$actual"
+
+expect=4
+actual=$(env SHLVL='2#11' $SHELL -c 'echo $SHLVL')
+[[ $actual == $expect ]] || log_error "base#value int literal not recognized" "$expect" "$actual"
+
+expect=12
+actual=$(env SHLVL='16#B' $SHELL -c 'echo $SHLVL')
+[[ $actual == $expect ]] || log_error "base#value int literal not recognized" "$expect" "$actual"
+
+expect=1
+actual=$(env SHLVL="2#11+x[\$($bin_echo DANGER WILL ROBINSON >&2)0]" $SHELL -c 'echo $SHLVL')
+[[ $actual == $expect ]] || log_error "expression allowed on env var import" "$expect" "$actual"