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