Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock

Please unblock package hyperscan. The 4.4.1 minor update in unstable
fixes a bug that might be affecting users by delivering incorrect
matches. This could especially be undesirable in contexts where
Hyperscan is used as a matching engine in security relevant
applications. Intrusion detection/prevention systems come to mind,
such as Suricata which is also in Debian and makes use of libhyperscan4.

IMHO the update is suitable for stretch because the new version only
adds two tests and fixes a bug without introducing or retiring
features that could cause further breakage. It has also already been
suggested that the update could be suitable (see #858887 [1]).

If there are any more questions, please just let us know.

unblock hyperscan/4.4.1-1

Thanks
Sascha

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=858887#20
diff -Nru hyperscan-4.4.0/CHANGELOG.md hyperscan-4.4.1/CHANGELOG.md
--- hyperscan-4.4.0/CHANGELOG.md        2017-01-20 04:16:41.000000000 +0100
+++ hyperscan-4.4.1/CHANGELOG.md        2017-03-01 03:09:48.000000000 +0100
@@ -2,6 +2,12 @@
 
 This is a list of notable changes to Hyperscan, in reverse chronological order.
 
+## [4.4.1] 2017-02-28
+- Bugfixes to fix issues where stale data was being referenced in scratch
+  memory. In particular this may have resulted in hs_close_stream()
+  referencing data from other previously scanned streams. This may result in
+  incorrect matches being been reported.
+
 ## [4.4.0] 2017-01-20
 - Introduce the "fat runtime" build. This will build several variants of the
   Hyperscan scanning engine specialised for different processor feature sets,
@@ -136,7 +142,9 @@
   supplied with a NULL scratch pointer if no matches are required. This is in
   line with the behaviour of `hs_close_stream()`.
 - Disallow bounded repeats with a very large minimum repeat but no maximum,
-  i.e. {N,} for very large N.
+  i.e. {
+    N,
+} for very large N.
 - Reduce compile memory usage in literal set explansion for some large cases.
 
 ## [4.0.0] 2015-10-20
diff -Nru hyperscan-4.4.0/CMakeLists.txt hyperscan-4.4.1/CMakeLists.txt
--- hyperscan-4.4.0/CMakeLists.txt      2017-01-20 04:16:41.000000000 +0100
+++ hyperscan-4.4.1/CMakeLists.txt      2017-03-01 03:09:48.000000000 +0100
@@ -3,7 +3,7 @@
 
 set (HS_MAJOR_VERSION 4)
 set (HS_MINOR_VERSION 4)
-set (HS_PATCH_VERSION 0)
+set (HS_PATCH_VERSION 1)
 set (HS_VERSION ${HS_MAJOR_VERSION}.${HS_MINOR_VERSION}.${HS_PATCH_VERSION})
 
 set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
diff -Nru hyperscan-4.4.0/debian/changelog hyperscan-4.4.1/debian/changelog
--- hyperscan-4.4.0/debian/changelog    2017-01-20 10:08:50.000000000 +0100
+++ hyperscan-4.4.1/debian/changelog    2017-03-04 17:06:09.000000000 +0100
@@ -1,7 +1,16 @@
+hyperscan (4.4.1-1) unstable; urgency=medium
+
+  * Fix overlong changelog line to please lintian
+  * Remove explicit FAT_RUNTIME cmake directive
+  * New upstream version 4.4.1
+
+ -- Robert Haist <rha...@mailbox.org>  Sat, 04 Mar 2017 17:06:09 +0100
+
 hyperscan (4.4.0-1) unstable; urgency=medium
 
   [ Robert Haist ]
-  * Add brazilian / portuguese translations thanks to Adriano Rafael Gomes 
(Closes: #846528)
+  * Add brazilian / portuguese translations thanks to Adriano Rafael Gomes
+    (Closes: #846528)
   * New upstream version 4.4.0
 
   [ Sascha Steinbiss ]
diff -Nru hyperscan-4.4.0/debian/rules hyperscan-4.4.1/debian/rules
--- hyperscan-4.4.0/debian/rules        2017-01-20 10:08:50.000000000 +0100
+++ hyperscan-4.4.1/debian/rules        2017-03-04 17:06:09.000000000 +0100
@@ -15,8 +15,7 @@
 override_dh_auto_configure:
        dh_auto_configure -- \
                -DBUILD_STATIC_AND_SHARED=1 \
-               -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-               -DFAT_RUNTIME=1
+               -DCMAKE_BUILD_TYPE=RelWithDebInfo
 
 override_dh_install:
        dh_install --fail-missing
diff -Nru hyperscan-4.4.0/src/nfa/lbr.c hyperscan-4.4.1/src/nfa/lbr.c
--- hyperscan-4.4.0/src/nfa/lbr.c       2017-01-20 04:16:41.000000000 +0100
+++ hyperscan-4.4.1/src/nfa/lbr.c       2017-03-01 03:09:48.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, Intel Corporation
+ * Copyright (c) 2015-2017, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -77,6 +77,7 @@
 
     const struct RepeatInfo *info = getRepeatInfo(l);
     repeatUnpack(stream_state, info, offset, &lstate->ctrl);
+    lstate->lastEscape = 0;
 }
 
 static really_inline
diff -Nru hyperscan-4.4.0/src/nfa/limex_compile.cpp 
hyperscan-4.4.1/src/nfa/limex_compile.cpp
--- hyperscan-4.4.0/src/nfa/limex_compile.cpp   2017-01-20 04:16:41.000000000 
+0100
+++ hyperscan-4.4.1/src/nfa/limex_compile.cpp   2017-03-01 03:09:48.000000000 
+0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, Intel Corporation
+ * Copyright (c) 2015-2017, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -1803,6 +1803,12 @@
             assert(cyclic != NO_STATE);
             maskSetBit(limex->repeatCyclicMask, cyclic);
         }
+        /* also include tugs in repeat cyclic mask */
+        for (NFAVertex v : args.tugs) {
+            u32 v_state = args.state_ids.at(v);
+            assert(v_state != NO_STATE);
+            maskSetBit(limex->repeatCyclicMask, v_state);
+        }
     }
 
     static
