Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/util-lua.c
Line
Count
Source
1
/* Copyright (C) 2014-2022 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 Victor Julien <victor@inliniac.net>
22
 *
23
 * Common function for Lua
24
 */
25
26
#include "suricata-common.h"
27
#include "detect.h"
28
#include "pkt-var.h"
29
#include "conf.h"
30
31
#include "threads.h"
32
#include "threadvars.h"
33
#include "tm-threads.h"
34
35
#include "util-print.h"
36
#include "util-unittest.h"
37
38
#include "util-debug.h"
39
40
#include "output.h"
41
#include "app-layer-htp.h"
42
#include "app-layer.h"
43
#include "app-layer-parser.h"
44
#include "util-privs.h"
45
#include "util-buffer.h"
46
#include "util-proto-name.h"
47
#include "util-logopenfile.h"
48
#include "util-time.h"
49
50
#include "lua.h"
51
#include "lualib.h"
52
#include "lauxlib.h"
53
54
#include "util-lua.h"
55
#include "util-lua-sandbox.h"
56
#include "detect-lua-extensions.h"
57
58
lua_State *LuaGetState(void)
59
0
{
60
0
    lua_State *s = NULL;
61
0
    s = luaL_newstate();
62
0
    return s;
63
0
}
64
65
void LuaReturnState(lua_State *s)
66
0
{
67
0
    if (s != NULL) {
68
        /* clear the stack */
69
0
        while (lua_gettop(s) > 0) {
70
0
            lua_pop(s, 1);
71
0
        }
72
0
        lua_close(s);
73
0
    }
74
0
}
75
76
/* key for tv (threadvars) pointer */
77
const char lua_ext_key_tv[] = "suricata:lua:tv:ptr";
78
/* key for the invocation-generation counter pointer */
79
const char lua_ext_key_invocation_gen[] = "suricata:lua:invocation:gen";
80
/* key for tx pointer */
81
const char lua_ext_key_tx[] = "suricata:lua:tx:ptr";
82
/* key for tx id */
83
const char lua_ext_key_tx_id[] = "suricata:lua:tx_id";
84
/* key for p (packet) pointer */
85
const char lua_ext_key_p[] = "suricata:lua:pkt:ptr";
86
/* key for f (flow) pointer */
87
const char lua_ext_key_flow[] = "suricata:lua:flow:ptr";
88
/* key for direction */
89
const char lua_ext_key_direction[] = "suricata:lua:direction";
90
91
/* key for pa (packet alert) pointer */
92
const char lua_ext_key_pa[] = "suricata:lua:pkt:alert:ptr";
93
/* key for s (signature) pointer */
94
const char lua_ext_key_s[] = "suricata:lua:signature:ptr";
95
/* key for file pointer */
96
const char lua_ext_key_file[] = "suricata:lua:file:ptr";
97
/* key for DetectEngineThreadCtx pointer */
98
const char lua_ext_key_det_ctx[] = "suricata:lua:det_ctx:ptr";
99
100
/*
101
 * Registry context values are wrapped in a typed userdata instead of
102
 * being stored as bare lightuserdata. In unsandboxed states the registry
103
 * is script-writable, so a bare pointer can be moved between keys and
104
 * re-cast by the libs (type confusion/UAF). The type id is checked on
105
 * every read; a retyped value fails the check and yields NULL.
106
 */
