1 /*
2  * Copyright (c) 2018-2020, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *  * Redistributions of source code must retain the above copyright notice,
8  *    this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *  * Neither the name of Intel Corporation nor the names of its contributors
13  *    may be used to endorse or promote products derived from this software
14  *    without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /** \file
30  * \brief Parse and build ParsedLogical::logicalTree and combInfoMap.
31  */
32 #include "logical_combination.h"
33 #include "parser/parse_error.h"
34 #include "util/container.h"
35 #include "hs_compile.h"
36 #include "allocator.h"
37 
38 #include <vector>
39 
40 using namespace std;
41 
42 namespace ue2 {
43 
getLogicalKey(u32 a)44 u32 ParsedLogical::getLogicalKey(u32 a) {
45     auto it = toLogicalKeyMap.find(a);
46     if (it == toLogicalKeyMap.end()) {
47         // get size before assigning to avoid wacky LHS shenanigans
48         u32 size = toLogicalKeyMap.size();
49         bool inserted;
50         tie(it, inserted) = toLogicalKeyMap.emplace(a, size);
51         assert(inserted);
52     }
53     DEBUG_PRINTF("%u -> lkey %u\n", it->first, it->second);
54     return it->second;
55 }
56 
getCombKey(u32 a)57 u32 ParsedLogical::getCombKey(u32 a) {
58     auto it = toCombKeyMap.find(a);
59     if (it == toCombKeyMap.end()) {
60         u32 size = toCombKeyMap.size();
61         bool inserted;
62         tie(it, inserted) = toCombKeyMap.emplace(a, size);
63         assert(inserted);
64     }
65     DEBUG_PRINTF("%u -> ckey %u\n", it->first, it->second);
66     return it->second;
67 }
68 
addRelateCKey(u32 lkey,u32 ckey)69 void ParsedLogical::addRelateCKey(u32 lkey, u32 ckey) {
70     auto it = lkey2ckeys.find(lkey);
71     if (it == lkey2ckeys.end()) {
72         bool inserted;
73         tie(it, inserted) = lkey2ckeys.emplace(lkey, set<u32>());
74         assert(inserted);
75     }
76     it->second.insert(ckey);
77     DEBUG_PRINTF("lkey %u belongs to combination key %u\n",
78                  it->first, ckey);
79 }
80 
81 #define TRY_RENUM_OP(ckey)                                                   \
82 do {                                                                         \
83     if (ckey & LOGICAL_OP_BIT) {                                             \
84         ckey = (ckey & ~LOGICAL_OP_BIT) + toLogicalKeyMap.size();            \
85     }                                                                        \
86 } while(0)
87 
logicalTreeAdd(u32 op,u32 left,u32 right)88 u32 ParsedLogical::logicalTreeAdd(u32 op, u32 left, u32 right) {
89     LogicalOp lop;
90     assert((LOGICAL_OP_BIT & (u32)logicalTree.size()) == 0);
91     lop.id = LOGICAL_OP_BIT | (u32)logicalTree.size();
92     lop.op = op;
93     lop.lo = left;
94     lop.ro = right;
95     logicalTree.push_back(lop);
96     return lop.id;
97 }
98 
combinationInfoAdd(UNUSED u32 ckey,u32 id,u32 ekey,u32 lkey_start,u32 lkey_result,u64a min_offset,u64a max_offset)99 void ParsedLogical::combinationInfoAdd(UNUSED u32 ckey, u32 id, u32 ekey,
100                                        u32 lkey_start, u32 lkey_result,
101                                        u64a min_offset, u64a max_offset) {
102     assert(ckey == combInfoMap.size());
103     CombInfo ci;
104     ci.id = id;
105     ci.ekey = ekey;
106     ci.start = lkey_start;
107     ci.result = lkey_result;
108     ci.min_offset = min_offset;
109     ci.max_offset = max_offset;
110     combInfoMap.push_back(ci);
111 
112     DEBUG_PRINTF("ckey %u (id %u) -> lkey %u..%u, ekey=0x%x\n", ckey, ci.id,
113                  ci.start, ci.result, ci.ekey);
114 }
115 
validateSubIDs(const unsigned * ids,const char * const * expressions,const unsigned * flags,unsigned elements)116 void ParsedLogical::validateSubIDs(const unsigned *ids,
117                                    const char *const *expressions,
118                                    const unsigned *flags,
119                                    unsigned elements) {
120     for (const auto &it : toLogicalKeyMap) {
121         bool unknown = true;
122         u32 i = 0;
123         for (i = 0; i < elements; i++) {
124             if ((ids ? ids[i] : 0) == it.first) {
125                 unknown = false;
126                 break;
127             }
128         }
129         if (unknown) {
130             throw CompileError("Unknown sub-expression id.");
131         }
132         if (contains(toCombKeyMap, it.first)) {
133             throw CompileError("Have combination of combination.");
134         }
135         if (flags && (flags[i] & HS_FLAG_SOM_LEFTMOST)) {
136             throw CompileError("Have SOM flag in sub-expression.");
137         }
138         if (flags && (flags[i] & HS_FLAG_PREFILTER)) {
139             throw CompileError("Have PREFILTER flag in sub-expression.");
140         }
141         hs_compile_error_t *compile_err = NULL;
142         hs_expr_info_t *info = NULL;
143         hs_error_t err = hs_expression_info(expressions[i], flags[i], &info,
144                                             &compile_err);
145         if (err != HS_SUCCESS) {
146             hs_free_compile_error(compile_err);
147             throw CompileError("Run hs_expression_info() failed.");
148         }
149         if (!info) {
150             throw CompileError("Get hs_expr_info_t failed.");
151         } else {
152             if (info->unordered_matches) {
153                 throw CompileError("Have unordered match in sub-expressions.");
154             }
155             hs_misc_free(info);
156         }
157     }
158 }
159 
logicalKeyRenumber()160 void ParsedLogical::logicalKeyRenumber() {
161     // renumber operation lkey in op vector
162     for (auto &op : logicalTree) {
163         TRY_RENUM_OP(op.id);
164         TRY_RENUM_OP(op.lo);
165         TRY_RENUM_OP(op.ro);
166     }
167     // renumber operation lkey in info map
168     for (auto &ci : combInfoMap) {
169         TRY_RENUM_OP(ci.start);
170         TRY_RENUM_OP(ci.result);
171     }
172 }
173 
174 struct LogicalOperator {
LogicalOperatorue2::LogicalOperator175     LogicalOperator(u32 op_in, u32 paren_in)
176         : op(op_in), paren(paren_in) {}
177     u32 op;
178     u32 paren;
179 };
180 
181 static
toOperator(char c)182 u32 toOperator(char c) {
183     u32 op = UNKNOWN_OP;
184     switch (c) {
185     case '!' :
186         op = LOGICAL_OP_NOT;
187         break;
188     case '&' :
189         op = LOGICAL_OP_AND;
190         break;
191     case '|' :
192         op = LOGICAL_OP_OR;
193         break;
194     default:
195         break;
196     };
197     return op;
198 }
199 
200 static
cmpOperator(const LogicalOperator & op1,const LogicalOperator & op2)201 bool cmpOperator(const LogicalOperator &op1, const LogicalOperator &op2) {
202     if (op1.paren < op2.paren) {
203         return false;
204     }
205     if (op1.paren > op2.paren) {
206         return true;
207     }
208     assert(op1.paren == op2.paren);
209     if (op1.op > op2.op) {
210         return false;
211     }
212     if (op1.op < op2.op) {
213         return true;
214     }
215     return true;
216 }
217 
218 static
fetchSubID(const char * logical,u32 & digit,u32 end)219 u32 fetchSubID(const char *logical, u32 &digit, u32 end) {
220     if (digit == (u32)-1) { // no digit parsing in progress
221         return (u32)-1;
222     }
223     assert(end > digit);
224     if (end - digit > 9) {
225         throw LocatedParseError("Expression id too large");
226     }
227     u32 mult = 1;
228     u32 sum = 0;
229     for (u32 j = end - 1; (j >= digit) && (j != (u32)-1) ; j--) {
230         assert(isdigit(logical[j]));
231         sum += (logical[j] - '0') * mult;
232         mult *= 10;
233     }
234     digit = (u32)-1;
235     return sum;
236 }
237 
238 static
popOperator(vector<LogicalOperator> & op_stack,vector<u32> & subid_stack,ParsedLogical & pl)239 void popOperator(vector<LogicalOperator> &op_stack, vector<u32> &subid_stack,
240                  ParsedLogical &pl) {
241     if (subid_stack.empty()) {
242         throw LocatedParseError("Not enough operand");
243     }
244     u32 right = subid_stack.back();
245     subid_stack.pop_back();
246     u32 left = 0;
247     if (op_stack.back().op != LOGICAL_OP_NOT) {
248         if (subid_stack.empty()) {
249             throw LocatedParseError("Not enough operand");
250         }
251         left = subid_stack.back();
252         subid_stack.pop_back();
253     }
254     subid_stack.push_back(pl.logicalTreeAdd(op_stack.back().op, left, right));
255     op_stack.pop_back();
256 }
257 
parseLogicalCombination(unsigned id,const char * logical,u32 ekey,u64a min_offset,u64a max_offset)258 void ParsedLogical::parseLogicalCombination(unsigned id, const char *logical,
259                                             u32 ekey, u64a min_offset,
260                                             u64a max_offset) {
261     u32 ckey = getCombKey(id);
262     vector<LogicalOperator> op_stack;
263     vector<u32> subid_stack;
264     u32 lkey_start = INVALID_LKEY; // logical operation's lkey
265     u32 paren = 0; // parentheses
266     u32 digit = (u32)-1; // digit start offset, invalid offset is -1
267     u32 subid = (u32)-1;
268     u32 i;
269     try {
270         for (i = 0; logical[i]; i++) {
271             if (isdigit(logical[i])) {
272                 if (digit == (u32)-1) { // new digit start
273                     digit = i;
274                 }
275             } else {
276                 if ((subid = fetchSubID(logical, digit, i)) != (u32)-1) {
277                     subid_stack.push_back(getLogicalKey(subid));
278                     addRelateCKey(subid_stack.back(), ckey);
279                 }
280                 if (logical[i] == ' ') { // skip whitespace
281                     continue;
282                 }
283                 if (logical[i] == '(') {
284                     paren += 1;
285                 } else if (logical[i] == ')') {
286                     if (paren <= 0) {
287                         throw LocatedParseError("Not enough left parentheses");
288                     }
289                     paren -= 1;
290                 } else {
291                     u32 prio = toOperator(logical[i]);
292                     if (prio != UNKNOWN_OP) {
293                         LogicalOperator op(prio, paren);
294                         while (!op_stack.empty()
295                                && cmpOperator(op_stack.back(), op)) {
296                             popOperator(op_stack, subid_stack, *this);
297                             if (lkey_start == INVALID_LKEY) {
298                                 lkey_start = subid_stack.back();
299                             }
300                         }
301                         op_stack.push_back(op);
302                     } else {
303                         throw LocatedParseError("Unknown character");
304                     }
305                 }
306             }
307         }
308         if (paren != 0) {
309             throw LocatedParseError("Not enough right parentheses");
310         }
311         if ((subid = fetchSubID(logical, digit, i)) != (u32)-1) {
312             subid_stack.push_back(getLogicalKey(subid));
313             addRelateCKey(subid_stack.back(), ckey);
314         }
315         while (!op_stack.empty()) {
316             popOperator(op_stack, subid_stack, *this);
317             if (lkey_start == INVALID_LKEY) {
318                 lkey_start = subid_stack.back();
319             }
320         }
321         if (subid_stack.size() != 1) {
322             throw LocatedParseError("Not enough operator");
323         }
324     } catch (LocatedParseError &error) {
325         error.locate(i);
326         throw;
327     }
328     u32 lkey_result = subid_stack.back(); // logical operation's lkey
329     if (lkey_start == INVALID_LKEY) {
330         throw CompileError("No logical operation.");
331     }
332     combinationInfoAdd(ckey, id, ekey, lkey_start, lkey_result,
333                        min_offset, max_offset);
334 }
335 
336 } // namespace ue2
337