• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22550902417

01 Mar 2026 07:32PM UTC coverage: 68.401% (-5.3%) from 73.687%
22550902417

Pull #14922

github

web-flow
github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #14922: github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

218243 of 319063 relevant lines covered (68.4%)

3284926.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.92
/src/util-lua-sandbox.c
1
/* Copyright (C) 2023-2024 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 * \author Jo Johnson <pyrojoe314@gmail.com>
22
 */
23

24
#include "suricata-common.h"
25

26
#include "lua.h"
27
#include "lauxlib.h"
28
#include "lualib.h"
29
#include "util-debug.h"
30

31
#include "util-debug.h"
32
#include "util-lua-sandbox.h"
33
#include "util-lua-builtins.h"
34
#include "util-validate.h"
35

36
#define SANDBOX_CTX "SANDBOX_CTX"
168✔
37

38
static void HookFunc(lua_State *L, lua_Debug *ar);
39

40
/**
41
 * Lua allocator function provided to lua_newstate.
42
 *
43
 * \param ud The pointer passed to lua_newstate
44
 * \param ptr Pointer to data being allocated/reallocated/freed
45
 * \param osize Original size of the block
46
 * \param nsize Size of the new block
47
 *
48
 * See: https://www.lua.org/manual/5.4/manual.html#lua_Alloc
49
 */
50
static void *LuaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
51
{
14,228✔
52
    (void)ud;
14,228✔
53
    (void)osize;
14,228✔
54
    SCLuaSbState *ctx = (SCLuaSbState *)ud;
14,228✔
55

56
    if (nsize == 0) {
14,228✔
57
        if (ptr == NULL) {
7,216✔
58
            /* This happens, ignore. */
59
            return NULL;
1,276✔
60
        }
1,276✔
61
        DEBUG_VALIDATE_BUG_ON(osize > ctx->alloc_bytes);
5,940✔
62
        SCFree(ptr);
5,940✔
63
        ctx->alloc_bytes -= osize;
5,940✔
64
        return NULL;
5,940✔
65
    } else if (ptr == NULL) {
7,216✔
66
        /* Allocating new data. */
67
        void *nptr = SCRealloc(ptr, nsize);
5,940✔
68
        if (nptr != NULL) {
5,940✔
69
            ctx->alloc_bytes += nsize;
5,940✔
70
        }
5,940✔
71
        return nptr;
5,940✔
72
    } else {
5,940✔
73
        /* Resizing existing data. */
74
        ssize_t diff = nsize - osize;
1,072✔
75

76
        if (ctx->alloc_limit != 0 && ctx->alloc_bytes + diff > ctx->alloc_limit) {
1,072✔
77
            /* This request will exceed the allocation limit. Act as
78
             * though allocation failed. */
79
            ctx->memory_limit_error = true;
×
80
            return NULL;
×
81
        }
×
82

83
        void *nptr = SCRealloc(ptr, nsize);
1,072✔
84
        if (nptr != NULL) {
1,072✔
85
            DEBUG_VALIDATE_BUG_ON((ssize_t)ctx->alloc_bytes + diff < 0);
1,072✔
86
            DEBUG_VALIDATE_BUG_ON(osize > ctx->alloc_bytes);
1,072✔
87
            ctx->alloc_bytes += diff;
1,072✔
88
        }
1,072✔
89
        return nptr;
1,072✔
90
    }
1,072✔
91
}
14,228✔
92

93
/**
94
 * Function put in place of Lua functions that are blocked.
95
 *
96
 * TODO: Might want to create a version of this for each library that
97
 * has blocked functions, so it can display the name of the
98
 * library. As it doesn't appear that can be retrieved.
99
 */
100
static int LuaBlockedFunction(lua_State *L)
101
{
×
102
    SCLuaSbState *context = SCLuaSbGetContext(L);
×
103
    context->blocked_function_error = true;
×
104
    lua_Debug ar;
×
105
    if (lua_getstack(L, 0, &ar) && lua_getinfo(L, "n", &ar) && ar.name) {
×
106
        luaL_error(L, "Blocked Lua function called: %s", ar.name);
×
107
    } else {
×
108
        luaL_error(L, "Blocked Lua function: name not available");
×
109
    }
×
110
    /* never reached */
111
    DEBUG_VALIDATE_BUG_ON(1);
×
112
    return -1;
×
113
}
×
114

