• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

OISF / suricata / 22618661228

02 Mar 2026 09:33PM UTC coverage: 42.258% (-34.4%) from 76.611%
22618661228

push

github

victorjulien
github-actions: bump actions/download-artifact from 7.0.0 to 8.0.0

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2...70fc10c6e)

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

Signed-off-by: dependabot[bot] <support@github.com>

91511 of 216553 relevant lines covered (42.26%)

3416852.41 hits per line

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

0.0
/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"
×
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
{
×
52
    (void)ud;
×
53
    (void)osize;
×
54
    SCLuaSbState *ctx = (SCLuaSbState *)ud;
×
55

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

76
        if (ctx->alloc_limit != 0 && ctx->alloc_bytes + diff > ctx->alloc_limit) {
×
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);
×
84
        if (nptr != NULL) {
×
85
            DEBUG_VALIDATE_BUG_ON((ssize_t)ctx->alloc_bytes + diff < 0);
×
86
            DEBUG_VALIDATE_BUG_ON(osize > ctx->alloc_bytes);
×
87
            ctx->alloc_bytes += diff;
×
88
        }
×
89
        return nptr;
×
90
    }
×
91
}
×
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
{
×
122
    static const char *base_allowed[] = {
×
123
        "assert",
×
124
        "ipairs",
×
125
        "next",
×
126
        "pairs",
×
127
        "print",
×
128
        "rawequal",
×
129
        "rawlen",
×
130
        "select",
×
131
        "tonumber",
×
132
        "tostring",
×
133
        "type",
×
134
        "warn",
×
135
        "rawget",
×
136
        "rawset",
×
137
        "error",
×
138
        NULL,
×
139
    };
×
140

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

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

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

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

221
    const char **allowed = NULL;
×
222

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

238
    if (allowed) {
×
239
        for (int i = 0; allowed[i] != NULL; i++) {
×
240
            if (strcmp(allowed[i], fname) == 0) {
×
241
                return true;
×
242
            }
×
243
        }
×
244
    }
×
245

246
    return false;
×
247
}
×
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
{
×
265
    const char *module_name = luaL_checkstring(L, 1);
×
266

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

271
    return luaL_error(L, "Module not found: %s", module_name);
×
272
}
×
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
{
×
282
    const luaL_Reg *lib;
×
283

284
    for (lib = AllowedLibs; lib->func; lib++) {
×
285
        luaL_requiref(L, lib->name, lib->func, 1);
×
286
        lua_pop(L, 1);
×
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);
×
291
        lua_pushnil(L);
×
292
        while (lua_next(L, -2)) {
×
293
            if (lua_type(L, -1) == LUA_TFUNCTION) {
×
294
                const char *name = lua_tostring(L, -2);
×
295
                if (!IsAllowed(lib->name, name)) {
×
296
                    SCLogDebug("Blocking Lua function %s.%s", lib->name, name);
×
297
                    lua_pushstring(L, name);
×
298
                    lua_pushcfunction(L, LuaBlockedFunction);
×
299
                    lua_settable(L, -5);
×
300
                } else {
×
301
                    SCLogDebug("Allowing Lua function %s.%s", lib->name, name);
×
302
                }
×
303
            }
×
304
            lua_pop(L, 1);
×
305
        }
×
306
        lua_pop(L, 1);
×
307
    }
×
308

309
    /* Setup our custom require. */
310
    lua_pushcfunction(L, SCLuaSbRequire);
×
311
    lua_setglobal(L, "require");
×
312
}
×
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
{
×
322
    SCLuaSbState *sb = SCCalloc(1, sizeof(SCLuaSbState));
×
323
    if (sb == NULL) {
×
324
        return NULL;
×
325
    }
×
326

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

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

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

342
    lua_sethook(sb->L, HookFunc, LUA_MASKCOUNT, sb->hook_instruction_count);
×
343
    return sb->L;
×
344
}
×
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
{
×
354
    lua_pushstring(L, SANDBOX_CTX);
×
355
    lua_gettable(L, LUA_REGISTRYINDEX);
×
356
    SCLuaSbState *ctx = lua_touserdata(L, -1);
×
357
    lua_pop(L, 1);
×
358
    return ctx;
×
359
}
×
360

361
void SCLuaSbStateClose(lua_State *L)
362
{
×
363
    SCLuaSbState *sb = SCLuaSbGetContext(L);
×
364
    lua_close(sb->L);
×
365
    BUG_ON(sb->alloc_bytes);
×
366
    SCFree(sb);
×
367
}
×
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
{
×
387
    uint64_t cfg_limit = 0;
×
388
    SCLuaSbState *sb = SCLuaSbGetContext(L);
×
389
    if (sb != NULL) {
×
390
        cfg_limit = sb->alloc_limit;
×
391
        sb->alloc_limit = 0;
×
392
    }
×
393
    return cfg_limit;
×
394
}
×
395

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

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

412
/**
413
 * Reset the instruction counter for the provided state.
414
 */
415
void SCLuaSbResetInstructionCounter(lua_State *L)
416
{
×
417
    SCLuaSbState *sb = SCLuaSbGetContext(L);
×
418
    if (sb != NULL) {
×
419
        sb->blocked_function_error = false;
×
420
        sb->instruction_count_error = false;
×
421
        sb->instruction_count = 0;
×
422
        lua_sethook(L, HookFunc, LUA_MASKCOUNT, sb->hook_instruction_count);
×
423
    }
×
424
}
×
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