1 /*
2  * Copyright (c) 2016, 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 #include "config.h"
30 
31 #include "test_util.h"
32 
33 #include "hs.h"
34 #include "gtest/gtest.h"
35 
36 #include <memory>
37 
38 using namespace std;
39 
40 struct RescanContext {
RescanContextRescanContext41     RescanContext(const hs_database_t *db_in, hs_scratch_t *scratch_in)
42         : db(db_in), scratch(scratch_in) {}
43     const hs_database_t *db;
44     hs_scratch_t *scratch;
45     size_t matches = 0;
46 };
47 
48 struct HyperscanDatabaseDeleter {
operator ()HyperscanDatabaseDeleter49     void operator()(hs_database_t *db) const {
50         hs_error_t err = hs_free_database(db);
51         EXPECT_EQ(HS_SUCCESS, err);
52     }
53 };
54 
55 unique_ptr<hs_database_t, HyperscanDatabaseDeleter>
makeDatabase(const char * expression,unsigned int flags,unsigned int mode)56 makeDatabase(const char *expression, unsigned int flags, unsigned int mode) {
57     hs_database_t *db = nullptr;
58     hs_compile_error_t *compile_err = nullptr;
59     hs_error_t err = hs_compile(expression, flags, mode, nullptr, &db,
60                                 &compile_err);
61     EXPECT_EQ(HS_SUCCESS, err);
62 
63     return unique_ptr<hs_database_t, HyperscanDatabaseDeleter>(db);
64 }
65 
66 // Generic block mode test that uses the given scan callback.
67 static
runBlockTest(match_event_handler cb_func)68 void runBlockTest(match_event_handler cb_func) {
69     auto db = makeDatabase("foo.*bar", 0, HS_MODE_BLOCK);
70     ASSERT_NE(nullptr, db.get());
71 
72     hs_scratch_t *scratch = nullptr;
73     hs_error_t err = hs_alloc_scratch(db.get(), &scratch);
74     ASSERT_EQ(HS_SUCCESS, err);
75     ASSERT_TRUE(scratch != nullptr);
76 
77     RescanContext rc(db.get(), scratch);
78     const string data = "___foo___bar_";
79 
80     err = hs_scan(db.get(), data.c_str(), data.length(), 0, scratch,
81                   cb_func, &rc);
82     ASSERT_EQ(HS_SUCCESS, err);
83     ASSERT_EQ(1, rc.matches);
84 
85     // teardown
86     err = hs_free_scratch(scratch);
87     ASSERT_EQ(HS_SUCCESS, err);
88 }
89 
90 // Generic streaming mode test that uses the given scan callback.
91 static
runStreamingTest(match_event_handler cb_func)92 void runStreamingTest(match_event_handler cb_func) {
93     auto db = makeDatabase("foo.*bar", 0, HS_MODE_STREAM);
94     ASSERT_NE(nullptr, db.get());
95 
96     hs_scratch_t *scratch = nullptr;
97     hs_error_t err = hs_alloc_scratch(db.get(), &scratch);
98     ASSERT_EQ(HS_SUCCESS, err);
99     ASSERT_TRUE(scratch != nullptr);
100 
101     hs_stream_t *stream = nullptr;
102     err = hs_open_stream(db.get(), 0, &stream);
103     ASSERT_EQ(HS_SUCCESS, err);
104     ASSERT_TRUE(stream != nullptr);
105 
106     RescanContext rc(db.get(), scratch);
107     const string data = "___foo___bar_";
108 
109     err = hs_scan_stream(stream, data.c_str(), data.length(), 0, scratch,
110                          cb_func, &rc);
111     ASSERT_EQ(HS_SUCCESS, err);
112     ASSERT_EQ(1, rc.matches);
113 
114     // teardown
115     hs_close_stream(stream, scratch, nullptr, nullptr);
116     err = hs_free_scratch(scratch);
117     ASSERT_EQ(HS_SUCCESS, err);
118 }
119 
120 // Generic vectored mode test that uses the given scan callback.
121 static
runVectoredTest(match_event_handler cb_func)122 void runVectoredTest(match_event_handler cb_func) {
123     auto db = makeDatabase("foo.*bar", 0, HS_MODE_VECTORED);
124     ASSERT_NE(nullptr, db.get());
125 
126     hs_scratch_t *scratch = nullptr;
127     hs_error_t err = hs_alloc_scratch(db.get(), &scratch);
128     ASSERT_EQ(HS_SUCCESS, err);
129     ASSERT_TRUE(scratch != nullptr);
130 
131     RescanContext rc(db.get(), scratch);
132     const string data1 = "___foo_";
133     const string data2 = "bar_";
134 
135     const char *vec[] = {data1.c_str(), data2.c_str()};
136     const unsigned int len[] = {unsigned(data1.length()),
137                                 unsigned(data2.length())};
138 
139     err = hs_scan_vector(db.get(), vec, len, 2, 0, scratch, cb_func, &rc);
140     ASSERT_EQ(HS_SUCCESS, err);
141     ASSERT_EQ(1, rc.matches);
142 
143     // teardown
144     err = hs_free_scratch(scratch);
145     ASSERT_EQ(HS_SUCCESS, err);
146 }
147 
148 static
rescan_block_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)149 int rescan_block_cb(unsigned, unsigned long long, unsigned long long, unsigned,
150                     void *ctx) {
151     RescanContext *rctx = (RescanContext *)ctx;
152     rctx->matches++;
153 
154     const string data = "___foo___bar_";
155 
156     hs_error_t err = hs_scan(rctx->db, data.c_str(), data.length(), 0,
157                              rctx->scratch, dummy_cb, nullptr);
158     EXPECT_EQ(HS_SCRATCH_IN_USE, err);
159     return 0;
160 }
161 
162 
163 // Attempt to use in-use scratch inside block mode callback.
TEST(ScratchInUse,Block)164 TEST(ScratchInUse, Block) {
165     runBlockTest(rescan_block_cb);
166 }
167 
168 static
rescan_stream_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)169 int rescan_stream_cb(unsigned, unsigned long long, unsigned long long, unsigned,
170                      void *ctx) {
171     RescanContext *rctx = (RescanContext *)ctx;
172     rctx->matches++;
173 
174     const string data = "___foo___bar_";
175 
176     hs_stream_t *stream = nullptr;
177     hs_error_t err = hs_open_stream(rctx->db, 0, &stream);
178     EXPECT_EQ(HS_SUCCESS, err);
179     EXPECT_TRUE(stream != nullptr);
180     if (stream == nullptr) {
181         return 1;
182     }
183 
184     err = hs_scan_stream(stream, data.c_str(), data.length(), 0,
185                          rctx->scratch, dummy_cb, nullptr);
186     EXPECT_EQ(HS_SCRATCH_IN_USE, err);
187 
188     hs_close_stream(stream, nullptr, nullptr, nullptr);
189     return 0;
190 }
191 
192 // Attempt to use in-use scratch inside streaming mode callback.
TEST(ScratchInUse,Streaming)193 TEST(ScratchInUse, Streaming) {
194     runStreamingTest(rescan_stream_cb);
195 }
196 
197 static
rescan_vector_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)198 int rescan_vector_cb(unsigned, unsigned long long, unsigned long long, unsigned,
199                     void *ctx) {
200     RescanContext *rctx = (RescanContext *)ctx;
201     rctx->matches++;
202 
203     const string data1 = "___foo_";
204     const string data2 = "bar_";
205 
206     const char *vec[] = {data1.c_str(), data2.c_str()};
207     const unsigned int len[] = {unsigned(data1.length()),
208                                 unsigned(data2.length())};
209 
210     hs_error_t err = hs_scan_vector(rctx->db, vec, len, 2, 0, rctx->scratch,
211                                     dummy_cb, nullptr);
212     EXPECT_EQ(HS_SCRATCH_IN_USE, err);
213     return 0;
214 }
215 
216 // Attempt to use in-use scratch inside vectored mode callback.
TEST(ScratchInUse,Vectored)217 TEST(ScratchInUse, Vectored) {
218     runVectoredTest(rescan_vector_cb);
219 }
220 
221 static
rescan_realloc_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)222 int rescan_realloc_cb(unsigned, unsigned long long, unsigned long long,
223                       unsigned, void *ctx) {
224     RescanContext *rctx = (RescanContext *)ctx;
225     rctx->matches++;
226 
227     auto db = makeDatabase("another db", 0, HS_MODE_BLOCK);
228     hs_error_t err = hs_alloc_scratch(db.get(), &rctx->scratch);
229     EXPECT_EQ(HS_SCRATCH_IN_USE, err);
230     return 0;
231 }
232 
233 // Attempt to use hs_alloc_scratch on in-use scratch inside callback (block
234 // scan).
TEST(ScratchInUse,ReallocScratchBlock)235 TEST(ScratchInUse, ReallocScratchBlock) {
236     runBlockTest(rescan_realloc_cb);
237 }
238 
239 // Attempt to use hs_alloc_scratch on in-use scratch inside callback (streaming
240 // scan).
TEST(ScratchInUse,ReallocScratchStreaming)241 TEST(ScratchInUse, ReallocScratchStreaming) {
242     runStreamingTest(rescan_realloc_cb);
243 }
244 
245 // Attempt to use hs_alloc_scratch on in-use scratch inside callback (vectored
246 // scan).
TEST(ScratchInUse,ReallocScratchVector)247 TEST(ScratchInUse, ReallocScratchVector) {
248     runVectoredTest(rescan_realloc_cb);
249 }
250 
251 static
rescan_free_cb(unsigned,unsigned long long,unsigned long long,unsigned,void * ctx)252 int rescan_free_cb(unsigned, unsigned long long, unsigned long long,
253                       unsigned, void *ctx) {
254     RescanContext *rctx = (RescanContext *)ctx;
255     rctx->matches++;
256 
257     hs_error_t err = hs_free_scratch(rctx->scratch);
258     EXPECT_EQ(HS_SCRATCH_IN_USE, err);
259     return 0;
260 }
261 
262 // Attempt to use hs_free_scratch on in-use scratch inside callback (block
263 // scan).
TEST(ScratchInUse,FreeScratchBlock)264 TEST(ScratchInUse, FreeScratchBlock) {
265     runBlockTest(rescan_free_cb);
266 }
267 
268 // Attempt to use hs_free_scratch on in-use scratch inside callback (streaming
269 // scan).
TEST(ScratchInUse,FreeScratchStreaming)270 TEST(ScratchInUse, FreeScratchStreaming) {
271     runStreamingTest(rescan_free_cb);
272 }
273 
274 // Attempt to use hs_free_scratch on in-use scratch inside callback (vectored
275 // scan).
TEST(ScratchInUse,FreeScratchVector)276 TEST(ScratchInUse, FreeScratchVector) {
277     runVectoredTest(rescan_free_cb);
278 }
279