107
typedef struct SCLuaRegValue_ {
108
    uint32_t type_id;
109
    void *ptr;
110
} SCLuaRegValue;
111
112
enum {
113
    SCLUA_REG_INVALID = 0,
114
    SCLUA_REG_TV,
115
    SCLUA_REG_PACKET,
116
    SCLUA_REG_TX,
117
    SCLUA_REG_FLOW,
118
    SCLUA_REG_PACKET_ALERT,
119
    SCLUA_REG_SIGNATURE,
120
    SCLUA_REG_FILE,
121
    SCLUA_REG_DET_CTX,
122
    SCLUA_REG_DETECT_LUA_DATA,
123
    SCLUA_REG_INVOCATION_GEN,
124
};
125
126
static void LuaStateSetReg(lua_State *luastate, const void *key, uint32_t type_id, void *ptr)
127
34.0k
{
128
34.0k
    lua_pushlightuserdata(luastate, (void *)key);
129
34.0k
    if (ptr == NULL) {
130
        /* clear without allocating (this runs at state teardown, where the
131
         * sandbox allocator may be over its cap): NULL a typed slot in
132
         * place, drop anything else (a nil settable never inserts) */
133
0
        lua_gettable(luastate, LUA_REGISTRYINDEX);
134
0
        if (lua_type(luastate, -1) == LUA_TUSERDATA &&
135
0
                lua_rawlen(luastate, -1) == sizeof(SCLuaRegValue)) {
136
0
            SCLuaRegValue *v = (SCLuaRegValue *)lua_touserdata(luastate, -1);
137
0
            if (v != NULL && v->type_id == type_id) {
138
0
                v->ptr = NULL;
139
0
                lua_pop(luastate, 1);
140
0
                return;
141
0
            }
142
0
        }
143
0
        lua_pop(luastate, 1);
144
0
        lua_pushlightuserdata(luastate, (void *)key);
145
0
        lua_pushnil(luastate);
146
0
        lua_settable(luastate, LUA_REGISTRYINDEX);
147
0
        return;
148
0
    }
149
    /* the registry survives across match calls: when the slot already
150
     * holds a value of this type, update it in place instead of paying
151
     * a fresh GC allocation on every set (hot path) */
152
34.0k
    lua_gettable(luastate, LUA_REGISTRYINDEX);
153
34.0k
    if (lua_type(luastate, -1) == LUA_TUSERDATA &&
154
0
            lua_rawlen(luastate, -1) == sizeof(SCLuaRegValue)) {
155
0
        SCLuaRegValue *v = (SCLuaRegValue *)lua_touserdata(luastate, -1);
156
0
        if (v != NULL && v->type_id == type_id) {
157
0
            v->ptr = ptr;
158
0
            lua_pop(luastate, 1);
159
0
            return;
160
0
        }
161
0
    }
162
34.0k
    lua_pop(luastate, 1);
163
34.0k
    lua_pushlightuserdata(luastate, (void *)key);
164
34.0k
    SCLuaRegValue *v = (SCLuaRegValue *)lua_newuserdata(luastate, sizeof(*v));
165
34.0k
    v->type_id = type_id;
166
34.0k
    v->ptr = ptr;
167
34.0k
    lua_settable(luastate, LUA_REGISTRYINDEX);
168
34.0k
}
169
170
static void *LuaStateGetReg(lua_State *luastate, const void *key, uint32_t type_id)
171
0
{
172
0
    lua_pushlightuserdata(luastate, (void *)key);
173
0
    lua_gettable(luastate, LUA_REGISTRYINDEX);
174
0
    void *ptr = NULL;
175
    /* require the exact full-userdata shape before casting: a
176
     * lightuserdata or a foreign small userdata would otherwise be
177
     * re-read as SCLuaRegValue (OOB ptr on a size match) */
178
0
    if (lua_type(luastate, -1) == LUA_TUSERDATA &&
179
0
            lua_rawlen(luastate, -1) == sizeof(SCLuaRegValue)) {
180
0
        SCLuaRegValue *v = (SCLuaRegValue *)lua_touserdata(luastate, -1);
181
0
        if (v != NULL && v->type_id == type_id) {
182
0
            ptr = v->ptr;
183
0
        }
184
0
    }
185
0
    lua_pop(luastate, 1);
186
0
    return ptr;
187
0
}
188
189
/** \brief get tv pointer from the lua state */
190
ThreadVars *LuaStateGetThreadVars(lua_State *luastate)
191
0
{
192
0
    return (ThreadVars *)LuaStateGetReg(luastate, lua_ext_key_tv, SCLUA_REG_TV);
193
0
}
194
195
void LuaStateSetThreadVars(lua_State *luastate, ThreadVars *tv)
196
0
{
197
    /* every registry context write is one script invocation: the state's
198
     * invocation generation counts them, so a handle caching
199
     * invocation-local pointers can compare it as a generation (a tx
200
     * cannot be freed while a script call runs) */
201
0
    uint64_t *gen = LuaStateGetReg(luastate, lua_ext_key_invocation_gen, SCLUA_REG_INVOCATION_GEN);
202
0
    if (gen != NULL) {
203
0
        *gen += 1;
204
0
    }
205
0
    LuaStateSetReg(luastate, lua_ext_key_tv, SCLUA_REG_TV, (void *)tv);
206
0
}
207
208
/* publish the state's invocation counter: the slot holds the counter's
209
 * address, not the counter - the value stays C-owned (sandbox context
210
 * or output ctx), so a script cannot forge a generation; a swapped or
211
 * cleared slot reads as 0 and no cursor is ever trusted */
