suricata
util-spm-hs.c
Go to the documentation of this file.
1 /* Copyright (C) 2016 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Justin Viiret <justin.viiret@intel.com>
22  *
23  * Single pattern matcher that uses the Hyperscan regex matcher.
24  */
25 
26 #include "suricata-common.h"
27 #include "suricata.h"
28 
29 #include "util-hyperscan.h"
30 #include "util-spm-hs.h"
31 
32 #ifdef BUILD_HYPERSCAN
33 
34 #include <hs.h>
35 
36 /**
37  * \internal
38  * \brief Hyperscan match callback, called by hs_scan.
39  */
40 static int MatchEvent(unsigned int id, unsigned long long from,
41  unsigned long long to, unsigned int flags, void *context)
42 {
43  uint64_t *match_offset = context;
44  BUG_ON(*match_offset != UINT64_MAX);
45  *match_offset = to;
46  return 1; /* Terminate matching. */
47 }
48 
49 typedef struct SpmHsCtx_ {
50  hs_database_t *db;
51  uint16_t needle_len;
52 } SpmHsCtx;
53 
54 static void HSDestroyCtx(SpmCtx *ctx)
55 {
56  if (ctx == NULL) {
57  return;
58  }
59  SpmHsCtx *sctx = ctx->ctx;
60  if (sctx) {
61  hs_free_database(sctx->db);
62  SCFree(sctx);
63  }
64  SCFree(ctx);
65 }
66 
67 static int HSBuildDatabase(const uint8_t *needle, uint16_t needle_len,
68  int nocase, SpmHsCtx *sctx,
69  SpmGlobalThreadCtx *global_thread_ctx)
70 {
71  char *expr = HSRenderPattern(needle, needle_len);
72  if (expr == NULL) {
73  SCLogDebug("HSRenderPattern returned NULL");
74  return -1;
75  }
76 
77  unsigned flags = nocase ? HS_FLAG_CASELESS : 0;
78 
79  hs_database_t *db = NULL;
80  hs_compile_error_t *compile_err = NULL;
81  hs_error_t err = hs_compile(expr, flags, HS_MODE_BLOCK, NULL, &db,
82  &compile_err);
83  if (err != HS_SUCCESS) {
84  SCLogError(SC_ERR_FATAL, "Unable to compile '%s' with Hyperscan, "
85  "returned %d.", expr, err);
86  exit(EXIT_FAILURE);
87  }
88 
89  SCFree(expr);
90 
91  /* Update scratch for this database. */
92  hs_scratch_t *scratch = global_thread_ctx->ctx;
93  err = hs_alloc_scratch(db, &scratch);
94  if (err != HS_SUCCESS) {
95  /* If scratch allocation failed, this is not recoverable: other SPM
96  * contexts may need this scratch space. */
98  "Unable to alloc scratch for Hyperscan, returned %d.", err);
99  exit(EXIT_FAILURE);
100  }
101  global_thread_ctx->ctx = scratch;
102  sctx->db = db;
103  sctx->needle_len = needle_len;
104 
105  return 0;
106 }
107 
108 static SpmCtx *HSInitCtx(const uint8_t *needle, uint16_t needle_len, int nocase,
109  SpmGlobalThreadCtx *global_thread_ctx)
110 {
111  SpmCtx *ctx = SCMalloc(sizeof(SpmCtx));
112  if (ctx == NULL) {
113  SCLogDebug("Unable to alloc SpmCtx.");
114  return NULL;
115  }
116  memset(ctx, 0, sizeof(SpmCtx));
117  ctx->matcher = SPM_HS;
118 
119  SpmHsCtx *sctx = SCMalloc(sizeof(SpmHsCtx));
120  if (sctx == NULL) {
121  SCLogDebug("Unable to alloc SpmHsCtx.");
122  SCFree(ctx);
123  return NULL;
124  }
125  ctx->ctx = sctx;
126 
127  memset(sctx, 0, sizeof(SpmHsCtx));
128  if (HSBuildDatabase(needle, needle_len, nocase, sctx,
129  global_thread_ctx) != 0) {
130  SCLogDebug("HSBuildDatabase failed.");
131  HSDestroyCtx(ctx);
132  return NULL;
133  }
134 
135  return ctx;
136 }
137 
138 static uint8_t *HSScan(const SpmCtx *ctx, SpmThreadCtx *thread_ctx,
139  const uint8_t *haystack, uint32_t haystack_len)
140 {
141  const SpmHsCtx *sctx = ctx->ctx;
142  hs_scratch_t *scratch = thread_ctx->ctx;
143 
144  if (unlikely(haystack_len == 0)) {
145  return NULL;
146  }
147 
148  uint64_t match_offset = UINT64_MAX;
149  hs_error_t err = hs_scan(sctx->db, (const char *)haystack, haystack_len, 0,
150  scratch, MatchEvent, &match_offset);
151  if (err != HS_SUCCESS && err != HS_SCAN_TERMINATED) {
152  /* An error value (other than HS_SCAN_TERMINATED) from hs_scan()
153  * indicates that it was passed an invalid database or scratch region,
154  * which is not something we can recover from at scan time. */
155  SCLogError(SC_ERR_FATAL, "Hyperscan returned fatal error %d.", err);
156  exit(EXIT_FAILURE);
157  }
158 
159  if (match_offset == UINT64_MAX) {
160  return NULL;
161  }
162 
163  BUG_ON(match_offset < sctx->needle_len);
164 
165  /* Note: existing API returns non-const ptr */
166  return (uint8_t *)haystack + (match_offset - sctx->needle_len);
167 }
168 
169 static SpmGlobalThreadCtx *HSInitGlobalThreadCtx(void)
170 {
171  SpmGlobalThreadCtx *global_thread_ctx = SCMalloc(sizeof(SpmGlobalThreadCtx));
172  if (global_thread_ctx == NULL) {
173  SCLogDebug("Unable to alloc SpmGlobalThreadCtx.");
174  return NULL;
175  }
176  memset(global_thread_ctx, 0, sizeof(*global_thread_ctx));
177  global_thread_ctx->matcher = SPM_HS;
178 
179  /* We store scratch in the HS-specific ctx. This will be initialized as
180  * patterns are compiled by SpmInitCtx. */
181  global_thread_ctx->ctx = NULL;
182 
183  return global_thread_ctx;
184 }
185 
186 static void HSDestroyGlobalThreadCtx(SpmGlobalThreadCtx *global_thread_ctx)
187 {
188  if (global_thread_ctx == NULL) {
189  return;
190  }
191  hs_free_scratch(global_thread_ctx->ctx);
192  SCFree(global_thread_ctx);
193 }
194 
195 static void HSDestroyThreadCtx(SpmThreadCtx *thread_ctx)
196 {
197  if (thread_ctx == NULL) {
198  return;
199  }
200  hs_free_scratch(thread_ctx->ctx);
201  SCFree(thread_ctx);
202 }
203 
204 static SpmThreadCtx *HSMakeThreadCtx(const SpmGlobalThreadCtx *global_thread_ctx)
205 {
206  SpmThreadCtx *thread_ctx = SCMalloc(sizeof(SpmThreadCtx));
207  if (thread_ctx == NULL) {
208  SCLogDebug("Unable to alloc SpmThreadCtx.");
209  return NULL;
210  }
211  memset(thread_ctx, 0, sizeof(*thread_ctx));
212  thread_ctx->matcher = SPM_HS;
213 
214  if (global_thread_ctx->ctx != NULL) {
215  hs_scratch_t *scratch = NULL;
216  hs_error_t err = hs_clone_scratch(global_thread_ctx->ctx, &scratch);
217  if (err != HS_SUCCESS) {
218  SCLogError(SC_ERR_FATAL, "Unable to clone scratch (error %d).",
219  err);
220  exit(EXIT_FAILURE);
221  }
222  thread_ctx->ctx = scratch;
223  }
224 
225  return thread_ctx;
226 }
227 
228 void SpmHSRegister(void)
229 {
230  spm_table[SPM_HS].name = "hs";
231  spm_table[SPM_HS].InitGlobalThreadCtx = HSInitGlobalThreadCtx;
232  spm_table[SPM_HS].DestroyGlobalThreadCtx = HSDestroyGlobalThreadCtx;
233  spm_table[SPM_HS].MakeThreadCtx = HSMakeThreadCtx;
234  spm_table[SPM_HS].DestroyThreadCtx = HSDestroyThreadCtx;
235  spm_table[SPM_HS].InitCtx = HSInitCtx;
236  spm_table[SPM_HS].DestroyCtx = HSDestroyCtx;
237  spm_table[SPM_HS].Scan = HSScan;
238 }
239 
240 #endif /* BUILD_HYPERSCAN */
const char * name
Definition: util-spm.h:62
uint16_t flags
#define SCLogDebug(...)
Definition: util-debug.h:335
#define BUG_ON(x)
uint16_t matcher
Definition: util-spm.h:43
#define unlikely(expr)
Definition: util-optimize.h:35
void(* DestroyCtx)(SpmCtx *)
Definition: util-spm.h:69
SpmCtx *(* InitCtx)(const uint8_t *needle, uint16_t needle_len, int nocase, SpmGlobalThreadCtx *g_thread_ctx)
Definition: util-spm.h:67
char * HSRenderPattern(const uint8_t *pat, uint16_t pat_len)
uint8_t *(* Scan)(const SpmCtx *ctx, SpmThreadCtx *thread_ctx, const uint8_t *haystack, uint32_t haystack_len)
Definition: util-spm.h:70
uint16_t matcher
Definition: util-spm.h:57
SpmGlobalThreadCtx *(* InitGlobalThreadCtx)(void)
Definition: util-spm.h:63
SpmTableElmt spm_table[SPM_TABLE_SIZE]
Definition: util-spm.h:74
void(* DestroyThreadCtx)(SpmThreadCtx *thread_ctx)
Definition: util-spm.h:66
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
SpmThreadCtx *(* MakeThreadCtx)(const SpmGlobalThreadCtx *g_thread_ctx)
Definition: util-spm.h:65
void(* DestroyGlobalThreadCtx)(SpmGlobalThreadCtx *g_thread_ctx)
Definition: util-spm.h:64
void * ctx
Definition: util-spm.h:58
void * ctx
Definition: util-spm.h:44
#define SCMalloc(a)
Definition: util-mem.h:222
#define SCFree(a)
Definition: util-mem.h:322
void SpmHSRegister(void)
uint16_t matcher
Definition: util-spm.h:50