]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/lua: don't treat a crashed script as no match
authorJason Ish <jason.ish@oisf.net>
Tue, 7 May 2024 17:09:30 +0000 (11:09 -0600)
committerVictor Julien <victor@inliniac.net>
Thu, 16 May 2024 17:58:36 +0000 (19:58 +0200)
If a rule script crashed, the return value was treated as a no
match. This would make a negation of the rule match and alert.

Instead cleanup and exit early if the rule script crashed and don't
run negation logic.

A stat, detect.lua.errors has been added to count how many times a
script crashes.

Also consolidates the running of the Lua script and return value
handling to a common function.

Bug: #6940

etc/schema.json
src/detect-engine.c
src/detect-lua.c
src/detect.h

index 77597135ee38702db5ce280e94fd234979124afc..5494878a968ea1914558e54309d5f29bc17db5c6 100644 (file)
                         "alerts_suppressed": {
                             "type": "integer"
                         },
+                        "lua": {
+                            "type": "object",
+                            "properties": {
+                                "errors": {
+                                    "description": "Errors encountered while running Lua scripts",
+                                    "type": "integer"
+                                }
+                            },
+                            "additionalProperties": false
+                        },
                         "mpm_list": {
                             "type": "integer"
                         },
index 21ce4906f58b20c8abdb19e0e89cc39927b94801..d23edb80d2bfa05f34a15899029fb444e86c3d1f 100644 (file)
@@ -3276,6 +3276,10 @@ TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
     det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", tv);
     det_ctx->counter_alerts_overflow = StatsRegisterCounter("detect.alert_queue_overflow", tv);
     det_ctx->counter_alerts_suppressed = StatsRegisterCounter("detect.alerts_suppressed", tv);
+
+    /* Register counter for Lua rule errors. */
+    det_ctx->lua_rule_errors = StatsRegisterCounter("detect.lua.errors", tv);
+
 #ifdef PROFILING
     det_ctx->counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", tv);
     det_ctx->counter_nonmpm_list = StatsRegisterAvgCounter("detect.nonmpm_list", tv);
index 93f4a687f87d88341ec38e3355401e5e915b099f..8fa919a8ca2815904a6078c6136605e403ffbaa8 100644 (file)
@@ -165,6 +165,8 @@ void DetectLuaRegister(void)
 
 #define DATATYPE_BUFFER BIT_U32(22)
 
+#define ERROR_LOGGED BIT_U32(23)
+
 #if 0
 /** \brief dump stack from lua state to screen */
 void LuaDumpStack(lua_State *state)
@@ -202,44 +204,30 @@ void LuaDumpStack(lua_State *state)
 }
 #endif
 
-int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx,
-        const Signature *s, const SigMatchData *smd,
-        const uint8_t *buffer, uint32_t buffer_len, uint32_t offset,
-        Flow *f)
+/**
+ * \brief Common function to run the Lua match function and process
+ *     the return value.
+ */
+static int DetectLuaRunMatch(
+        DetectEngineThreadCtx *det_ctx, const DetectLuaData *lua, DetectLuaThreadData *tlua)
 {
-    SCEnter();
-    int ret = 0;
-
-    if (buffer == NULL || buffer_len == 0)
-        SCReturnInt(0);
-
-    DetectLuaData *lua = (DetectLuaData *)smd->ctx;
-    if (lua == NULL)
-        SCReturnInt(0);
-
-    DetectLuaThreadData *tlua = (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, lua->thread_ctx_id);
-    if (tlua == NULL)
+    if (lua_pcall(tlua->luastate, 1, 1, 0) != 0) {
+        if (!(tlua->flags & ERROR_LOGGED)) {
+            /* Log once per thread, the message from Lua will include
+             * the filename. */
+            SCLogWarning(
+                    "Lua script failed to run successfully: %s", lua_tostring(tlua->luastate, -1));
+            tlua->flags |= ERROR_LOGGED;
+        }
+        StatsIncr(det_ctx->tv, det_ctx->lua_rule_errors);
+        while (lua_gettop(tlua->luastate) > 0) {
+            lua_pop(tlua->luastate, 1);
+        }
         SCReturnInt(0);
-
-    LuaExtensionsMatchSetup(tlua->luastate, lua, det_ctx, f, /* no packet in the ctx */ NULL, s, 0);
-
-    /* prepare data to pass to script */
-    lua_getglobal(tlua->luastate, "match");
-    lua_newtable(tlua->luastate); /* stack at -1 */
-
-    lua_pushliteral (tlua->luastate, "offset"); /* stack at -2 */
-    lua_pushnumber (tlua->luastate, (int)(offset + 1));
-    lua_settable(tlua->luastate, -3);
-
-    lua_pushstring (tlua->luastate, lua->buffername); /* stack at -2 */
-    LuaPushStringBuffer(tlua->luastate, (const uint8_t *)buffer, (size_t)buffer_len);
-    lua_settable(tlua->luastate, -3);
-
-    int retval = lua_pcall(tlua->luastate, 1, 1, 0);
-    if (retval != 0) {
-        SCLogInfo("failed to run script: %s", lua_tostring(tlua->luastate, -1));
     }
 
+    int match = 0;
+
     /* process returns from script */
     if (lua_gettop(tlua->luastate) > 0) {
         /* script returns a number (return 1 or return 0) */
@@ -249,7 +237,7 @@ int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx,
             lua_pop(tlua->luastate, 1);
 
             if (script_ret == 1.0)
-                ret = 1;
+                match = 1;
 
         /* script returns a table */
         } else if (lua_type(tlua->luastate, 1) == LUA_TTABLE) {
@@ -271,10 +259,10 @@ int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx,
                         SCLogError("Invalid value "
                                    "for \"retval\" from LUA return table: '%s'",
                                 v);
-                        ret = 0;
+                        match = 0;
                     }
                     else if (val == 1) {
-                        ret = 1;
+                        match = 1;
                     }
                 } else {
                     /* set flow var? */
@@ -284,23 +272,55 @@ int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx,
             /* pop the table */
             lua_pop(tlua->luastate, 1);
         }
-    } else {
-        SCLogDebug("no stack");
     }
 