212
void LuaStateSetInvocationGen(lua_State *luastate, uint64_t *gen)
213
0
{
214
0
    LuaStateSetReg(luastate, lua_ext_key_invocation_gen, SCLUA_REG_INVOCATION_GEN, (void *)gen);
215
0
}
216
217
/** \brief get the current invocation generation for this state */
218
uint64_t LuaStateGetInvocationGen(lua_State *luastate)
219
0
{
220
0
    uint64_t *gen = LuaStateGetReg(luastate, lua_ext_key_invocation_gen, SCLUA_REG_INVOCATION_GEN);
221
0
    return (gen != NULL) ? *gen : 0;
222
0
}
223
224
/** \brief get packet pointer from the lua state */
225
Packet *LuaStateGetPacket(lua_State *luastate)
226
0
{
227
0
    return (Packet *)LuaStateGetReg(luastate, lua_ext_key_p, SCLUA_REG_PACKET);
228
0
}
229
230
void LuaStateSetPacket(lua_State *luastate, Packet *p)
231
0
{
232
0
    LuaStateSetReg(luastate, lua_ext_key_p, SCLUA_REG_PACKET, (void *)p);
233
0
}
234
235
/** \brief get tx pointer from the lua state */
236
void *LuaStateGetTX(lua_State *luastate)
237
0
{
238
0
    return LuaStateGetReg(luastate, lua_ext_key_tx, SCLUA_REG_TX);
239
0
}
240
241
/** \brief get tx id from the lua state */
242
uint64_t LuaStateGetTxId(lua_State *luastate)
243
0
{
244
0
    lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tx_id);
245
0
    lua_gettable(luastate, LUA_REGISTRYINDEX);
246
0
    uint64_t tx_id = lua_tointeger(luastate, -1);
247
0
    lua_pop(luastate, 1);
248
0
    return tx_id;