diff -Nru hyperscan-4.4.0/src/nfa/limex_internal.h 
hyperscan-4.4.1/src/nfa/limex_internal.h
--- hyperscan-4.4.0/src/nfa/limex_internal.h    2017-01-20 04:16:41.000000000 
+0100
+++ hyperscan-4.4.1/src/nfa/limex_internal.h    2017-03-01 03:09:48.000000000 
+0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, Intel Corporation
+ * Copyright (c) 2015-2017, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -151,7 +151,7 @@
                                     *  followers */                         \
     u_##size compressMask; /**< switch off before compress */               \
     u_##size exceptionMask;                                                 \
-    u_##size repeatCyclicMask;                                              \
+    u_##size repeatCyclicMask; /**< also includes tug states */             \
     u_##size zombieMask; /**< zombie if in any of the set states */         \
     u_##size shift[MAX_SHIFT_COUNT];                                        \
     u32 shiftCount; /**< number of shift masks used */                      \
diff -Nru hyperscan-4.4.0/src/nfa/limex_runtime_impl.h 
hyperscan-4.4.1/src/nfa/limex_runtime_impl.h
--- hyperscan-4.4.0/src/nfa/limex_runtime_impl.h        2017-01-20 
04:16:41.000000000 +0100
+++ hyperscan-4.4.1/src/nfa/limex_runtime_impl.h        2017-03-01 
03:09:48.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, Intel Corporation
+ * Copyright (c) 2015-2017, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -393,19 +393,16 @@
         DEBUG_PRINTF("repeat %u\n", i);
         const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
 
