summaryrefslogtreecommitdiffstats
path: root/meta/recipes-support/re2c/re2c/CVE-2018-21232-3.patch
blob: f942e21cba558e95d768481e4fa01295a6c86b00 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
From 4d9c809355b574f2a58eac119f5e076c48e4d1e2 Mon Sep 17 00:00:00 2001
From: Ulya Trofimovich <skvadrik@gmail.com>
Date: Thu, 23 Apr 2020 22:16:51 +0100
Subject: [PATCH] Rewrite recursion into iteration (nullable RE).

This is to avoid stack overflow on large RE (especially on instrumented
builds that have larger stack frames, like AddressSanitizer).

Partial fix for #219 "overflow-1.re test fails on system with small stack".

Upstream-Status: Backport:
https://github.com/skvadrik/re2c/commit/4d9c809355b574f2a58eac119f5e076c48e4d1e2

CVE: CVE-2018-21232

Signed-off-by: Davide Gardenal <davide.gardenal@huawei.com>
---
diff --git a/src/re/nullable.cc b/src/re/nullable.cc
--- a/src/re/nullable.cc	(revision e58939b34bb4c37cd990f82dc286f21cb405743e)
+++ b/src/re/nullable.cc	(date 1647253886226)
@@ -9,43 +9,100 @@
 #include "src/re/tag.h"
 
 namespace re2c {
+    namespace {
+
+        struct StackItem {
+            const RE *re;   // current sub-RE
+            uint8_t   succ; // index of the next sucessor to be visited
+        };
 
-static bool nullable(const RESpec &spec, const RE *re, bool &trail)
-{
-	if (trail) return true;
+        static bool nullable(const RESpec &spec, std::vector<StackItem> &stack, const RE *re0)
+        {
+            // the "nullable" status of the last sub-RE visited by DFS
+            bool null = false;
 
-	switch (re->type) {
-		case RE::NIL: return true;
-		case RE::SYM: return false;
-		case RE::ITER:
-			return nullable(spec, re->iter.re, trail);
-		case RE::TAG:
-			trail |= trailing(spec.tags[re->tag.idx]);
-			return true;
-		case RE::ALT:
-			return nullable(spec, re->alt.re1, trail)
-				|| nullable(spec, re->alt.re2, trail);
-		case RE::CAT:
-			return nullable(spec, re->cat.re1, trail)
-				&& nullable(spec, re->cat.re2, trail);
-	}
-	return false; /* unreachable */
-}
+            const StackItem i0 = {re0, 0};
+            stack.push_back(i0);
+
+            while (!stack.empty()) {
+                const StackItem i = stack.back();
+                stack.pop_back();
+
+                const RE *re = i.re;
+                if (re->type == RE::NIL) {
+                    null = true;
+                }
+                else if (re->type == RE::SYM) {
+                    null = false;
+                }
+                else if (re->type == RE::TAG) {
+                    null = true;
 
-/*
- * warn about rules that match empty string
- * (including rules with nonempty trailing context)
- * false positives on partially self-shadowed rules like [^]?
- */
-void warn_nullable(const RESpec &spec, const std::string &cond)
-{
-	const size_t nre = spec.res.size();
-	for (size_t i = 0; i < nre; ++i) {
-		bool trail = false;
-		if (nullable(spec, spec.res[i], trail)) {
-			spec.warn.match_empty_string(spec.rules[i].code->fline, cond);
-		}
-	}
-}
+                    // Trailing context is always in top-level concatenation, and sub-RE
+                    // are visited from left to right. Since we are here, sub-RE to the
+                    // left of the trailing context is nullable (otherwise we would not
+                    // recurse into the right sub-RE), therefore the whole RE is nullable.
+                    if (trailing(spec.tags[re->tag.idx])) {
+                        //DASSERT(stack.size() == 1 && stack.back().re->type == RE::CAT);
+                        stack.pop_back();
+                        break;
+                    }
+                }
+                else if (re->type == RE::ALT) {
+                    if (i.succ == 0) {
+                        // recurse into the left sub-RE
+                        StackItem k = {re, 1};
+                        stack.push_back(k);
+                        StackItem j = {re->alt.re1, 0};
+                        stack.push_back(j);
+                    }
+                    else if (!null) {
+                        // if the left sub-RE is nullable, so is alternative, so stop
+                        // recursion; otherwise recurse into the right sub-RE
+                        StackItem j = {re->alt.re2, 0};
+                        stack.push_back(j);
+                    }
+                }
+                else if (re->type == RE::CAT) {
+                    if (i.succ == 0) {
+                        // recurse into the left sub-RE
+                        StackItem k = {re, 1};
+                        stack.push_back(k);
+                        StackItem j = {re->cat.re1, 0};
+                        stack.push_back(j);
+                    }
+                    else if (null) {
+                        // if the left sub-RE is not nullable, neither is concatenation,
+                        // so stop recursion; otherwise recurse into the right sub-RE
+                        StackItem j = {re->cat.re2, 0};
+                        stack.push_back(j);
+                    }
+                }
+                else if (re->type == RE::ITER) {
+                    // iteration is nullable if the sub-RE is nullable
+                    // (zero repetitions is represented with alternative)
+                    StackItem j = {re->iter.re, 0};
+                    stack.push_back(j);
+                }
+            }
+
+            //DASSERT(stack.empty());
+            return null;
+        }
+
+    } // anonymous namespace
+
+// Warn about rules that match empty string (including rules with nonempty
+// trailing context). False positives on partially self-shadowed rules like [^]?
+    void warn_nullable(const RESpec &spec, const std::string &cond)
+    {
+        std::vector<StackItem> stack;
+        const size_t nre = spec.res.size();
+        for (size_t i = 0; i < nre; ++i) {
+            if (nullable(spec, stack, spec.res[i])) {
+                spec.warn.match_empty_string(spec.rules[i].code->fline, cond);
+            }
+        }
+    }
 
 } // namespace re2c