From 1449bc48a4b7f7dc10aea0090bb1d6b843c011c2 Mon Sep 17 00:00:00 2001
From: Victor Julien <victor@inliniac.net>
Date: Sat, 6 Feb 2021 11:31:03 +0100
Subject: [PATCH] TEST mpm: honor endswith

---
 src/detect-engine-mpm.c |  4 ++++
 src/util-hyperscan.c    | 30 ++++++++++++++++++++++++++++++
 src/util-hyperscan.h    |  1 +
 src/util-mpm-hs.c       |  6 +++++-
 src/util-mpm.h          |  1 +
 5 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c
index fe1549789c..4b96b9520c 100644
--- a/src/detect-engine-mpm.c
+++ b/src/detect-engine-mpm.c
@@ -737,6 +737,10 @@ static void PopulateMpmHelperAddPattern(MpmCtx *mpm_ctx,
         SCLogDebug("Negated MPM");
         flags |= MPM_PATTERN_FLAG_NEGATED;
     }
+    if (cd->flags & DETECT_CONTENT_ENDS_WITH) {
+        SCLogDebug("Negated MPM");
+        flags |= MPM_PATTERN_FLAG_ENDSWITH;
+    }
 
     /* We have to effectively "wild card" values that will be coming from
      * byte_extract variables
diff --git a/src/util-hyperscan.c b/src/util-hyperscan.c
index c5886d6691..0f74920d7e 100644
--- a/src/util-hyperscan.c
+++ b/src/util-hyperscan.c
@@ -57,4 +57,34 @@ char *HSRenderPattern(const uint8_t *pat, uint16_t pat_len)
     return str;
 }
 
+/**
+ * \internal
+ * \brief Convert a pattern into a regex string accepted by the Hyperscan
+ * compiler.
+ *
+ * For simplicity, we just take each byte of the original pattern and render it
+ * with a hex escape (i.e. ' ' -> "\x20")/
+ */
+char *HSRenderPatternAnchorToEnd(const uint8_t *pat, uint16_t pat_len)
+{
+    if (pat == NULL) {
+        return NULL;
+    }
+    const size_t hex_len = (pat_len * 4) + 1 + 1;
+    char *str = SCMalloc(hex_len);
+    if (str == NULL) {
+        return NULL;
+    }
+    memset(str, 0, hex_len);
+    char *sp = str;
+    for (uint16_t i = 0; i < pat_len; i++) {
+        snprintf(sp, 5, "\\x%02x", pat[i]);
+        sp += 4;
+    }
+    *sp = '$';
+    sp++;
+    *sp = '\0';
+    return str;
+}
+
 #endif /* BUILD_HYPERSCAN */
diff --git a/src/util-hyperscan.h b/src/util-hyperscan.h
index 67bcabf49d..bdcd162ae8 100644
--- a/src/util-hyperscan.h
+++ b/src/util-hyperscan.h
@@ -27,5 +27,6 @@
 #define __UTIL_HYPERSCAN__H__
 
 char *HSRenderPattern(const uint8_t *pat, uint16_t pat_len);
+char *HSRenderPatternAnchorToEnd(const uint8_t *pat, uint16_t pat_len);
 
 #endif /* __UTIL_HYPERSCAN__H__ */
diff --git a/src/util-mpm-hs.c b/src/util-mpm-hs.c
index 425a22fc0d..f76097e795 100644
--- a/src/util-mpm-hs.c
+++ b/src/util-mpm-hs.c
@@ -681,7 +681,11 @@ int SCHSPreparePatterns(MpmCtx *mpm_ctx)
             cd->flags[i] |= HS_FLAG_CASELESS;
         }
 
-        cd->expressions[i] = HSRenderPattern(p->original_pat, p->len);
+        if (p->flags & MPM_PATTERN_FLAG_ENDSWITH) {
+            cd->expressions[i] = HSRenderPatternAnchorToEnd(p->original_pat, p->len);
+        }
+        else
+            cd->expressions[i] = HSRenderPattern(p->original_pat, p->len);
 
         if (p->flags & (MPM_PATTERN_FLAG_OFFSET | MPM_PATTERN_FLAG_DEPTH)) {
             cd->ext[i] = SCMalloc(sizeof(hs_expr_ext_t));
diff --git a/src/util-mpm.h b/src/util-mpm.h
index c94822bb99..7276795f1a 100644
--- a/src/util-mpm.h
+++ b/src/util-mpm.h
@@ -143,6 +143,7 @@ typedef struct MpmCtxFactoryContainer_ {
 /** the ctx uses it's own internal id instead of
  *  what is passed through the API */
 #define MPM_PATTERN_CTX_OWNS_ID     0x20
+#define MPM_PATTERN_FLAG_ENDSWITH   0x40
 
 typedef struct MpmTableElmt_ {
     const char *name;
-- 
2.43.0

