]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
lua: track memory limit exceede errors
authorJason Ish <jason.ish@oisf.net>
Mon, 27 May 2024 21:37:17 +0000 (15:37 -0600)
committerJason Ish <jason.ish@oisf.net>
Mon, 27 May 2024 22:44:54 +0000 (16:44 -0600)
Update the Lua allocated to set a code on memory allocation limit
exceeded errors so an appropriate error message can be logged and a
state incremented.

Fixes the tracking of the allocated size by using the difference
between original size, and new size and toss in some debug
validations.

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

index 7c0e9afcef9956903862b71d392292bad769eff5..2f6721b1eacdc9fb985367e98a206f8068df1aef 100644 (file)
                                     "description": "Count of Lua rules exceeding the instruction limit",
                                     "type": "integer"
                                 },
+                                "memory_limit_errors": {
+                                    "description": "Count of Lua rules exceeding the memory limit",
+                                    "type": "integer"
+                                },
                                 "errors": {
                                     "description": "Errors encountered while running Lua scripts",
                                     "type": "integer"
index b5b36703a32773a118b26ce5dcce9809f80446b4..4f66560fa85fe5bb95c15ad59de0bb734446464d 100644 (file)
@@ -3343,6 +3343,9 @@ TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
     det_ctx->lua_instruction_limit_errors =
             StatsRegisterCounter("detect.lua.instruction_limit_errors", tv);
 
+    /* Register a counter for Lua memory limit errors. */
+    det_ctx->lua_memory_limit_errors = StatsRegisterCounter("detect.lua.memory_limit_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 8f6907814e03b39597dc04d8c0a012b99037cb25..ffc5362054183dc1fd4a3dd3b2619fdaf65c2542 100644 (file)
@@ -125,6 +125,7 @@ void DetectLuaRegister(void)
 #define FLAG_ERROR_LOGGED                       BIT_U32(23)
 #define FLAG_BLOCKED_FUNCTION_LOGGED            BIT_U32(24)
 #define FLAG_INSTRUCTION_LIMIT_LOGGED           BIT_U32(25)
+#define FLAG_MEMORY_LIMIT_LOGGED                BIT_U32(26)
 
 #define DEFAULT_LUA_ALLOC_LIMIT       500000
 #define DEFAULT_LUA_INSTRUCTION_LIMIT 500000
@@ -177,6 +178,7 @@ static int DetectLuaRunMatch(
     SCLuaSbResetInstructionCounter(tlua->luastate);
 
     if (lua_pcall(tlua->luastate, 1, 1, 0) != 0) {
+        const char *reason = lua_tostring(tlua->luastate, -1);
         SCLuaSbState *context = SCLuaSbGetContext(tlua->luastate);
         uint32_t flag = 0;
         if (context->blocked_function_error) {
@@ -185,6 +187,10 @@ static int DetectLuaRunMatch(
         } else if (context->instruction_count_error) {
             StatsIncr(det_ctx->tv, det_ctx->lua_instruction_limit_errors);
             flag = FLAG_INSTRUCTION_LIMIT_LOGGED;
+        } else if (context->memory_limit_error) {
+            StatsIncr(det_ctx->tv, det_ctx->lua_memory_limit_errors);
+            reason = "memory limit exceeded";
+            flag = FLAG_MEMORY_LIMIT_LOGGED;
         } else {
             flag = FLAG_ERROR_LOGGED;
         }
@@ -192,8 +198,7 @@ static int DetectLuaRunMatch(
         /* Log once per thread per error type, the message from Lua
          * will include the filename. */
         if (!(tlua->flags & flag)) {
-            SCLogWarning(
-                    "Lua script failed to run successfully: %s", lua_tostring(tlua->luastate, -1));
+            SCLogWarning("Lua script failed to run successfully: %s", reason);
             tlua->flags |= flag;
         }
 
index 49570b7317eb3dd2d01bb65ff6af44f12f0882e5..2e30e19ec52d7a9f91bc106fd9b7b199be37415e 100644 (file)
@@ -1245,6 +1245,9 @@ typedef struct DetectEngineThreadCtx_ {
     /** stats if for lua instruction limit errors */
     uint16_t lua_instruction_limit_errors;
 
+    /** stat of lua memory limit errors. */
+    uint16_t lua_memory_limit_errors;
+
 #ifdef DEBUG
     uint64_t pkt_stream_add_cnt;
     uint64_t payload_mpm_cnt;
index 192aacedc4bfc478d9a32b28c6b6ca13d22d59ba..c3596f97c54381070a660b5b329f26d8e328800a 100644 (file)
@@ -49,26 +49,41 @@ static void HookFunc(lua_State *L, lua_Debug *ar);
 static void *LuaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
 {
     (void)ud;
-    (void)osize; /* not used */
+    (void)osize;
     SCLuaSbState *ctx = (SCLuaSbState *)ud;
+
     if (nsize == 0) {
-        if (ptr != NULL) {
-            // ASSERT: alloc_bytes > osize
-            DEBUG_VALIDATE_BUG_ON(ctx->alloc_bytes < osize);
-            ctx->alloc_bytes -= osize;
+        if (ptr == NULL) {
+            /* This happens, ignore. */
+            return NULL;
         }
+        BUG_ON(osize > ctx->alloc_bytes);
         SCFree(ptr);
+        ctx->alloc_bytes -= osize;
         return NULL;
+    } else if (ptr == NULL) {
+        /* Allocating new data. */
+        void *nptr = SCRealloc(ptr, nsize);
+        if (nptr != NULL) {
+            ctx->alloc_bytes += nsize;
+        }
+        return nptr;
     } else {
-        // We can be a bit sloppy on the alloc limit since it's not supposed to be hit.
-        //  ASSERT: ctx->alloc_bytes + nsize > ctx->alloc_bytes
-        if (ctx->alloc_bytes + nsize > ctx->alloc_limit) {
-            // TODO: Trace in a better way
+        /* Resizing existing data. */
+        ssize_t diff = nsize - osize;
+
+        if (ctx->alloc_bytes + diff > ctx->alloc_limit) {
+            /* This request will exceed the allocation limit. Act as
+             * though allocation failed. */
+            ctx->memory_limit_error = true;
             return NULL;
         }
+
         void *nptr = SCRealloc(ptr, nsize);
         if (nptr != NULL) {
-            ctx->alloc_bytes += nsize;
+            BUG_ON((ssize_t)ctx->alloc_bytes + diff < 0);
+            BUG_ON(osize > ctx->alloc_bytes);
+            ctx->alloc_bytes += diff;
         }
         return nptr;
     }
@@ -298,7 +313,7 @@ lua_State *SCLuaSbStateNew(uint64_t alloclimit, uint64_t instructionlimit)
     sb->hook_instruction_count = 100;
     sb->instruction_limit = instructionlimit;
 
-    sb->L = lua_newstate(LuaAlloc, sb); /* create state */
+    sb->L = lua_newstate(LuaAlloc, sb);
     if (sb->L == NULL) {
         SCFree(sb);
         return NULL;
@@ -331,6 +346,7 @@ void SCLuaSbStateClose(lua_State *L)
 {
     SCLuaSbState *sb = SCLuaSbGetContext(L);
     lua_close(sb->L);
+    BUG_ON(sb->alloc_bytes);
     SCFree(sb);
 }
 
index f16604933021fa6f3f62582552c0346d4eccfa3f..b04ab746d230a2e7825f4b0e691be1b647790495 100644 (file)
@@ -41,7 +41,7 @@ typedef struct SCLuaSbState {
     lua_State *L;
 
     /* Allocation limits */
-    uint64_t alloc_bytes;
+    size_t alloc_bytes;
     uint64_t alloc_limit;
 
     /* Execution Limits */
@@ -52,6 +52,7 @@ typedef struct SCLuaSbState {
     /* Errors. */
     bool blocked_function_error;
     bool instruction_count_error;
+    bool memory_limit_error;
 } SCLuaSbState;
 
 /*