115
/**
116
 * Check if a Lua function in a specific module is allowed.
117
 *
118
 * This is essentially an allow list for Lua functions.
119
 */
120
static bool IsAllowed(const char *module, const char *fname)
121
{
1,992✔
122
    static const char *base_allowed[] = {
1,992✔
123
        "assert",
1,992✔
124
        "ipairs",
1,992✔
125
        "next",
1,992✔
126
        "pairs",
1,992✔
127
        "print",
1,992✔
128
        "rawequal",
1,992✔
129
        "rawlen",
1,992✔
130
        "select",
1,992✔
131
        "tonumber",
1,992✔
132
        "tostring",
1,992✔
133
        "type",
1,992✔
134
        "warn",
1,992✔
135
        "rawget",
1,992✔
136
        "rawset",
1,992✔
137
        "error",
1,992✔
138
        NULL,
1,992✔
139
    };
1,992✔
140

141
    /* Allow all. */
142
    static const char *table_allowed[] = {
1,992✔
143
        "concat",
1,992✔
144
        "insert",
1,992✔
145
        "move",
1,992✔
146
        "pack",
1,992✔
147
        "remove",
1,992✔
148
        "sort",
1,992✔
149
        "unpack",
1,992✔
150
        NULL,
1,992✔
151
    };
1,992✔
152

153
    /* Allow all. */
154
    static const char *string_allowed[] = {
1,992✔
155
        "byte",
1,992✔
156
        "char",
1,992✔
157
        "dump",
1,992✔
158
        "find",
1,992✔
159
        "format",
1,992✔
160
        "gmatch",
1,992✔
161
        "gsub",
1,992✔
162
        "len",
1,992✔
163
        "lower",
1,992✔
164
        "match",
1,992✔
165
        "pack",
1,992✔
166
        "packsize",
1,992✔
167
        "rep",
1,992✔
168
        "reverse",
1,992✔
169
        "sub",
1,992✔
170
        "unpack",
1,992✔
171
        "upper",
1,992✔
172
        NULL,
1,992✔
173
    };
1,992✔
174

175
    /* Allow all. */
176
    static const char *math_allowed[] = {
1,992✔
177
        "abs",
1,992✔
178
        "acos",
1,992✔
179
        "asin",
1,992✔
180
        "atan",
1,992✔
181
        "atan2",
1,992✔
182
        "ceil",
1,992✔
183
        "cos",
1,992✔
184
        "cosh",
1,992✔
185
        "deg",
1,992✔
186
        "exp",
1,992✔
187
        "floor",
1,992✔
188
        "fmod",
1,992✔
189
        "frexp",
1,992✔
190
        "ldexp",
1,992✔
191
        "log",
1,992✔
192
        "log10",
1,992✔
193
        "max",
1,992✔
194
        "min",
1,992✔
195
        "modf",
1,992✔
196
        "pow",
1,992✔
197
        "rad",
1,992✔
198
        "random",
1,992✔
199
        "randomseed",
1,992✔
200
        "sin",
1,992✔
201
        "sinh",
1,992✔
202
        "sqrt",
1,992✔
203
        "tan",
1,992✔
204
        "tanh",
1,992✔
205
        "tointeger",
1,992✔
206
        "type",
1,992✔
207
        "ult",
1,992✔
208
        NULL,
1,992✔
209
    };
1,992✔
210

211
    /* Allow all. */
212
    static const char *utf8_allowed[] = {
1,992✔
213
        "offset",
1,992✔
214
        "len",
1,992✔
215
        "codes",
1,992✔
216
        "char",
1,992✔
217
        "codepoint",
1,992✔
218
        NULL,
1,992✔
219
    };
1,992✔
220

221
    const char **allowed = NULL;
1,992✔
222

223
    if (strcmp(module, LUA_GNAME) == 0) {
1,992✔
224
        allowed = base_allowed;
552✔
225
    } else if (strcmp(module, LUA_TABLIBNAME) == 0) {
1,440✔
226
        allowed = table_allowed;
168✔
227
    } else if (strcmp(module, LUA_STRLIBNAME) == 0) {
1,272✔
228
        allowed = string_allowed;
408✔
229
    } else if (strcmp(module, LUA_MATHLIBNAME) == 0) {
864✔
230
        allowed = math_allowed;
744✔
231
    } else if (strcmp(module, LUA_UTF8LIBNAME) == 0) {
744✔
232
        allowed = utf8_allowed;
120✔
233
    } else {
120✔
234
        /* This is a programming error. */
235
        FatalError("Unknown Lua module %s", module);
×
236
    }
×
237

238
    if (allowed) {
1,992✔
239
        for (int i = 0; allowed[i] != NULL; i++) {
22,560✔
240
            if (strcmp(allowed[i], fname) == 0) {
22,368✔
241
                return true;
1,800✔
242
            }
1,800✔
243
        }
22,368✔
244
    }
1,992✔
245

246
    return false;
192✔
247
}
1,992✔
248