-    /* clear the stack */
+    if (lua->negated) {
+        if (match == 1)
+            match = 0;
+        else
+            match = 1;
+    }
+
     while (lua_gettop(tlua->luastate) > 0) {
         lua_pop(tlua->luastate, 1);
     }
 
-    if (lua->negated) {
-        if (ret == 1)
-            ret = 0;
-        else
-            ret = 1;
-    }
+    SCReturnInt(match);
+}
 
-    SCReturnInt(ret);
+int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx, const Signature *s,
+        const SigMatchData *smd, const uint8_t *buffer, uint32_t buffer_len, uint32_t offset,
+        Flow *f)
+{
+    SCEnter();
+
+    if (buffer == NULL || buffer_len == 0)
+        SCReturnInt(0);
+
+    DetectLuaData *lua = (DetectLuaData *)smd->ctx;
+    if (lua == NULL)
+        SCReturnInt(0);
+
+    DetectLuaThreadData *tlua =
+            (DetectLuaThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, lua->thread_ctx_id);
+    if (tlua == NULL)
+        SCReturnInt(0);
+
+    LuaExtensionsMatchSetup(tlua->luastate, lua, det_ctx, f, /* no packet in the ctx */ NULL, s, 0);
+
+    /* prepare data to pass to script */
+    lua_getglobal(tlua->luastate, "match");
+    lua_newtable(tlua->luastate); /* stack at -1 */
+
+    lua_pushliteral(tlua->luastate, "offset"); /* stack at -2 */
+    lua_pushnumber(tlua->luastate, (int)(offset + 1));
+    lua_settable(tlua->luastate, -3);
+
+    lua_pushstring(tlua->luastate, lua->buffername); /* stack at -2 */
+    LuaPushStringBuffer(tlua->luastate, (const uint8_t *)buffer, (size_t)buffer_len);
+    lua_settable(tlua->luastate, -3);
+
+    SCReturnInt(DetectLuaRunMatch(det_ctx, lua, tlua));
 }
 
 /**
@@ -319,7 +339,6 @@ static int DetectLuaMatch (DetectEngineThreadCtx *det_ctx,
         Packet *p, const Signature *s, const SigMatchCtx *ctx)
 {
     SCEnter();
-    int ret = 0;
     DetectLuaData *lua = (DetectLuaData *)ctx;
     if (lua == NULL)
         SCReturnInt(0);
@@ -389,70 +408,7 @@ static int DetectLuaMatch (DetectEngineThreadCtx *det_ctx,
         }
     }
 
-    int retval = lua_pcall(tlua->luastate, 1, 1, 0);
-    if (retval != 0) {
-        SCLogInfo("failed to run script: %s", lua_tostring(tlua->luastate, -1));
-    }
-
-    /* process returns from script */
-    if (lua_gettop(tlua->luastate) > 0) {
-
-        /* script returns a number (return 1 or return 0) */
-        if (lua_type(tlua->luastate, 1) == LUA_TNUMBER) {
-            double script_ret = lua_tonumber(tlua->luastate, 1);
-            SCLogDebug("script_ret %f", script_ret);
-            lua_pop(tlua->luastate, 1);
-
-            if (script_ret == 1.0)
-                ret = 1;
-
-        /* script returns a table */
-        } else if (lua_type(tlua->luastate, 1) == LUA_TTABLE) {
-            lua_pushnil(tlua->luastate);
-            const char *k, *v;
-            while (lua_next(tlua->luastate, -2)) {
-                v = lua_tostring(tlua->luastate, -1);
-                lua_pop(tlua->luastate, 1);
-                k = lua_tostring(tlua->luastate, -1);
-
-                if (!k || !v)
-                    continue;
-
-                SCLogDebug("k='%s', v='%s'", k, v);
-
-                if (strcmp(k, "retval") == 0) {
-                    int val;
-                    if (StringParseInt32(&val, 10, 0,
-                                         (const char *)v) < 0) {
-                        SCLogError("Invalid value "
-                                   "for \"retval\" from LUA return table: '%s'",
-                                v);
-                        ret = 0;
-                    }
-                    else if (val == 1) {
-                        ret = 1;
-                    }
-                } else {
-                    /* set flow var? */
-                }
-            }
-
-            /* pop the table */
-            lua_pop(tlua->luastate, 1);
-        }
-    }
-    while (lua_gettop(tlua->luastate) > 0) {
-        lua_pop(tlua->luastate, 1);
-    }
-
-    if (lua->negated) {
-        if (ret == 1)
-            ret = 0;
-        else
-            ret = 1;
-    }
-
-    SCReturnInt(ret);
+    SCReturnInt(DetectLuaRunMatch(det_ctx, lua, tlua));
 }
 
 static int DetectLuaAppMatchCommon (DetectEngineThreadCtx *det_ctx,
@@ -460,7 +416,6 @@ static int DetectLuaAppMatchCommon (DetectEngineThreadCtx *det_ctx,
         const Signature *s, const SigMatchCtx *ctx)
 {
     SCEnter();
-    int ret = 0;
     DetectLuaData *lua = (DetectLuaData *)ctx;
     if (lua == NULL)
         SCReturnInt(0);
@@ -499,70 +454,7 @@ static int DetectLuaAppMatchCommon (DetectEngineThreadCtx *det_ctx,
         }
     }
 
-    int retval = lua_pcall(tlua->luastate, 1, 1, 0);
-    if (retval != 0) {
-        SCLogInfo("failed to run script: %s", lua_tostring(tlua->luastate, -1));
-    }
-
-    /* process returns from script */
-    if (lua_gettop(tlua->luastate) > 0) {
-
-        /* script returns a number (return 1 or return 0) */
-        if (lua_type(tlua->luastate, 1) == LUA_TNUMBER) {
-            double script_ret = lua_tonumber(tlua->luastate, 1);
-            SCLogDebug("script_ret %f", script_ret);
-            lua_pop(tlua->luastate, 1);
-
-            if (script_ret == 1.0)
-                ret = 1;
-
-        /* script returns a table */
-        } else if (lua_type(tlua->luastate, 1) == LUA_TTABLE) {
-            lua_pushnil(tlua->luastate);
-            const char *k, *v;
-            while (lua_next(tlua->luastate, -2)) {
-                v = lua_tostring(tlua->luastate, -1);
-                lua_pop(tlua->luastate, 1);
-                k = lua_tostring(tlua->luastate, -1);
-
-                if (!k || !v)
-                    continue;
-
-                SCLogDebug("k='%s', v='%s'", k, v);
-
-                if (strcmp(k, "retval") == 0) {
-                    int val;
-                    if (StringParseInt32(&val, 10, 0,
-                                         (const char *)v) < 0) {
-                        SCLogError("Invalid value "
-                                   "for \"retval\" from LUA return table: '%s'",
-                                v);
-                        ret = 0;
-                    }
-                    else if (val == 1) {
-                        ret = 1;
-                    }
-                } else {
-                    /* set flow var? */
-                }
-            }
-
-            /* pop the table */
-            lua_pop(tlua->luastate, 1);
-        }
-    }
-    while (lua_gettop(tlua->luastate) > 0) {
-        lua_pop(tlua->luastate, 1);
-    }
-
-    if (lua->negated) {
-        if (ret == 1)
-            ret = 0;
-        else
-            ret = 1;
-    }
-
-    SCReturnInt(ret);
+    SCReturnInt(DetectLuaRunMatch(det_ctx, lua, tlua));
 }
 
 /**
index 52b456318969273245b70f5a7b31374ac2466f98..e9576c0310db3182fda36ca361e5b24792fc5461 100644 (file)
@@ -1227,6 +1227,9 @@ typedef struct DetectEngineThreadCtx_ {
     AppLayerDecoderEvents *decoder_events;
     uint16_t events;
 
+    /** stats id for lua rule errors */
+    uint16_t lua_rule_errors;
+
 #ifdef DEBUG
     uint64_t pkt_stream_add_cnt;
     uint64_t payload_mpm_cnt;