249
0
}
250
void LuaStateSetTX(lua_State *luastate, void *txptr, const uint64_t tx_id)
251
0
{
252
0
    LuaStateSetReg(luastate, lua_ext_key_tx, SCLUA_REG_TX, txptr);
253
0
    if (txptr == NULL) {
254
0
        return; /* cleared: the id is read together with the slot */
255
0
    }
256
257
0
    lua_pushlightuserdata(luastate, (void *)&lua_ext_key_tx_id);
258
0
    lua_pushinteger(luastate, tx_id);
259
0
    lua_settable(luastate, LUA_REGISTRYINDEX);
260
0
}
261
262
Flow *LuaStateGetFlow(lua_State *luastate)
263
0
{
264
0
    Flow *f = (Flow *)LuaStateGetReg(luastate, lua_ext_key_flow, SCLUA_REG_FLOW);
265
266
0
    return f;
267
0
}
268
269
void LuaStateSetFlow(lua_State *luastate, Flow *f)
270
0
{
271
    /* flow */
272
0
    LuaStateSetReg(luastate, lua_ext_key_flow, SCLUA_REG_FLOW, (void *)f);
273
0
}
274
275
/** \brief get packet alert pointer from the lua state */
276
PacketAlert *LuaStateGetPacketAlert(lua_State *luastate)
277
0
{
278
0
    return (PacketAlert *)LuaStateGetReg(luastate, lua_ext_key_pa, SCLUA_REG_PACKET_ALERT);
279
0
}
280
281
void LuaStateSetPacketAlert(lua_State *luastate, PacketAlert *pa)
282
0
{
283
0
    LuaStateSetReg(luastate, lua_ext_key_pa, SCLUA_REG_PACKET_ALERT, (void *)pa);
284
0
}
285
286
/** \brief get signature pointer from the lua state */
287
Signature *LuaStateGetSignature(lua_State *luastate)
288
0
{
289
0
    return (Signature *)LuaStateGetReg(luastate, lua_ext_key_s, SCLUA_REG_SIGNATURE);
290
0
}
291
292
void LuaStateSetSignature(lua_State *luastate, const Signature *s)
293
0
{
294
0
    LuaStateSetReg(luastate, lua_ext_key_s, SCLUA_REG_SIGNATURE, (void *)s);
295
0
}
296
297
/** \brief get file pointer from the lua state */
298
File *LuaStateGetFile(lua_State *luastate)
299
0
{
300
0
    return (File *)LuaStateGetReg(luastate, lua_ext_key_file, SCLUA_REG_FILE);
301
0
}
302
303
void LuaStateSetFile(lua_State *luastate, File *file)
304
0
{
305
0
    LuaStateSetReg(luastate, lua_ext_key_file, SCLUA_REG_FILE, (void *)file);
306
0
}
307
308
/** \brief get DetectEngineThreadCtx pointer from the lua state */
309
DetectEngineThreadCtx *LuaStateGetDetCtx(lua_State *luastate)
310
0
{
311
0
    return (DetectEngineThreadCtx *)LuaStateGetReg(
312
0
            luastate, lua_ext_key_det_ctx, SCLUA_REG_DET_CTX);
313
0
}
314
315
void LuaStateSetDetCtx(lua_State *luastate, DetectEngineThreadCtx *det_ctx)
316
0
{
317
0
    LuaStateSetReg(luastate, lua_ext_key_det_ctx, SCLUA_REG_DET_CTX, (void *)det_ctx);
318
0
}
319
320
/** \brief get the per-rule DetectLuaData */
321
DetectLuaData *LuaStateGetDetectLuaData(lua_State *luastate)
322
0
{
323
0
    return (DetectLuaData *)LuaStateGetReg(luastate, luaext_key_ld, SCLUA_REG_DETECT_LUA_DATA);
324
0
}
325
326
void LuaStateSetDetectLuaData(lua_State *luastate, DetectLuaData *ld)
327
34.0k
{
328
34.0k
    LuaStateSetReg(luastate, luaext_key_ld, SCLUA_REG_DETECT_LUA_DATA, (void *)ld);
329
34.0k
}
330
331
/** \brief get packet pointer from the lua state */
332
int LuaStateGetDirection(lua_State *luastate)
333
0
{
334
0
    lua_pushlightuserdata(luastate, (void *)&lua_ext_key_direction);
335
0
    lua_gettable(luastate, LUA_REGISTRYINDEX);
336
0
    int dir = lua_toboolean(luastate, -1);
337
0
    lua_pop(luastate, 1);
338
0
    return dir;
339
0
}
340
341
void LuaStateSetDirection(lua_State *luastate, int direction)
342
0
{
343
0
    lua_pushlightuserdata(luastate, (void *)&lua_ext_key_direction);
344
0
    lua_pushboolean(luastate, direction);
345
0
    lua_settable(luastate, LUA_REGISTRYINDEX);
346
0
}
347
348
/** \brief dump stack from lua state to screen */
349
0
void LuaPrintStack(lua_State *state) {
350
0
    int size = lua_gettop(state);
351
0
    int i;
352
353
0
    for (i = 1; i <= size; i++) {
354
0
        int type = lua_type(state, i);
355
0
        printf("Stack size=%d, level=%d, type=%d, ", size, i, type);
356
357
0
        switch (type) {
358
0
            case LUA_TFUNCTION:
359
0
                printf("function %s", lua_tostring(state, i) ? "true" : "false");
360
0
                break;
361
0
            case LUA_TBOOLEAN:
362
0
                printf("bool %s", lua_toboolean(state, i) ? "true" : "false");
363
0
                break;
364
0
            case LUA_TNUMBER:
365
0
                printf("number %g", lua_tonumber(state, i));
366
0
                break;
367
0
            case LUA_TSTRING:
368
0
                printf("string `%s'", lua_tostring(state, i));
369
0
                break;
370
0
            case LUA_TTABLE:
371
0
                printf("table `%s'", lua_tostring(state, i));
372
0
                break;
373
0
            default:
374
0
                printf("other %s", lua_typename(state, type));
375
0
                break;
376
377
0
        }
378
0
        printf("\n");
379
0
    }
380
0
}
381
382
int LuaPushStringBuffer(lua_State *luastate, const uint8_t *input, size_t input_len)
383
0
{
384
    /* a NULL buffer is outside the lua_pushlstring contract even with
385
     * zero length; push the empty string instead (and clamp the length,
386
     * a NULL input has no content of its own) */
387
0
    if (input == NULL) {
388
0
        input = (const uint8_t *)"";
389
0
        input_len = 0;
390
0
    }
391
0
    lua_pushlstring(luastate, (char *)input, input_len);
392
0
    return 1;
393
0
}
394
395
int LuaPushInteger(lua_State *luastate, lua_Integer n)
396
0
{
397
0
    lua_pushinteger(luastate, n);
398
0
    return 1;
399
0
}