1/*
2 * Copyright (c) 2015-2018, 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 Report structure used to manage data associated with a report at
31 * compile time.
32 */
33
34#ifndef UTIL_REPORT_H
35#define UTIL_REPORT_H
36
37#include "ue2common.h"
38#include "util/exhaust.h" // for INVALID_EKEY
39#include "util/logical.h" // for INVALID_LKEY
40#include "util/hash.h"
41#include "util/order_check.h"
42
43#include <cassert>
44
45namespace ue2 {
46
47class ReportManager;
48
49enum ReportType {
50 EXTERNAL_CALLBACK,
51 EXTERNAL_CALLBACK_SOM_REL,
52 INTERNAL_SOM_LOC_SET,
53 INTERNAL_SOM_LOC_SET_IF_UNSET,
54 INTERNAL_SOM_LOC_SET_IF_WRITABLE,
55 INTERNAL_SOM_LOC_SET_SOM_REV_NFA,
56 INTERNAL_SOM_LOC_SET_SOM_REV_NFA_IF_UNSET,
57 INTERNAL_SOM_LOC_SET_SOM_REV_NFA_IF_WRITABLE,
58 INTERNAL_SOM_LOC_COPY,
59 INTERNAL_SOM_LOC_COPY_IF_WRITABLE,
60 INTERNAL_SOM_LOC_MAKE_WRITABLE,
61 EXTERNAL_CALLBACK_SOM_STORED,
62 EXTERNAL_CALLBACK_SOM_ABS,
63 EXTERNAL_CALLBACK_SOM_REV_NFA,
64 INTERNAL_SOM_LOC_SET_FROM,
65 INTERNAL_SOM_LOC_SET_FROM_IF_WRITABLE,
66 INTERNAL_ROSE_CHAIN,
67 EXTERNAL_CALLBACK_SOM_PASS
68};
69
70/**
71 * \brief All the data we use for handling a match.
72 *
73 * Includes extparam constraints and bounds, exhaustion/dedupe keys, offset
74 * adjustment and SOM information.
75 *
76 * The data in this structure eventually becomes a list of Rose programs
77 * instructions.
78 */
79struct Report {
80 Report(ReportType type_in, u32 onmatch_in)
81 : type(type_in), onmatch(onmatch_in) {}
82
83 /** \brief True if this report has bounds from extended parameters, i.e.
84 * min offset, max offset, min length. */
85 bool hasBounds() const {
86 return minOffset > 0 || maxOffset < MAX_OFFSET || minLength > 0;
87 }
88
89 /** \brief Type of this report. */
90 ReportType type;
91
92 /** \brief use SOM for minLength, but don't report it to user callback. */
93 bool quashSom = false;
94
95 /** \brief min offset in the stream at which this report can match. */
96 u64a minOffset = 0;
97
98 /** \brief max offset in the stream at which this report can match. */
99 u64a maxOffset = MAX_OFFSET;
100
101 /** \brief min match length (start of match to current offset) */
102 u64a minLength = 0;
103
104 /** \brief Exhaustion key.
105 *
106 * If exhaustible, the ekey to check before reporting a match.
107 * Additionally after reporting a match the ekey will be set. If not
108 * exhaustible, this will be INVALID_EKEY. */
109 u32 ekey = INVALID_EKEY;
110
111 /** \brief Logical Combination key in each combination.
112 *
113 * If in Logical Combination, the lkey to check before reporting a match.
114 * Additionally before checking the lkey will be set. If not
115 * in Logical Combination, this will be INVALID_LKEY. */
116 u32 lkey = INVALID_LKEY;
117
118 /** \brief Quiet flag for expressions in any logical combination. */
119 bool quiet = false;
120
121 /** \brief Adjustment to add to the match offset when we report a match.
122 *
123 * This is usually used for reports attached to states that form part of a
124 * zero-width assertion, like '$'. */
125 s32 offsetAdjust = 0;
126
127 /** \brief Match report ID, for external reports.
128 *
129 * - external callback -> external report id
130 * - internal_som_* -> som loc to modify
131 * - INTERNAL_ROSE_CHAIN -> top event to push on
132 * - otherwise -> target subnfa */
133 u32 onmatch;
134
135 /** \brief Index of the reverse nfa.
136 *
137 * Used by EXTERNAL_CALLBACK_SOM_REV_NFA and
138 * INTERNAL_SOM_LOC_SET_SOM_REV_NFA*.
139 */
140 u32 revNfaIndex = 0;
141
142 /** \brief SOM distance value, use varies according to type.
143 *
144 * - for EXTERNAL_CALLBACK_SOM_REL, from-offset is this many bytes
145 * before the to-offset.
146 * - for EXTERNAL_CALLBACK_SOM_ABS, set from-offset to this value.
147 * - for INTERNAL_SOM_LOC_COPY*, som location read_from.
148 */
149 u64a somDistance = 0;
150
151 /** \brief Number of bytes behind us that we are allowed to squash
152 * identical top events on the queue.
153 *
154 * Used by INTERNAL_ROSE_CHAIN.
155 */
156 u64a topSquashDistance = 0;
157};
158
159static inline
160bool isExternalReport(const Report &r) {
161 switch (r.type) {
162 case INTERNAL_SOM_LOC_SET:
163 case INTERNAL_SOM_LOC_SET_IF_UNSET:
164 case INTERNAL_SOM_LOC_SET_IF_WRITABLE:
165 case INTERNAL_SOM_LOC_SET_SOM_REV_NFA:
166 case INTERNAL_SOM_LOC_SET_SOM_REV_NFA_IF_UNSET:
167 case INTERNAL_SOM_LOC_SET_SOM_REV_NFA_IF_WRITABLE:
168 case INTERNAL_SOM_LOC_COPY:
169 case INTERNAL_SOM_LOC_COPY_IF_WRITABLE:
170 case INTERNAL_SOM_LOC_MAKE_WRITABLE:
171 case INTERNAL_SOM_LOC_SET_FROM:
172 case INTERNAL_SOM_LOC_SET_FROM_IF_WRITABLE:
173 case INTERNAL_ROSE_CHAIN:
174 return false;
175 case EXTERNAL_CALLBACK:
176 case EXTERNAL_CALLBACK_SOM_REL:
177 case EXTERNAL_CALLBACK_SOM_STORED:
178 case EXTERNAL_CALLBACK_SOM_ABS:
179 case EXTERNAL_CALLBACK_SOM_REV_NFA:
180 case EXTERNAL_CALLBACK_SOM_PASS:
181 return true;
182 default:
183 break; // fall through
184 }
185 assert(0); // unknown?
186 return true;
187}
188
189static inline
190bool isExternalSomReport(const Report &r) {
191 return r.type != EXTERNAL_CALLBACK && isExternalReport(r);
192}
193
194static inline
195bool operator<(const Report &a, const Report &b) {
196 ORDER_CHECK(type);
197 ORDER_CHECK(quashSom);
198 ORDER_CHECK(ekey);
199 ORDER_CHECK(offsetAdjust);
200 ORDER_CHECK(onmatch);
201 ORDER_CHECK(minOffset);
202 ORDER_CHECK(maxOffset);
203 ORDER_CHECK(minLength);
204 ORDER_CHECK(somDistance);
205 ORDER_CHECK(revNfaIndex);
206 ORDER_CHECK(topSquashDistance);
207 return false;
208}
209
210inline
211bool operator==(const Report &a, const Report &b) {
212 return a.type == b.type && a.quashSom == b.quashSom &&
213 a.minOffset == b.minOffset && a.maxOffset == b.maxOffset &&
214 a.minLength == b.minLength && a.ekey == b.ekey &&
215 a.offsetAdjust == b.offsetAdjust && a.onmatch == b.onmatch &&
216 a.revNfaIndex == b.revNfaIndex && a.somDistance == b.somDistance &&
217 a.topSquashDistance == b.topSquashDistance;
218}
219
220static inline
221Report makeECallback(u32 report, s32 offsetAdjust, u32 ekey, bool quiet) {
222 Report ir(EXTERNAL_CALLBACK, report);
223 ir.offsetAdjust = offsetAdjust;
224 ir.ekey = ekey;
225 ir.quiet = (u8)quiet;
226 return ir;
227}
228
229static inline
230Report makeCallback(u32 report, s32 offsetAdjust) {
231 return makeECallback(report, offsetAdjust, INVALID_EKEY, false);
232}
233
234static inline
235Report makeSomRelativeCallback(u32 report, s32 offsetAdjust, u64a distance) {
236 Report ir(EXTERNAL_CALLBACK_SOM_REL, report);
237 ir.offsetAdjust = offsetAdjust;
238 ir.ekey = INVALID_EKEY;
239 ir.somDistance = distance;
240 return ir;
241}
242
243static inline
244Report makeMpvTrigger(u32 event, u64a squashDistance) {
245 Report ir(INTERNAL_ROSE_CHAIN, event);
246 ir.ekey = INVALID_EKEY;
247 ir.topSquashDistance = squashDistance;
248 return ir;
249}
250
251/** simple exhaustible: exhaustible and if the first attempted match does not
252 * succeed, no later matches will succeed either */
253static inline
254bool isSimpleExhaustible(const Report &ir) {
255 if (ir.ekey == INVALID_EKEY) {
256 return false;
257 }
258
259 if (ir.hasBounds() && (ir.minOffset || ir.minLength)) {
260 return false;
261 }
262
263 if (!isExternalReport(ir)) {
264 return false;
265 }
266
267 return true;
268}
269
270} // namespace ue2
271
272namespace std {
273
274template<>
275struct hash<ue2::Report> {
276 std::size_t operator()(const ue2::Report &r) const {
277 return ue2::hash_all(r.type, r.quashSom, r.minOffset, r.maxOffset,
278 r.minLength, r.ekey, r.offsetAdjust, r.onmatch,
279 r.revNfaIndex, r.somDistance, r.topSquashDistance);
280 }
281};
282
283} // namespace std
284
285#endif // UTIL_REPORT_H
286