249
/**
250
 * Set of libs that are allowed and loaded into the Lua state.
251
 */
252
static const luaL_Reg AllowedLibs[] = {
253
    // clang-format off
254
    { LUA_GNAME, luaopen_base },
255
    { LUA_TABLIBNAME, luaopen_table },
256
    { LUA_STRLIBNAME, luaopen_string },
257
    { LUA_MATHLIBNAME, luaopen_math },
258
    { LUA_UTF8LIBNAME, luaopen_utf8 },
259
    { NULL, NULL }
260
    // clang-format on
261
};
262

263
static int SCLuaSbRequire(lua_State *L)
264
{
24✔
265
    const char *module_name = luaL_checkstring(L, 1);
24✔
266

267
    if (SCLuaLoadBuiltIns(L, module_name)) {
24✔
268
        return 1;
24✔
269
    }
24✔
270

271
    return luaL_error(L, "Module not found: %s", module_name);
×
272
}
24✔
273

274
/**
275
 * Load allowed Lua libraries into the state.
276
 *
277
 * Functions from each library that are not in the allowed list are
278
 * replaced with LuaBlockedFunction.
279
 */
280
void SCLuaSbLoadLibs(lua_State *L)
281
{
24✔
282
    const luaL_Reg *lib;
24✔
283

284
    for (lib = AllowedLibs; lib->func; lib++) {
144✔
285
        luaL_requiref(L, lib->name, lib->func, 1);
120✔
286
        lua_pop(L, 1);
120✔
287
        /* Iterate over all the functions in the just loaded table and
288
         * replace functions now on the allow list with our blocked
289
         * function placeholder. */
290
        lua_getglobal(L, lib->name);
120✔
291
        lua_pushnil(L);
120✔
292
        while (lua_next(L, -2)) {
2,280✔
293
            if (lua_type(L, -1) == LUA_TFUNCTION) {
2,160✔
294
                const char *name = lua_tostring(L, -2);
1,992✔
295
                if (!IsAllowed(lib->name, name)) {
1,992✔
296
                    SCLogDebug("Blocking Lua function %s.%s", lib->name, name);
192✔
297
                    lua_pushstring(L, name);
192✔
298
                    lua_pushcfunction(L, LuaBlockedFunction);
192✔
299
                    lua_settable(L, -5);
192✔
300
                } else {
1,800✔
301
                    SCLogDebug("Allowing Lua function %s.%s", lib->name, name);
1,800✔
302
                }
1,800✔
303
            }
1,992✔
304
            lua_pop(L, 1);
2,160✔
305
        }
2,160✔
306
        lua_pop(L, 1);
120✔
307
    }
120✔
308

309
    /* Setup our custom require. */
310
    lua_pushcfunction(L, SCLuaSbRequire);
24✔
311
    lua_setglobal(L, "require");
24✔
312
}
24✔
313

314
/**
315
 * \brief Allocate a new Lua sandbox.
316
 *
317
 * \returns An allocated sandbox state or NULL if memory allocation
318
 *     fails.
319
 */
