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 Chimera: main runtime.
31 */
32
33 #include <limits.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "ch.h"
39 #include "hs.h"
40 #include "hs_internal.h"
41 #include "ue2common.h"
42 #include "ch_database.h"
43 #include "ch_internal.h"
44 #include "ch_scratch.h"
45 #include "util/multibit.h"
46 #include "util/unicode_def.h"
47
48 typedef struct queue_item PQ_T;
49
50 static
PQ_COMP(PQ_T * pqc_items,int a,int b)51 char PQ_COMP(PQ_T *pqc_items, int a, int b) {
52 if ((pqc_items)[a].to != (pqc_items)[b].to) {
53 return (pqc_items)[a].to < (pqc_items)[b].to;
54 } else if ((pqc_items)[a].from != (pqc_items)[b].from) {
55 return (pqc_items)[a].from < (pqc_items)[b].from;
56 } else {
57 return (pqc_items)[a].id < (pqc_items)[b].id;
58 }
59 }
60
61 static
PQ_COMP_B(PQ_T * pqc_items,int a,PQ_T b_fixed)62 char PQ_COMP_B(PQ_T *pqc_items, int a, PQ_T b_fixed) {
63 if ((pqc_items)[a].to != (b_fixed).to) {
64 return (pqc_items)[a].to < (b_fixed).to;
65 } else if ((pqc_items)[a].from != (b_fixed).from) {
66 return (pqc_items)[a].from < (b_fixed).from;
67 } else {
68 return (pqc_items)[a].id < b_fixed.id;
69 }
70 }
71
72 #include "util/pqueue.h"
73
74 static really_inline
pq_insert_with(struct match_pq * pq,int from,int to,u32 id)75 void pq_insert_with(struct match_pq *pq, int from, int to, u32 id) {
76 DEBUG_PRINTF("inserting pattern%u in pq at %u\n", id, to);
77 struct queue_item temp = {
78 .from = from,
79 .to = to,
80 .id = id,
81 };
82
83 pq_insert(pq->item, pq->size, temp);
84 ++pq->size;
85 }
86
87 static really_inline
pq_pop_nice(struct match_pq * pq)88 void pq_pop_nice(struct match_pq *pq) {
89 pq_pop(pq->item, pq->size);
90 pq->size--;
91 }
92
93 /** dummy event handler for use when user does not provide one */
94 static
null_onEvent(UNUSED unsigned id,UNUSED unsigned long long from,UNUSED unsigned long long to,UNUSED unsigned flags,UNUSED unsigned size,UNUSED const ch_capture_t * captured,UNUSED void * ctxt)95 int HS_CDECL null_onEvent(UNUSED unsigned id, UNUSED unsigned long long from,
96 UNUSED unsigned long long to, UNUSED unsigned flags,
97 UNUSED unsigned size, UNUSED const ch_capture_t *captured,
98 UNUSED void *ctxt) {
99 return 0;
100 }
101
102 /** \brief Chimera runtime context. */
103 struct HybridContext {
104 const char *data; //!< buffer being scanned
105 u32 length; //!< length of data buffer
106 u32 valid_utf8_highwater; //!< UTF-8 has been validated up to here.
107 const struct ch_bytecode *db;
108 struct ch_scratch *scratch;
109 struct match_pq *pq;
110 /** \brief user-supplied match callback */
111 int (HS_CDECL *match_callback)(unsigned int id, unsigned long long from,
112 unsigned long long to, unsigned int flags,
113 unsigned int size, const ch_capture_t *capture,
114 void *ctx);
115 /** \brief user-supplied error callback */
116 int (HS_CDECL *error_callback)(ch_error_event_t error_type, unsigned int id,
117 void *info, void *ctx);
118 /** \brief user-supplied context */
119 void *context;
120 };
121
122 // Internal PCRE func.
123 extern int _pcre_valid_utf(const unsigned char *, int, int *);
124
125 /** UTF-8 validity check. Returns >0 if the given region of the data is valid
126 * UTF-8, 0 otherwise. */
127 static
isValidUTF8(struct HybridContext * hyctx,u32 end)128 char isValidUTF8(struct HybridContext *hyctx, u32 end) {
129 assert(hyctx);
130
131 if (hyctx->valid_utf8_highwater >= end) {
132 return 1; // Already validated.
133 }
134
135 const unsigned char *data =
136 (const unsigned char *)hyctx->data + hyctx->valid_utf8_highwater;
137 int validate_len = end - hyctx->valid_utf8_highwater;
138
139 DEBUG_PRINTF("validating %d bytes\n", validate_len);
140
141 int erroroffset = 0;
142 if (_pcre_valid_utf(data, validate_len, &erroroffset)) {
143 DEBUG_PRINTF("UTF8 invalid at offset %d\n", erroroffset);
144 return 0;
145 }
146
147 hyctx->valid_utf8_highwater = end;
148 return 1;
149 }
150
151 static
getPcre(const struct ch_pattern * pattern)152 const pcre *getPcre(const struct ch_pattern *pattern) {
153 const char *ptr = (const char *)pattern;
154 const pcre *p = (const pcre *)(ptr + ROUNDUP_N(sizeof(*pattern), 8));
155 assert(ISALIGNED_N(p, 8));
156 return p;
157 }
158
159 /** \brief Fill the Chimera groups array from a pcre_exec ovector. */
160 static
fillGroupsFromOvector(ch_capture_t * groups,int numPairs,int * ovector)161 void fillGroupsFromOvector(ch_capture_t *groups, int numPairs, int *ovector) {
162 assert(groups);
163 assert(ISALIGNED_N(groups, alignof(ch_capture_t)));
164
165 DEBUG_PRINTF("filling %d groups (@ %p) from pcre ovector\n",
166 numPairs, groups);
167
168 for (int i = 0; i < numPairs * 2; i += 2) {
169 if (ovector[i] == -1) {
170 groups->flags = CH_CAPTURE_FLAG_INACTIVE;
171 } else {
172 groups->flags = CH_CAPTURE_FLAG_ACTIVE;
173 assert(ovector[i] <= ovector[i + 1]);
174 groups->from = ovector[i];
175 groups->to = ovector[i + 1];
176 }
177 ++groups;
178 }
179 }
180
181 static
handlePcreNonMatch(const struct ch_pattern * pattern,int rv,ch_error_event_handler onError,void * userContext)182 ch_error_t handlePcreNonMatch(const struct ch_pattern *pattern, int rv,
183 ch_error_event_handler onError,
184 void *userContext) {
185 assert(rv < 0);
186
187 if (rv == PCRE_ERROR_NOMATCH) {
188 DEBUG_PRINTF("no match found by libpcre\n");
189 return CH_SUCCESS;
190 } else if (rv == PCRE_ERROR_MATCHLIMIT) {
191 DEBUG_PRINTF("pcre hit match limit\n");
192 if (onError) {
193 return onError(CH_ERROR_MATCHLIMIT, pattern->id, NULL,
194 userContext);
195 }
196 return CH_SUCCESS;
197 } else if (rv == PCRE_ERROR_RECURSIONLIMIT) {
198 DEBUG_PRINTF("pcre hit recursion limit\n");
199 if (onError) {
200 return onError(CH_ERROR_RECURSIONLIMIT, pattern->id, NULL,
201 userContext);
202 }
203 return CH_SUCCESS;
204 }
205
206 // All other errors not handled above are fatal.
207 return CH_FAIL_INTERNAL;
208 }
209
210 static
scanPcre(struct HybridContext * hyctx,UNUSED unsigned int length,unsigned int offset,u32 id)211 ch_error_t scanPcre(struct HybridContext *hyctx, UNUSED unsigned int length,
212 unsigned int offset, u32 id) {
213 const char *data = hyctx->data;
214 unsigned int full_length = hyctx->length;
215 ch_error_event_handler onError = hyctx->error_callback;
216 void *userContext = hyctx->context;
217
218 const struct ch_pattern *pattern = getPattern(hyctx->db, id);
219 const pcre *p = getPcre(pattern);
220
221 // Set up the PCRE extra block.
222 const pcre_extra *extra = &pattern->extra;
223
224 int startoffset = offset;
225
226 int *ovector = hyctx->scratch->ovector;
227 int ovectorSize = (hyctx->scratch->maxCaptureGroups + 1) * 3;
228 assert(ovectorSize >= 2);
229
230 DEBUG_PRINTF("scanning %u bytes, pattern %u, startoffset %d\n",
231 length, id, startoffset);
232
233 int options = 0;
234 if (pattern->flags & CHIMERA_PATTERN_FLAG_UTF8) {
235 // We do our own UTF-8 validation.
236 options |= PCRE_NO_UTF8_CHECK;
237 if (!isValidUTF8(hyctx, full_length)) {
238 return handlePcreNonMatch(pattern, PCRE_ERROR_BADUTF8, onError,
239 userContext);
240 }
241 }
242
243 int rv = pcre_exec(p, extra, data, full_length, startoffset, options,
244 ovector, ovectorSize);
245
246 DEBUG_PRINTF("pcre return code is %d\n", rv);
247
248 // Handle all non-match or error cases, all of which involve us
249 // terminating the loop.
250 if (rv < 0) {
251 return handlePcreNonMatch(pattern, rv, onError, userContext);
252 }
253
254 // We've found a match, and we should always have room for at least the
255 // start and end offsets in our ovector. Pass this info to the user.
256 assert(rv >= 1);
257 assert(rv < ovectorSize);
258 int from = ovector[0];
259 int to = ovector[1];
260 DEBUG_PRINTF("match %d -> %d\n", from, to);
261
262 struct ch_patterndata *pd = hyctx->scratch->patternData + id;
263
264 if (hyctx->db->flags & CHIMERA_FLAG_GROUPS) {
265 fillGroupsFromOvector(pd->match, rv, ovector);
266 } else {
267 rv = 0;
268 }
269 pd->groupCount = (u32)rv;
270
271 // Insert new matched item to the queue
272 pq_insert_with(hyctx->pq, from, to, id);
273
274 // Next scan starts at the first codepoint after the match. It's
275 // possible that we have a vacuous match, in which case we must step
276 // past it to ensure that we always progress.
277 if (from != to) {
278 startoffset = to;
279 } else if (pattern->flags & CHIMERA_PATTERN_FLAG_UTF8) {
280 startoffset = to + 1;
281 while (startoffset < (int)full_length &&
282 ((data[startoffset] & 0xc0) == UTF_CONT_BYTE_HEADER)) {
283 ++startoffset;
284 }
285 } else {
286 startoffset = to + 1;
287 }
288
289 pd->scanStart = startoffset;
290 DEBUG_PRINTF("new offset %u\n", pd->scanStart);
291
292 return CH_SUCCESS;
293 }
294
295 static
catchupPcre(struct HybridContext * hyctx,unsigned int id,unsigned long long from,unsigned long long to)296 ch_error_t catchupPcre(struct HybridContext *hyctx, unsigned int id,
297 unsigned long long from, unsigned long long to) {
298 ch_match_event_handler onEvent = hyctx->match_callback;
299 void *userContext = hyctx->context;
300 DEBUG_PRINTF("priority queue size %u\n", hyctx->pq->size);
301 while (hyctx->pq->size) {
302 u32 num_item = hyctx->pq->size;
303 struct queue_item *item = pq_top(hyctx->pq->item);
304 size_t top_from = item->from;
305 size_t top_to = item->to;
306 u32 top_id = item->id;
307
308 if (top_to > to) {
309 pq_insert_with(hyctx->pq, from, to, id);
310 break;
311 }
312 pq_pop_nice(hyctx->pq);
313
314 const struct ch_pattern *pattern = getPattern(hyctx->db, top_id);
315 struct ch_patterndata *pd = hyctx->scratch->patternData + top_id;
316
317 // Report match for pattern
318 DEBUG_PRINTF("trigger match@%zu\n", top_to);
319 ch_callback_t cbrv =
320 onEvent(pattern->id, top_from, top_to, 0 /* flags */,
321 pd->groupCount, pd->match, userContext);
322
323 if (cbrv == CH_CALLBACK_TERMINATE) {
324 DEBUG_PRINTF("user callback told us to terminate scanning\n");
325 return CH_SCAN_TERMINATED;
326 } else if (cbrv == CH_CALLBACK_SKIP_PATTERN) {
327 DEBUG_PRINTF("user callback told us to skip this pattern\n");
328 pd->scanStart = hyctx->length;
329 }
330
331 if (top_id == id) {
332 break;
333 }
334
335 // Push a new match to replace the old one
336 unsigned int start = pd->scanStart;
337 unsigned int len = hyctx->length - pd->scanStart;
338 if (hyctx->length >= pd->scanStart &&
339 !(pattern->flags & CHIMERA_PATTERN_FLAG_SINGLEMATCH)) {
340 DEBUG_PRINTF("get a new match item\n");
341 int ret = scanPcre(hyctx, len, start, top_id);
342
343 if (ret == CH_CALLBACK_TERMINATE) {
344 DEBUG_PRINTF("user callback told us to terminate scanning\n");
345 return CH_SCAN_TERMINATED;
346 } else if (ret == CH_CALLBACK_SKIP_PATTERN) {
347 DEBUG_PRINTF("user callback told us to skip this pattern\n");
348 pd->scanStart = hyctx->length;
349 ret = CH_SUCCESS;
350 } else if (ret == CH_FAIL_INTERNAL) {
351 return ret;
352 }
353
354 // No further match is found
355 if (hyctx->pq->size == num_item - 1) {
356 pd->scanStart = hyctx->length;
357 }
358 }
359 }
360
361 return CH_SUCCESS;
362 }
363
364 /** \brief Callback used for internal Hyperscan multi-matcher. */
365 static
multiCallback(unsigned int id,unsigned long long from,unsigned long long to,UNUSED unsigned int flags,void * ctx)366 int HS_CDECL multiCallback(unsigned int id, unsigned long long from,
367 unsigned long long to, UNUSED unsigned int flags,
368 void *ctx) {
369 assert(ctx);
370 struct HybridContext *hyctx = ctx;
371
372 DEBUG_PRINTF("match for ID %u at offset %llu\n", id, to);
373 assert(id < hyctx->db->patternCount);
374
375 const struct ch_pattern *pattern = getPattern(hyctx->db, id);
376 struct ch_patterndata *pd = hyctx->scratch->patternData + id;
377 char needConfirm = pattern->fixedWidth == ~0U;
378
379 if (needConfirm &&
380 mmbit_isset(hyctx->scratch->active, hyctx->db->patternCount, id)) {
381 if ((hyctx->db->flags & CHIMERA_FLAG_ALL_CONFIRM) &&
382 mmbit_all(hyctx->scratch->active, hyctx->db->patternCount)) {
383 return 1;
384 }
385 return 0;
386 }
387 // Store the fact that we've seen this bit.
388 char already = mmbit_set(hyctx->scratch->active,
389 hyctx->db->patternCount, id);
390 DEBUG_PRINTF("match from %u to %llu\n", pd->scanStart, to);
391
392 if (!already) {
393 pd->scanStart = 0;
394 } else if (to < pd->scanStart + pattern->minWidth) {
395 return 0;
396 } else if (pattern->flags & CHIMERA_PATTERN_FLAG_SINGLEMATCH) {
397 if ((hyctx->db->flags & CHIMERA_FLAG_ALL_SINGLE) &&
398 mmbit_all(hyctx->scratch->active, hyctx->db->patternCount)) {
399 return 1;
400 }
401 // Note: we may have unordered match from Hyperscan,
402 // thus possibly get to < pd->scanStart.
403 return 0;
404 }
405
406 int ret = HS_SUCCESS;
407 unsigned int start = pd->scanStart;
408 unsigned int len = hyctx->length - pd->scanStart;
409 assert(hyctx->length >= pd->scanStart);
410 const char *data = hyctx->data;
411 if (needConfirm) {
412 DEBUG_PRINTF("run confirm for the first time\n");
413 ret = scanPcre(hyctx, len, start, id);
414 hyctx->scratch->ret = ret;
415 if (ret == CH_CALLBACK_TERMINATE) {
416 DEBUG_PRINTF("user callback told us to terminate scanning\n");
417 return HS_SCAN_TERMINATED;
418 } else if (ret == CH_CALLBACK_SKIP_PATTERN) {
419 DEBUG_PRINTF("user callback told us to skip this pattern\n");
420 pd->scanStart = hyctx->length;
421 ret = HS_SUCCESS;
422 hyctx->scratch->ret = ret;
423 } else if (ret == CH_FAIL_INTERNAL) {
424 return ret;
425 }
426 } else {
427 if (already) {
428 DEBUG_PRINTF("catch up with new matches\n");
429 ret = catchupPcre(hyctx, id, from, to);
430
431 hyctx->scratch->ret = ret;
432 if (pd->scanStart >= hyctx->length) {
433 return ret;
434 }
435 }
436 int startoffset = 0;
437 // Next scan starts at the first codepoint after the match. It's
438 // possible that we have a vacuous match, in which case we must step
439 // past it to ensure that we always progress.
440 if (from != to) {
441 startoffset = to;
442 } else if (pattern->flags & CHIMERA_PATTERN_FLAG_UTF8) {
443 startoffset = to + 1;
444 while (startoffset < (int)hyctx->length &&
445 ((data[startoffset] & 0xc0) == UTF_CONT_BYTE_HEADER)) {
446 ++startoffset;
447 }
448 } else {
449 startoffset = to + 1;
450 }
451 pd->scanStart = startoffset;
452 int rv = 0;
453 if (hyctx->db->flags & CHIMERA_FLAG_GROUPS) {
454 ch_capture_t *groups = pd->match;
455 groups->flags = CH_CAPTURE_FLAG_ACTIVE;
456 groups->from = from;
457 groups->to = to;
458 rv = 1;
459 }
460 pd->groupCount = (u32)rv;
461 pq_insert_with(hyctx->pq, from, to, id);
462 }
463
464 return ret;
465 }
466
467 static
scanHyperscan(struct HybridContext * hyctx,const char * data,unsigned int length)468 hs_error_t scanHyperscan(struct HybridContext *hyctx, const char *data,
469 unsigned int length) {
470 DEBUG_PRINTF("scanning %u bytes with Hyperscan\n", length);
471 const struct ch_bytecode *hydb = hyctx->db;
472 const hs_database_t *db = getHyperscanDatabase(hydb);
473 hs_scratch_t *scratch = hyctx->scratch->multi_scratch;
474
475 hs_error_t err = hs_scan(db, data, length, 0, scratch, multiCallback,
476 hyctx);
477
478 return err;
479 }
480
481 /** \brief Init match priority queue.
482 *
483 * Add a first match offset for each pattern that is not supported by Hyperscan
484 * with prefiltering.
485 */
486 static really_inline
initQueue(struct HybridContext * hyctx,struct match_pq * pq)487 ch_error_t initQueue(struct HybridContext *hyctx, struct match_pq *pq) {
488 const struct ch_bytecode *db = hyctx->db;
489
490 u8 *active = hyctx->scratch->active;
491 mmbit_clear(active, db->patternCount);
492
493 // Init match queue size
494 pq->size = 0;
495
496 unsigned int length = hyctx->length;
497 const u32 *unguarded = getUnguarded(db);
498 for (u32 i = 0; i < db->unguardedCount; i++) {
499 u32 patternId = unguarded[i];
500 DEBUG_PRINTF("switch on unguarded pcre %u\n", patternId);
501 mmbit_set(active, db->patternCount, patternId);
502
503 DEBUG_PRINTF("get a new match item\n");
504 int ret = scanPcre(hyctx, length, 0, patternId);
505
506 struct ch_patterndata *pd = hyctx->scratch->patternData + patternId;
507 if (ret == CH_CALLBACK_TERMINATE) {
508 DEBUG_PRINTF("user callback told us to terminate scanning\n");
509 return CH_SCAN_TERMINATED;
510 } else if (ret == CH_CALLBACK_SKIP_PATTERN) {
511 DEBUG_PRINTF("user callback told us to skip this pattern\n");
512 pd->scanStart = length;
513 ret = CH_SUCCESS;
514 } else if (ret == CH_FAIL_INTERNAL) {
515 return ret;
516 }
517 }
518
519 return CH_SUCCESS;
520 }
521
522 static really_inline
ch_scan_i(const ch_database_t * hydb,const char * data,unsigned int length,UNUSED unsigned int flags,ch_scratch_t * scratch,ch_match_event_handler onEvent,ch_error_event_handler onError,void * userContext)523 ch_error_t ch_scan_i(const ch_database_t *hydb,
524 const char *data, unsigned int length,
525 UNUSED unsigned int flags,
526 ch_scratch_t *scratch,
527 ch_match_event_handler onEvent,
528 ch_error_event_handler onError,
529 void *userContext) {
530 if (unlikely(!hydb || !scratch || !data)) {
531 DEBUG_PRINTF("args invalid\n");
532 return CH_INVALID;
533 }
534 ch_error_t ret = hydbIsValid(hydb);
535 if (ret != CH_SUCCESS) {
536 DEBUG_PRINTF("database invalid\n");
537 return ret;
538 }
539
540 if (!ISALIGNED_CL(scratch)) {
541 DEBUG_PRINTF("bad alignment %p\n", scratch);
542 return CH_INVALID;
543 }
544
545 if (scratch->magic != CH_SCRATCH_MAGIC) {
546 DEBUG_PRINTF("scratch invalid\n");
547 return CH_INVALID;
548 }
549
550 if (unlikely(markScratchInUse(scratch))) {
551 return CH_SCRATCH_IN_USE;
552 }
553
554 // Hyperscan underlying scratch and database validity will be checked by
555 // the hs_scan() call, so no need to do it here.
556
557 // PCRE takes the data region length in as an int, so this limits our block
558 // size to INT_MAX.
559 if (length > INT_MAX) {
560 DEBUG_PRINTF("length invalid\n");
561 unmarkScratchInUse(scratch);
562 return CH_INVALID;
563 }
564
565 const struct ch_bytecode *db = ch_get_bytecode(hydb);
566
567 scratch->pq.size = 0;
568 scratch->ret = CH_SUCCESS;
569
570 // Firstly, we run Hyperscan in block mode and add its matches into the
571 // active list for subsequent confirmation with pcre.
572 struct HybridContext hyctx = {
573 .data = data,
574 .length = length,
575 .valid_utf8_highwater = 0,
576 .db = db,
577 .scratch = scratch,
578 .pq = &scratch->pq,
579 .match_callback = onEvent ? onEvent : null_onEvent,
580 .error_callback = onError,
581 .context = userContext
582 };
583
584 // Init priority queue.
585 ret = initQueue(&hyctx, &scratch->pq);
586 if (ret != CH_SUCCESS) {
587 DEBUG_PRINTF("Chimera returned error %d\n", ret);
588 unmarkScratchInUse(scratch);
589 return ret;
590 }
591
592 if (!(db->flags & CHIMERA_FLAG_NO_MULTIMATCH)) {
593 ret = scanHyperscan(&hyctx, data, length);
594 // Errors from pcre scan.
595 if (scratch->ret == CH_CALLBACK_TERMINATE) {
596 DEBUG_PRINTF("Pcre terminates scan\n");
597 unmarkScratchInUse(scratch);
598 return CH_SCAN_TERMINATED;
599 } else if (scratch->ret != CH_SUCCESS) {
600 DEBUG_PRINTF("Pcre internal error\n");
601 unmarkScratchInUse(scratch);
602 return scratch->ret;
603 }
604 // Errors from Hyperscan scan. Note Chimera could terminate
605 // Hyperscan callback on purpose so this is not counted as an error.
606 if (ret != HS_SUCCESS && ret != HS_SCAN_TERMINATED) {
607 assert(scratch->ret == CH_SUCCESS);
608 DEBUG_PRINTF("Hyperscan returned error %d\n", ret);
609 unmarkScratchInUse(scratch);
610 return ret;
611 }
612 }
613
614 DEBUG_PRINTF("Flush priority queue\n");
615 // Catch up with PCRE and make up id and offsets as we don't really care
616 // about their values
617 ret = catchupPcre(&hyctx, ~0U, length, length);
618 if (ret != CH_SUCCESS) {
619 DEBUG_PRINTF("PCRE catch up returned error %d\n", ret);
620 unmarkScratchInUse(scratch);
621 return ret;
622 }
623
624 unmarkScratchInUse(scratch);
625 return CH_SUCCESS;
626 }
627
628 HS_PUBLIC_API
ch_scan(const ch_database_t * hydb,const char * data,unsigned int length,unsigned int flags,ch_scratch_t * scratch,ch_match_event_handler onEvent,ch_error_event_handler onError,void * userContext)629 ch_error_t HS_CDECL ch_scan(const ch_database_t *hydb, const char *data,
630 unsigned int length, unsigned int flags,
631 ch_scratch_t *scratch,
632 ch_match_event_handler onEvent,
633 ch_error_event_handler onError, void *userContext) {
634 ch_error_t ret = ch_scan_i(hydb, data, length, flags, scratch, onEvent,
635 onError, userContext);
636
637 return ret;
638 }
639
640 HS_PUBLIC_API
ch_version(void)641 const char * HS_CDECL ch_version(void) {
642 return HS_VERSION_STRING;
643 }
644