-        if (!TESTBIT_STATE(s, info->cyclicState)) {
+        const ENG_STATE_T *tug_mask =
+            (const ENG_STATE_T *)((const char *)info + info->tugMaskOffset);
+        /* repeat may still be inspected if its tug state is on */
+        if (!TESTBIT_STATE(s, info->cyclicState)
+            && ISZERO_STATE(AND_STATE(s, LOAD_FROM_ENG(tug_mask)))) {
             DEBUG_PRINTF("is dead\n");
             continue;
         }
 
         const struct RepeatInfo *repeat = getRepeatInfo(info);
-        if (repeatHasMatch(repeat, &ctrl[i], state_base + info->stateOffset,
-                           offset) == REPEAT_STALE) {
-            DEBUG_PRINTF("is stale, clearing state\n");
-            CLEARBIT_STATE(&s, info->cyclicState);
-            continue;
-        }
-
         DEBUG_PRINTF("packing state (packedCtrlOffset=%u)\n",
                      info->packedCtrlOffset);
         repeatPack(state_base + info->packedCtrlOffset, repeat, &ctrl[i],
@@ -448,8 +445,11 @@
     for (u32 i = 0; i < limex->repeatCount; i++) {
         DEBUG_PRINTF("repeat %u\n", i);
         const struct NFARepeatInfo *info = GET_NFA_REPEAT_INFO_FN(limex, i);
+        const ENG_STATE_T *tug_mask =
+            (const ENG_STATE_T *)((const char *)info + info->tugMaskOffset);
 
-        if (!TESTBIT_STATE(cyclics, info->cyclicState)) {
+        if (!TESTBIT_STATE(cyclics, info->cyclicState)
+            && ISZERO_STATE(AND_STATE(cyclics, LOAD_FROM_ENG(tug_mask)))) {
             DEBUG_PRINTF("is dead\n");
             continue;
         }
diff -Nru hyperscan-4.4.0/src/nfa/repeat.c hyperscan-4.4.1/src/nfa/repeat.c
--- hyperscan-4.4.0/src/nfa/repeat.c    2017-01-20 04:16:41.000000000 +0100
+++ hyperscan-4.4.1/src/nfa/repeat.c    2017-03-01 03:09:48.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, Intel Corporation
+ * Copyright (c) 2015-2017, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -177,6 +177,10 @@
 
 u64a repeatLastTopBitmap(const union RepeatControl *ctrl) {
     const struct RepeatBitmapControl *xs = &ctrl->bitmap;
+    if (!xs->bitmap) {
+        /* last top was too long ago */
+        return 0;
+    }
     return xs->offset + 63 - clz64(xs->bitmap);
 }
 
diff -Nru hyperscan-4.4.0/src/rose/program_runtime.h 
hyperscan-4.4.1/src/rose/program_runtime.h
--- hyperscan-4.4.0/src/rose/program_runtime.h  2017-01-20 04:16:41.000000000 
+0100
+++ hyperscan-4.4.1/src/rose/program_runtime.h  2017-03-01 03:09:48.000000000 
+0100
@@ -1209,6 +1209,8 @@
 
     const u8 *aa = getActiveLeafArray(rose, scratch->core_info.state);
     const u32 aaCount = rose->activeArrayCount;
+    const u32 qCount = rose->queueCount;
+    struct fatbit *aqa = scratch->aqa;
 
     const struct mmbit_sparse_iter *it = getByOffset(rose, iter_offset);
     assert(ISALIGNED(it));
@@ -1221,6 +1223,10 @@
          qi = mmbit_sparse_iter_next(aa, aaCount, qi, &idx, it, si_state)) {
         DEBUG_PRINTF("checking nfa %u\n", qi);
         struct mq *q = scratch->queues + qi;
+        if (!fatbit_set(aqa, qCount, qi)) {
+            initQueue(q, qi, rose, scratch);
+        }
+
         assert(q->nfa == getNfaByQueue(rose, qi));
         assert(nfaAcceptsEod(q->nfa));
 
diff -Nru hyperscan-4.4.0/src/runtime.c hyperscan-4.4.1/src/runtime.c
--- hyperscan-4.4.0/src/runtime.c       2017-01-20 04:16:41.000000000 +0100
+++ hyperscan-4.4.1/src/runtime.c       2017-03-01 03:09:48.000000000 +0100
@@ -187,6 +187,18 @@
 }
 
 static really_inline
+void pureLiteralInitScratch(struct hs_scratch *scratch, u64a offset) {
+    // Some init has already been done.
+    assert(offset == scratch->core_info.buf_offset);
+
+    scratch->tctxt.lit_offset_adjust = offset + 1;
+    scratch->tctxt.lastEndOffset = offset;
+    scratch->tctxt.delayLastEndOffset = offset;
+    scratch->tctxt.filledDelayedSlots = 0;
+    scratch->al_log_sum = 0;
+}
+
+static really_inline
 void pureLiteralBlockExec(const struct RoseEngine *rose,
                           struct hs_scratch *scratch) {
     assert(rose);
@@ -198,9 +210,8 @@
     size_t length = scratch->core_info.len;
     DEBUG_PRINTF("rose engine %d\n", rose->runtimeImpl);
 
-    // RoseContext values that need to be set for use by roseCallback.
+    pureLiteralInitScratch(scratch, 0);
     scratch->tctxt.groups = rose->initialGroups;
-    scratch->tctxt.lit_offset_adjust = 1;
 
     hwlmExec(ftable, buffer, length, 0, roseCallback, scratch,
              rose->initialGroups);
@@ -743,9 +754,8 @@
     DEBUG_PRINTF("::: streaming rose ::: offset = %llu len = %zu\n",
                  stream_state->offset, scratch->core_info.len);
 
-    // RoseContext values that need to be set for use by roseCallback.
+    pureLiteralInitScratch(scratch, stream_state->offset);
     scratch->tctxt.groups = loadGroups(rose, scratch->core_info.state);
-    scratch->tctxt.lit_offset_adjust = scratch->core_info.buf_offset + 1;
 
     // Pure literal cases don't have floatingMinDistance set, so we always
     // start the match region at zero.
diff -Nru hyperscan-4.4.0/unit/hyperscan/behaviour.cpp 
hyperscan-4.4.1/unit/hyperscan/behaviour.cpp
--- hyperscan-4.4.0/unit/hyperscan/behaviour.cpp        2017-01-20 
04:16:41.000000000 +0100
+++ hyperscan-4.4.1/unit/hyperscan/behaviour.cpp        2017-03-01 
03:09:48.000000000 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, Intel Corporation
+ * Copyright (c) 2015-2017, Intel Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -1166,6 +1166,106 @@
     ASSERT_EQ(HS_SUCCESS, err);
     EXPECT_EQ(1U, matchCount);
 
+    // teardown
+    err = hs_free_scratch(scratch);
+    ASSERT_EQ(HS_SUCCESS, err);
+    hs_free_database(db);
+}
+
+TEST(HyperscanTestBehaviour, MultiStream1) {
+    hs_error_t err;
+
+    // build a database
+    hs_database_t *db = buildDB("foo.*bar.*\\b", 0, 0, HS_MODE_STREAM);
+    ASSERT_TRUE(db != nullptr);
+
+    // alloc some scratch
+    hs_scratch_t *scratch = nullptr;
+    err = hs_alloc_scratch(db, &scratch);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_TRUE(scratch != nullptr);
+
+    hs_stream_t *stream = nullptr;
+    err = hs_open_stream(db, 0, &stream);
+    ASSERT_EQ(HS_SUCCESS, err);
+    ASSERT_TRUE(stream != nullptr);
+
+    hs_stream_t *stream2 = nullptr;
+    err = hs_open_stream(db, 0, &stream2);
+    ASSERT_EQ(HS_SUCCESS, err);
+    ASSERT_TRUE(stream2 != nullptr);
+
+    matchCount = 0;
+    const string data("foo        bara");
+    err = hs_scan_stream(stream, data.c_str(), data.size(), 0, scratch,
+                         countHandler, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(0U, matchCount); // hasn't matched until stream end
+
+    const string data2("foo        bar ");
+    err = hs_scan_stream(stream2, data2.c_str(), data2.size(), 0, scratch,
+                         nullptr, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(0U, matchCount);
+
+    err = hs_close_stream(stream, scratch, countHandler, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(1U, matchCount);
+
+    err = hs_close_stream(stream2, scratch, countHandler, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(1U, matchCount);
+
+    // teardown
+    err = hs_free_scratch(scratch);
+    ASSERT_EQ(HS_SUCCESS, err);
+    hs_free_database(db);
+}
+
+TEST(HyperscanTestBehaviour, MultiStream2) {
+    hs_error_t err;
+
+    // build a database
+    hs_database_t *db = buildDB("foo.*bar.*\\b", 0, 0, HS_MODE_STREAM);
+    ASSERT_TRUE(db != nullptr);
+
+    // alloc some scratch
+    hs_scratch_t *scratch = nullptr;
+    err = hs_alloc_scratch(db, &scratch);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_TRUE(scratch != nullptr);
+
+    hs_stream_t *stream = nullptr;
+    err = hs_open_stream(db, 0, &stream);
+    ASSERT_EQ(HS_SUCCESS, err);
+    ASSERT_TRUE(stream != nullptr);
+
+    hs_stream_t *stream2 = nullptr;
+    err = hs_open_stream(db, 0, &stream2);
+    ASSERT_EQ(HS_SUCCESS, err);
+    ASSERT_TRUE(stream2 != nullptr);
+
+    matchCount = 0;
+    const string data2("foo        bar ");
+    err = hs_scan_stream(stream2, data2.c_str(), data2.size(), 0, scratch,
+                         nullptr, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(0U, matchCount);
+
+    const string data("foo        bara");
+    err = hs_scan_stream(stream, data.c_str(), data.size(), 0, scratch,
+                         countHandler, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(0U, matchCount); // hasn't matched until stream end
+
+    err = hs_close_stream(stream2, scratch, countHandler, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(0U, matchCount);
+
+    err = hs_close_stream(stream, scratch, countHandler, nullptr);
+    ASSERT_EQ(HS_SUCCESS, err);
+    EXPECT_EQ(1U, matchCount);
+
     // teardown
     err = hs_free_scratch(scratch);
     ASSERT_EQ(HS_SUCCESS, err);

Reply via email to