summaryrefslogtreecommitdiffstats
path: root/meta/recipes-support/re2c/re2c/CVE-2018-21232-4.patch
blob: ee8d84b1bc774217bd8e2d5809d83e250f7d27e9 (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
157
158
159
160
161
162
163
164
165
166
From 89be91f3df00657261870adbc590209fdb2bc405 Mon Sep 17 00:00:00 2001
From: Ulya Trofimovich <skvadrik@gmail.com>
Date: Thu, 23 Apr 2020 23:02:21 +0100
Subject: [PATCH] Rewrite recursion into iteration (estimation of NFA size for
 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".

Upstram-Status: Backport:
https://github.com/skvadrik/re2c/commit/89be91f3df00657261870adbc590209fdb2bc405

CVE: CVE-2018-21232

Signed-off-by: Davide Gardenal <davide.gardenal@huawei.com>
---
diff --git a/src/nfa/estimate_size.cc b/src/nfa/estimate_size.cc
--- a/src/nfa/estimate_size.cc	(revision e58939b34bb4c37cd990f82dc286f21cb405743e)
+++ b/src/nfa/estimate_size.cc	(date 1647005399735)
@@ -6,41 +6,113 @@
 #include "src/re/re.h"
 
 namespace re2c {
+namespace {
+
+struct StackItem {
+    const RE *re;   // current sub-RE
+    uint32_t  size; // size of the sub-RE (only for alternative and concatenation)
+    uint8_t   succ; // index of the next sucessor to be visited
+};
 
-static size_t estimate(const RE *re)
+static uint32_t estimate_re_size(const RE *re0, std::vector<StackItem> &stack)
 {
-	switch (re->type) {
-		case RE::NIL: return 0;
-		case RE::SYM: return 1;
-		case RE::TAG: return 1;
-		case RE::ALT:
-			return estimate(re->alt.re1)
-				+ estimate(re->alt.re2)
-				+ 1;
-		case RE::CAT:
-			return estimate(re->cat.re1)
-				+ estimate(re->cat.re2);
-		case RE::ITER: {
-			const size_t
-				iter = estimate(re->iter.re),
-				min = re->iter.min,
-				max = re->iter.max;
-			return max == AST::MANY
-				? iter * min + 1
-				: iter * max + (max - min);
-		}
-	}
-	return 0; /* unreachable */
-}
+    // the estimated size of the last sub-RE visited by DFS
+    uint32_t size = 0;
+
+    const StackItem i0 = {re0, 0, 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) {
+            size = 0;
+        }
+        else if (re->type == RE::SYM || re->type == RE::TAG) {
+            size = 1;
+        }
+        else if (re->type == RE::ALT) {
+            if (i.succ == 0) {
+                // recurse into the left sub-RE
+                StackItem k = {re, 0, 1};
+                stack.push_back(k);
+                StackItem j = {re->alt.re1, 0, 0};
+                stack.push_back(j);
+            }
+            else if (i.succ == 1) {
+                // recurse into the right sub-RE
+                StackItem k = {re, size, 2};
+                stack.push_back(k);
+                StackItem j = {re->alt.re2, 0, 0};
+                stack.push_back(j);
+            }
+            else {
+                // both sub-RE visited, recursive return
+                size = i.size // left sub-RE (saved on stack)
+                       + size    // right sub-RE (just visited by DFS)
+                       + 1;      // additional state for alternative
+            }
+        }
+        else if (re->type == RE::CAT) {
+            if (i.succ == 0) {
+                // recurse into the left sub-RE
+                StackItem k = {re, 0, 1};
+                stack.push_back(k);
+                StackItem j = {re->cat.re1, 0, 0};
+                stack.push_back(j);
+            }
+            else if (i.succ == 1) {
+                // recurse into the right sub-RE
+                StackItem k = {re, size, 2};
+                stack.push_back(k);
+                StackItem j = {re->cat.re2, 0, 0};
+                stack.push_back(j);
+            }
+            else {
+                // both sub-RE visited, recursive return
+                size = i.size // left sub-RE (saved on stack)
+                       + size;   // right sub-RE (just visited by DFS)
+            }
+        }
+        else if (re->type == RE::ITER) {
+            if (i.succ == 0) {
+                // recurse into the sub-RE
+                StackItem k = {re, 0, 1};
+                stack.push_back(k);
+                StackItem j = {re->iter.re, 0, 0};
+                stack.push_back(j);
+            }
+            else {
+                // sub-RE visited, recursive return
+                const uint32_t min = re->iter.min, max = re->iter.max;
+                size = max == AST::MANY
+                       ? size * min + 1
+                       : size * max + (max - min);
+            }
+        }
+    }
+
+    //DASSERT(stack.empty());
+    return size;
+}
+
+} // anonymous namespace
 
 size_t estimate_size(const std::vector<RE*> &res)
 {
-	const size_t nre = res.size();
-	size_t size = nre - 1;
-	for (size_t i = 0; i < nre; ++i) {
-		size += estimate(res[i]) + 1;
-	}
-	return size;
+    std::vector<StackItem> stack;
+
+    const size_t nre = res.size();
+    //DASSERT(nre > 0);
+    size_t size = nre - 1;
+
+    for (size_t i = 0; i < nre; ++i) {
+        size += estimate_re_size(res[i], stack) + 1;
+    }
+
+    return size;
 }
 
 } // namespace re2c