320
lua_State *SCLuaSbStateNew(uint64_t alloclimit, uint64_t instructionlimit)
321
{
24✔
322
    SCLuaSbState *sb = SCCalloc(1, sizeof(SCLuaSbState));
24✔
323
    if (sb == NULL) {
24✔
324
        return NULL;
×
325
    }
×
326

327
    sb->alloc_limit = alloclimit;
24✔
328
    sb->alloc_bytes = 0;
24✔
329
    sb->hook_instruction_count = 100;
24✔
330
    sb->instruction_limit = instructionlimit;
24✔
331

332
    sb->L = lua_newstate(LuaAlloc, sb);
24✔
333
    if (sb->L == NULL) {
24✔
334
        SCFree(sb);
×
335
        return NULL;
×
336
    }
×
337

338
    lua_pushstring(sb->L, SANDBOX_CTX);
24✔
339
    lua_pushlightuserdata(sb->L, sb);
24✔
340
    lua_settable(sb->L, LUA_REGISTRYINDEX);
24✔
341

342
    lua_sethook(sb->L, HookFunc, LUA_MASKCOUNT, sb->hook_instruction_count);
24✔
343
    return sb->L;
24✔
344
}
24✔
345

346
/**
347
 * Get the Suricata Lua sandbox context from the lua_State.
348
 *
349
 * Note: May return null if this Lua state was not allocated from the
350
 * sandbox.
351
 */
352
SCLuaSbState *SCLuaSbGetContext(lua_State *L)
353
{
144✔
354
    lua_pushstring(L, SANDBOX_CTX);
144✔
355
    lua_gettable(L, LUA_REGISTRYINDEX);
144✔
356
    SCLuaSbState *ctx = lua_touserdata(L, -1);
144✔
357
    lua_pop(L, 1);
144✔
358
    return ctx;
144✔
359
}
144✔
360

361
void SCLuaSbStateClose(lua_State *L)
362
{
24✔
363
    SCLuaSbState *sb = SCLuaSbGetContext(L);
24✔
364
    lua_close(sb->L);
24✔
365
    BUG_ON(sb->alloc_bytes);
24✔
366
    SCFree(sb);
24✔
367
}
24✔
368

369
/**
370
 * Lua debugging hook, but used here for instruction limit counting.
371
 */
372
static void HookFunc(lua_State *L, lua_Debug *ar)
373
{
×
374
    (void)ar;
×
375
    SCLuaSbState *sb = SCLuaSbGetContext(L);
×
376

377
    sb->instruction_count += sb->hook_instruction_count;
×
378

379
    if (sb->instruction_limit > 0 && sb->instruction_count > sb->instruction_limit) {
×
380
        sb->instruction_count_error = true;
×
381
        luaL_error(L, "instruction limit exceeded");
×
382
    }
×
383
}
×
384

385
uint64_t SCLuaSbResetBytesLimit(lua_State *L)
386
{
24✔
387
    uint64_t cfg_limit = 0;
24✔
388
    SCLuaSbState *sb = SCLuaSbGetContext(L);
24✔
389
    if (sb != NULL) {
24✔
390
        cfg_limit = sb->alloc_limit;
24✔
391
        sb->alloc_limit = 0;
24✔
392
    }
24✔
393
    return cfg_limit;
24✔
394
}
24✔
395

396
void SCLuaSbUpdateBytesLimit(lua_State *L)
397
{
24✔
398
    SCLuaSbState *sb = SCLuaSbGetContext(L);
24✔
399
    if (sb != NULL) {
24✔
400
        sb->alloc_limit = sb->alloc_bytes + sb->alloc_limit;
24✔
401
    }
24✔
402
}
24✔
403

404
void SCLuaSbRestoreBytesLimit(lua_State *L, const uint64_t cfg_limit)
405
{
48✔
406
    SCLuaSbState *sb = SCLuaSbGetContext(L);
48✔
407
    if (sb != NULL) {
48✔
408
        sb->alloc_limit = cfg_limit;
48✔
409
    }
48✔
410
}
48✔
411

412
/**
413
 * Reset the instruction counter for the provided state.
414
 */
415
void SCLuaSbResetInstructionCounter(lua_State *L)
416
{
24✔
417
    SCLuaSbState *sb = SCLuaSbGetContext(L);
24✔
418
    if (sb != NULL) {
24✔
419
        sb->blocked_function_error = false;
24✔
420
        sb->instruction_count_error = false;
24✔
421
        sb->instruction_count = 0;
24✔
422
        lua_sethook(L, HookFunc, LUA_MASKCOUNT, sb->hook_instruction_count);
24✔
423
    }
24✔
424
}
24✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc