aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/src/script_lua.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/src/script_lua.c')
-rw-r--r--examples/redis-unstable/src/script_lua.c1767
1 files changed, 0 insertions, 1767 deletions
diff --git a/examples/redis-unstable/src/script_lua.c b/examples/redis-unstable/src/script_lua.c
deleted file mode 100644
index 2da14ae..0000000
--- a/examples/redis-unstable/src/script_lua.c
+++ /dev/null
@@ -1,1767 +0,0 @@
1/*
2 * Copyright (c) 2009-Present, Redis Ltd.
3 * All rights reserved.
4 *
5 * Copyright (c) 2024-present, Valkey contributors.
6 * All rights reserved.
7 *
8 * Licensed under your choice of (a) the Redis Source Available License 2.0
9 * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
10 * GNU Affero General Public License v3 (AGPLv3).
11 *
12 * Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.
13 */
14
15#include "script_lua.h"
16#include "fpconv_dtoa.h"
17
18#include "server.h"
19#include "sha1.h"
20#include "rand.h"
21#include "cluster.h"
22#include "monotonic.h"
23#include "resp_parser.h"
24#include "version.h"
25#include <lauxlib.h>
26#include <lualib.h>
27#include <ctype.h>
28#include <math.h>
29
30/* Globals that are added by the Lua libraries */
31static char *libraries_allow_list[] = {
32 "string",
33 "cjson",
34 "bit",
35 "cmsgpack",
36 "math",
37 "table",
38 "struct",
39 "os",
40 NULL,
41};
42
43/* Redis Lua API globals */
44static char *redis_api_allow_list[] = {
45 "redis",
46 "__redis__err__handler", /* error handler for eval, currently located on globals.
47 Should move to registry. */
48 NULL,
49};
50
51/* Lua builtins */
52static char *lua_builtins_allow_list[] = {
53 "xpcall",
54 "tostring",
55 "setmetatable",
56 "next",
57 "assert",
58 "tonumber",
59 "rawequal",
60 "collectgarbage",
61 "getmetatable",
62 "rawset",
63 "pcall",
64 "coroutine",
65 "type",
66 "_G",
67 "select",
68 "unpack",
69 "gcinfo",
70 "pairs",
71 "rawget",
72 "loadstring",
73 "ipairs",
74 "_VERSION",
75 "load",
76 "error",
77 NULL,
78};
79
80/* Lua builtins which are deprecated for sandboxing concerns */
81static char *lua_builtins_deprecated[] = {
82 "newproxy",
83 "setfenv",
84 "getfenv",
85 NULL,
86};
87
88/* Lua builtins which are allowed on initialization but will be removed right after */
89static char *lua_builtins_removed_after_initialization_allow_list[] = {
90 "debug", /* debug will be set to nil after the error handler will be created */
91 NULL,
92};
93
94/* Those allow lists was created from the globals that was
95 * available to the user when the allow lists was first introduce.
96 * Because we do not want to break backward compatibility we keep
97 * all the globals. The allow lists will prevent us from accidentally
98 * creating unwanted globals in the future.
99 *
100 * Also notice that the allow list is only checked on start time,
101 * after that the global table is locked so not need to check anything.*/
102static char **allow_lists[] = {
103 libraries_allow_list,
104 redis_api_allow_list,
105 lua_builtins_allow_list,
106 lua_builtins_removed_after_initialization_allow_list,
107 NULL,
108};
109
110/* Deny list contains elements which we know we do not want to add to globals
111 * and there is no need to print a warning message form them. We will print a
112 * log message only if an element was added to the globals and the element is
113 * not on the allow list nor on the back list. */
114static char *deny_list[] = {
115 "dofile",
116 "loadfile",
117 "print",
118 NULL,
119};
120
121static int redis_math_random (lua_State *L);
122static int redis_math_randomseed (lua_State *L);
123static void redisProtocolToLuaType_Int(void *ctx, long long val, const char *proto, size_t proto_len);
124static void redisProtocolToLuaType_BulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
125static void redisProtocolToLuaType_NullBulkString(void *ctx, const char *proto, size_t proto_len);
126static void redisProtocolToLuaType_NullArray(void *ctx, const char *proto, size_t proto_len);
127static void redisProtocolToLuaType_Status(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
128static void redisProtocolToLuaType_Error(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
129static void redisProtocolToLuaType_Array(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
130static void redisProtocolToLuaType_Map(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
131static void redisProtocolToLuaType_Set(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
132static void redisProtocolToLuaType_Null(void *ctx, const char *proto, size_t proto_len);
133static void redisProtocolToLuaType_Bool(void *ctx, int val, const char *proto, size_t proto_len);
134static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto, size_t proto_len);
135static void redisProtocolToLuaType_BigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
136static void redisProtocolToLuaType_VerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len);
137static void redisProtocolToLuaType_Attribute(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
138static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua);
139
140/*
141 * Save the give pointer on Lua registry, used to save the Lua context and
142 * function context so we can retrieve them from lua_State.
143 */
144void luaSaveOnRegistry(lua_State* lua, const char* name, void* ptr) {
145 lua_pushstring(lua, name);
146 if (ptr) {
147 lua_pushlightuserdata(lua, ptr);
148 } else {
149 lua_pushnil(lua);
150 }
151 lua_settable(lua, LUA_REGISTRYINDEX);
152}
153
154/*
155 * Get a saved pointer from registry
156 */
157void* luaGetFromRegistry(lua_State* lua, const char* name) {
158 lua_pushstring(lua, name);
159 lua_gettable(lua, LUA_REGISTRYINDEX);
160
161 if (lua_isnil(lua, -1)) {
162 lua_pop(lua, 1); /* pops the value */
163 return NULL;
164 }
165 /* must be light user data */
166 serverAssert(lua_islightuserdata(lua, -1));
167
168 void* ptr = (void*) lua_topointer(lua, -1);
169 serverAssert(ptr);
170
171 /* pops the value */
172 lua_pop(lua, 1);
173
174 return ptr;
175}
176
177/* ---------------------------------------------------------------------------
178 * Redis reply to Lua type conversion functions.
179 * ------------------------------------------------------------------------- */
180
181/* Take a Redis reply in the Redis protocol format and convert it into a
182 * Lua type. Thanks to this function, and the introduction of not connected
183 * clients, it is trivial to implement the redis() lua function.
184 *
185 * Basically we take the arguments, execute the Redis command in the context
186 * of a non connected client, then take the generated reply and convert it
187 * into a suitable Lua type. With this trick the scripting feature does not
188 * need the introduction of a full Redis internals API. The script
189 * is like a normal client that bypasses all the slow I/O paths.
190 *
191 * Note: in this function we do not do any sanity check as the reply is
192 * generated by Redis directly. This allows us to go faster.
193 *
194 * Errors are returned as a table with a single 'err' field set to the
195 * error string.
196 */
197
198static const ReplyParserCallbacks DefaultLuaTypeParserCallbacks = {
199 .null_array_callback = redisProtocolToLuaType_NullArray,
200 .bulk_string_callback = redisProtocolToLuaType_BulkString,
201 .null_bulk_string_callback = redisProtocolToLuaType_NullBulkString,
202 .error_callback = redisProtocolToLuaType_Error,
203 .simple_str_callback = redisProtocolToLuaType_Status,
204 .long_callback = redisProtocolToLuaType_Int,
205 .array_callback = redisProtocolToLuaType_Array,
206 .set_callback = redisProtocolToLuaType_Set,
207 .map_callback = redisProtocolToLuaType_Map,
208 .bool_callback = redisProtocolToLuaType_Bool,
209 .double_callback = redisProtocolToLuaType_Double,
210 .null_callback = redisProtocolToLuaType_Null,
211 .big_number_callback = redisProtocolToLuaType_BigNumber,
212 .verbatim_string_callback = redisProtocolToLuaType_VerbatimString,
213 .attribute_callback = redisProtocolToLuaType_Attribute,
214 .error = NULL,
215};
216
217static void redisProtocolToLuaType(lua_State *lua, char* reply) {
218 ReplyParser parser = {.curr_location = reply, .callbacks = DefaultLuaTypeParserCallbacks};
219
220 parseReply(&parser, lua);
221}
222
223static void redisProtocolToLuaType_Int(void *ctx, long long val, const char *proto, size_t proto_len) {
224 UNUSED(proto);
225 UNUSED(proto_len);
226 if (!ctx) {
227 return;
228 }
229
230 lua_State *lua = ctx;
231 if (!lua_checkstack(lua, 1)) {
232 /* Increase the Lua stack if needed, to make sure there is enough room
233 * to push elements to the stack. On failure, exit with panic. */
234 serverPanic("lua stack limit reach when parsing redis.call reply");
235 }
236 lua_pushnumber(lua,(lua_Number)val);
237}
238
239static void redisProtocolToLuaType_NullBulkString(void *ctx, const char *proto, size_t proto_len) {
240 UNUSED(proto);
241 UNUSED(proto_len);
242 if (!ctx) {
243 return;
244 }
245
246 lua_State *lua = ctx;
247 if (!lua_checkstack(lua, 1)) {
248 /* Increase the Lua stack if needed, to make sure there is enough room
249 * to push elements to the stack. On failure, exit with panic. */
250 serverPanic("lua stack limit reach when parsing redis.call reply");
251 }
252 lua_pushboolean(lua,0);
253}
254
255static void redisProtocolToLuaType_NullArray(void *ctx, const char *proto, size_t proto_len) {
256 UNUSED(proto);
257 UNUSED(proto_len);
258 if (!ctx) {
259 return;
260 }
261 lua_State *lua = ctx;
262 if (!lua_checkstack(lua, 1)) {
263 /* Increase the Lua stack if needed, to make sure there is enough room
264 * to push elements to the stack. On failure, exit with panic. */
265 serverPanic("lua stack limit reach when parsing redis.call reply");
266 }
267 lua_pushboolean(lua,0);
268}
269
270
271static void redisProtocolToLuaType_BulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
272 UNUSED(proto);
273 UNUSED(proto_len);
274 if (!ctx) {
275 return;
276 }
277
278 lua_State *lua = ctx;
279 if (!lua_checkstack(lua, 1)) {
280 /* Increase the Lua stack if needed, to make sure there is enough room
281 * to push elements to the stack. On failure, exit with panic. */
282 serverPanic("lua stack limit reach when parsing redis.call reply");
283 }
284 lua_pushlstring(lua,str,len);
285}
286
287static void redisProtocolToLuaType_Status(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
288 UNUSED(proto);
289 UNUSED(proto_len);
290 if (!ctx) {
291 return;
292 }
293
294 lua_State *lua = ctx;
295 if (!lua_checkstack(lua, 3)) {
296 /* Increase the Lua stack if needed, to make sure there is enough room
297 * to push elements to the stack. On failure, exit with panic. */
298 serverPanic("lua stack limit reach when parsing redis.call reply");
299 }
300 lua_newtable(lua);
301 lua_pushstring(lua,"ok");
302 lua_pushlstring(lua,str,len);
303 lua_settable(lua,-3);
304}
305
306static void redisProtocolToLuaType_Error(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
307 UNUSED(proto);
308 UNUSED(proto_len);
309 if (!ctx) {
310 return;
311 }
312
313 lua_State *lua = ctx;
314 if (!lua_checkstack(lua, 3)) {
315 /* Increase the Lua stack if needed, to make sure there is enough room
316 * to push elements to the stack. On failure, exit with panic. */
317 serverPanic("lua stack limit reach when parsing redis.call reply");
318 }
319 sds err_msg = sdscatlen(sdsnew("-"), str, len);
320 luaPushErrorBuff(lua,err_msg);
321 /* push a field indicate to ignore updating the stats on this error
322 * because it was already updated when executing the command. */
323 lua_pushstring(lua,"ignore_error_stats_update");
324 lua_pushboolean(lua, 1);
325 lua_settable(lua,-3);
326}
327
328static void redisProtocolToLuaType_Map(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
329 UNUSED(proto);
330 lua_State *lua = ctx;
331 if (lua) {
332 if (!lua_checkstack(lua, 3)) {
333 /* Increase the Lua stack if needed, to make sure there is enough room
334 * to push elements to the stack. On failure, exit with panic. */
335 serverPanic("lua stack limit reach when parsing redis.call reply");
336 }
337 lua_newtable(lua);
338 lua_pushstring(lua, "map");
339 lua_newtable(lua);
340 }
341 for (size_t j = 0; j < len; j++) {
342 parseReply(parser,lua);
343 parseReply(parser,lua);
344 if (lua) lua_settable(lua,-3);
345 }
346 if (lua) lua_settable(lua,-3);
347}
348
349static void redisProtocolToLuaType_Set(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
350 UNUSED(proto);
351
352 lua_State *lua = ctx;
353 if (lua) {
354 if (!lua_checkstack(lua, 3)) {
355 /* Increase the Lua stack if needed, to make sure there is enough room
356 * to push elements to the stack. On failure, exit with panic. */
357 serverPanic("lua stack limit reach when parsing redis.call reply");
358 }
359 lua_newtable(lua);
360 lua_pushstring(lua, "set");
361 lua_newtable(lua);
362 }
363 for (size_t j = 0; j < len; j++) {
364 parseReply(parser,lua);
365 if (lua) {
366 if (!lua_checkstack(lua, 1)) {
367 /* Increase the Lua stack if needed, to make sure there is enough room
368 * to push elements to the stack. On failure, exit with panic.
369 * Notice that here we need to check the stack again because the recursive
370 * call to redisProtocolToLuaType might have use the room allocated in the stack*/
371 serverPanic("lua stack limit reach when parsing redis.call reply");
372 }
373 lua_pushboolean(lua,1);
374 lua_settable(lua,-3);
375 }
376 }
377 if (lua) lua_settable(lua,-3);
378}
379
380static void redisProtocolToLuaType_Array(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
381 UNUSED(proto);
382
383 lua_State *lua = ctx;
384 if (lua){
385 if (!lua_checkstack(lua, 2)) {
386 /* Increase the Lua stack if needed, to make sure there is enough room
387 * to push elements to the stack. On failure, exit with panic. */
388 serverPanic("lua stack limit reach when parsing redis.call reply");
389 }
390 lua_newtable(lua);
391 }
392 for (size_t j = 0; j < len; j++) {
393 if (lua) lua_pushnumber(lua,j+1);
394 parseReply(parser,lua);
395 if (lua) lua_settable(lua,-3);
396 }
397}
398
399static void redisProtocolToLuaType_Attribute(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
400 UNUSED(proto);
401
402 /* Parse the attribute reply.
403 * Currently, we do not expose the attribute to the Lua script so
404 * we just need to continue parsing and ignore it (the NULL ensures that the
405 * reply will be ignored). */
406 for (size_t j = 0; j < len; j++) {
407 parseReply(parser,NULL);
408 parseReply(parser,NULL);
409 }
410
411 /* Parse the reply itself. */
412 parseReply(parser,ctx);
413}
414
415static void redisProtocolToLuaType_VerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len) {
416 UNUSED(proto);
417 UNUSED(proto_len);
418 if (!ctx) {
419 return;
420 }
421
422 lua_State *lua = ctx;
423 if (!lua_checkstack(lua, 5)) {
424 /* Increase the Lua stack if needed, to make sure there is enough room
425 * to push elements to the stack. On failure, exit with panic. */
426 serverPanic("lua stack limit reach when parsing redis.call reply");
427 }
428 lua_newtable(lua);
429 lua_pushstring(lua,"verbatim_string");
430 lua_newtable(lua);
431 lua_pushstring(lua,"string");
432 lua_pushlstring(lua,str,len);
433 lua_settable(lua,-3);
434 lua_pushstring(lua,"format");
435 lua_pushlstring(lua,format,3);
436 lua_settable(lua,-3);
437 lua_settable(lua,-3);
438}
439
440static void redisProtocolToLuaType_BigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
441 UNUSED(proto);
442 UNUSED(proto_len);
443 if (!ctx) {
444 return;
445 }
446
447 lua_State *lua = ctx;
448 if (!lua_checkstack(lua, 3)) {
449 /* Increase the Lua stack if needed, to make sure there is enough room
450 * to push elements to the stack. On failure, exit with panic. */
451 serverPanic("lua stack limit reach when parsing redis.call reply");
452 }
453 lua_newtable(lua);
454 lua_pushstring(lua,"big_number");
455 lua_pushlstring(lua,str,len);
456 lua_settable(lua,-3);
457}
458
459static void redisProtocolToLuaType_Null(void *ctx, const char *proto, size_t proto_len) {
460 UNUSED(proto);
461 UNUSED(proto_len);
462 if (!ctx) {
463 return;
464 }
465
466 lua_State *lua = ctx;
467 if (!lua_checkstack(lua, 1)) {
468 /* Increase the Lua stack if needed, to make sure there is enough room
469 * to push elements to the stack. On failure, exit with panic. */
470 serverPanic("lua stack limit reach when parsing redis.call reply");
471 }
472 lua_pushnil(lua);
473}
474
475static void redisProtocolToLuaType_Bool(void *ctx, int val, const char *proto, size_t proto_len) {
476 UNUSED(proto);
477 UNUSED(proto_len);
478 if (!ctx) {
479 return;
480 }
481
482 lua_State *lua = ctx;
483 if (!lua_checkstack(lua, 1)) {
484 /* Increase the Lua stack if needed, to make sure there is enough room
485 * to push elements to the stack. On failure, exit with panic. */
486 serverPanic("lua stack limit reach when parsing redis.call reply");
487 }
488 lua_pushboolean(lua,val);
489}
490
491static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto, size_t proto_len) {
492 UNUSED(proto);
493 UNUSED(proto_len);
494 if (!ctx) {
495 return;
496 }
497
498 lua_State *lua = ctx;
499 if (!lua_checkstack(lua, 3)) {
500 /* Increase the Lua stack if needed, to make sure there is enough room
501 * to push elements to the stack. On failure, exit with panic. */
502 serverPanic("lua stack limit reach when parsing redis.call reply");
503 }
504 lua_newtable(lua);
505 lua_pushstring(lua,"double");
506 lua_pushnumber(lua,d);
507 lua_settable(lua,-3);
508}
509
510/* This function is used in order to push an error on the Lua stack in the
511 * format used by redis.pcall to return errors, which is a lua table
512 * with an "err" field set to the error string including the error code.
513 * Note that this table is never a valid reply by proper commands,
514 * since the returned tables are otherwise always indexed by integers, never by strings.
515 *
516 * The function takes ownership on the given err_buffer. */
517void luaPushErrorBuff(lua_State *lua, sds err_buffer) {
518 sds msg;
519 sds error_code;
520
521 /* If debugging is active and in step mode, log errors resulting from
522 * Redis commands. */
523 if (ldbIsEnabled()) {
524 ldbLog(sdscatprintf(sdsempty(),"<error> %s",err_buffer));
525 }
526
527 /* There are two possible formats for the received `error` string:
528 * 1) "-CODE msg": in this case we remove the leading '-' since we don't store it as part of the lua error format.
529 * 2) "msg": in this case we prepend a generic 'ERR' code since all error statuses need some error code.
530 * We support format (1) so this function can reuse the error messages used in other places in redis.
531 * We support format (2) so it'll be easy to pass descriptive errors to this function without worrying about format.
532 */
533 if (err_buffer[0] == '-') {
534 /* derive error code from the message */
535 char *err_msg = strstr(err_buffer, " ");
536 if (!err_msg) {
537 msg = sdsnew(err_buffer+1);
538 error_code = sdsnew("ERR");
539 } else {
540 *err_msg = '\0';
541 msg = sdsnew(err_msg+1);
542 error_code = sdsnew(err_buffer + 1);
543 }
544 sdsfree(err_buffer);
545 } else {
546 msg = err_buffer;
547 error_code = sdsnew("ERR");
548 }
549 /* Trim newline at end of string. If we reuse the ready-made Redis error objects (case 1 above) then we might
550 * have a newline that needs to be trimmed. In any case the lua Redis error table shouldn't end with a newline. */
551 msg = sdstrim(msg, "\r\n");
552 sds final_msg = sdscatfmt(error_code, " %s", msg);
553
554 lua_newtable(lua);
555 lua_pushstring(lua,"err");
556 lua_pushstring(lua, final_msg);
557 lua_settable(lua,-3);
558
559 sdsfree(msg);
560 sdsfree(final_msg);
561}
562
563void luaPushError(lua_State *lua, const char *error) {
564 luaPushErrorBuff(lua, sdsnew(error));
565}
566
567/* In case the error set into the Lua stack by luaPushError() was generated
568 * by the non-error-trapping version of redis.pcall(), which is redis.call(),
569 * this function will raise the Lua error so that the execution of the
570 * script will be halted. */
571int luaError(lua_State *lua) {
572 return lua_error(lua);
573}
574
575
576/* ---------------------------------------------------------------------------
577 * Lua reply to Redis reply conversion functions.
578 * ------------------------------------------------------------------------- */
579
580/* Reply to client 'c' converting the top element in the Lua stack to a
581 * Redis reply. As a side effect the element is consumed from the stack. */
582static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua) {
583 int t = lua_type(lua,-1);
584
585 if (!lua_checkstack(lua, 4)) {
586 /* Increase the Lua stack if needed to make sure there is enough room
587 * to push 4 elements to the stack. On failure, return error.
588 * Notice that we need, in the worst case, 4 elements because returning a map might
589 * require push 4 elements to the Lua stack.*/
590 addReplyError(c, "reached lua stack limit");
591 lua_pop(lua,1); /* pop the element from the stack */
592 return;
593 }
594
595 switch(t) {
596 case LUA_TSTRING:
597 addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1));
598 break;
599 case LUA_TBOOLEAN:
600 if (script_client->resp == 2)
601 addReply(c,lua_toboolean(lua,-1) ? shared.cone :
602 shared.null[c->resp]);
603 else
604 addReplyBool(c,lua_toboolean(lua,-1));
605 break;
606 case LUA_TNUMBER:
607 addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
608 break;
609 case LUA_TTABLE:
610 /* We need to check if it is an array, an error, or a status reply.
611 * Error are returned as a single element table with 'err' field.
612 * Status replies are returned as single element table with 'ok'
613 * field. */
614
615 /* Handle error reply. */
616 /* we took care of the stack size on function start */
617 lua_pushstring(lua,"err");
618 lua_rawget(lua,-2);
619 t = lua_type(lua,-1);
620 if (t == LUA_TSTRING) {
621 lua_pop(lua, 1); /* pop the error message, we will use luaExtractErrorInformation to get error information */
622 errorInfo err_info = {0};
623 luaExtractErrorInformation(lua, &err_info);
624 addReplyErrorFormatEx(c,
625 err_info.ignore_err_stats_update? ERR_REPLY_FLAG_NO_STATS_UPDATE: 0,
626 "-%s",
627 err_info.msg);
628 luaErrorInformationDiscard(&err_info);
629 lua_pop(lua,1); /* pop the result table */
630 return;
631 }
632 lua_pop(lua,1); /* Discard field name pushed before. */
633
634 /* Handle status reply. */
635 lua_pushstring(lua,"ok");
636 lua_rawget(lua,-2);
637 t = lua_type(lua,-1);
638 if (t == LUA_TSTRING) {
639 sds ok = sdsnew(lua_tostring(lua,-1));
640 sdsmapchars(ok,"\r\n"," ",2);
641 addReplyStatusLength(c, ok, sdslen(ok));
642 sdsfree(ok);
643 lua_pop(lua,2);
644 return;
645 }
646 lua_pop(lua,1); /* Discard field name pushed before. */
647
648 /* Handle double reply. */
649 lua_pushstring(lua,"double");
650 lua_rawget(lua,-2);
651 t = lua_type(lua,-1);
652 if (t == LUA_TNUMBER) {
653 addReplyDouble(c,lua_tonumber(lua,-1));
654 lua_pop(lua,2);
655 return;
656 }
657 lua_pop(lua,1); /* Discard field name pushed before. */
658
659 /* Handle big number reply. */
660 lua_pushstring(lua,"big_number");
661 lua_rawget(lua,-2);
662 t = lua_type(lua,-1);
663 if (t == LUA_TSTRING) {
664 sds big_num = sdsnewlen(lua_tostring(lua,-1), lua_strlen(lua,-1));
665 sdsmapchars(big_num,"\r\n"," ",2);
666 addReplyBigNum(c,big_num,sdslen(big_num));
667 sdsfree(big_num);
668 lua_pop(lua,2);
669 return;
670 }
671 lua_pop(lua,1); /* Discard field name pushed before. */
672
673 /* Handle verbatim reply. */
674 lua_pushstring(lua,"verbatim_string");
675 lua_rawget(lua,-2);
676 t = lua_type(lua,-1);
677 if (t == LUA_TTABLE) {
678 lua_pushstring(lua,"format");
679 lua_rawget(lua,-2);
680 t = lua_type(lua,-1);
681 if (t == LUA_TSTRING){
682 char* format = (char*)lua_tostring(lua,-1);
683 lua_pushstring(lua,"string");
684 lua_rawget(lua,-3);
685 t = lua_type(lua,-1);
686 if (t == LUA_TSTRING){
687 size_t len;
688 char* str = (char*)lua_tolstring(lua,-1,&len);
689 addReplyVerbatim(c, str, len, format);
690 lua_pop(lua,4);
691 return;
692 }
693 lua_pop(lua,1);
694 }
695 lua_pop(lua,1);
696 }
697 lua_pop(lua,1); /* Discard field name pushed before. */
698
699 /* Handle map reply. */
700 lua_pushstring(lua,"map");
701 lua_rawget(lua,-2);
702 t = lua_type(lua,-1);
703 if (t == LUA_TTABLE) {
704 int maplen = 0;
705 void *replylen = addReplyDeferredLen(c);
706 /* we took care of the stack size on function start */
707 lua_pushnil(lua); /* Use nil to start iteration. */
708 while (lua_next(lua,-2)) {
709 /* Stack now: table, key, value */
710 lua_pushvalue(lua,-2); /* Dup key before consuming. */
711 luaReplyToRedisReply(c, script_client, lua); /* Return key. */
712 luaReplyToRedisReply(c, script_client, lua); /* Return value. */
713 /* Stack now: table, key. */
714 maplen++;
715 }
716 setDeferredMapLen(c,replylen,maplen);
717 lua_pop(lua,2);
718 return;
719 }
720 lua_pop(lua,1); /* Discard field name pushed before. */
721
722 /* Handle set reply. */
723 lua_pushstring(lua,"set");
724 lua_rawget(lua,-2);
725 t = lua_type(lua,-1);
726 if (t == LUA_TTABLE) {
727 int setlen = 0;
728 void *replylen = addReplyDeferredLen(c);
729 /* we took care of the stack size on function start */
730 lua_pushnil(lua); /* Use nil to start iteration. */
731 while (lua_next(lua,-2)) {
732 /* Stack now: table, key, true */
733 lua_pop(lua,1); /* Discard the boolean value. */
734 lua_pushvalue(lua,-1); /* Dup key before consuming. */
735 luaReplyToRedisReply(c, script_client, lua); /* Return key. */
736 /* Stack now: table, key. */
737 setlen++;
738 }
739 setDeferredSetLen(c,replylen,setlen);
740 lua_pop(lua,2);
741 return;
742 }
743 lua_pop(lua,1); /* Discard field name pushed before. */
744
745 /* Handle the array reply. */
746 void *replylen = addReplyDeferredLen(c);
747 int j = 1, mbulklen = 0;
748 while(1) {
749 /* we took care of the stack size on function start */
750 lua_pushnumber(lua,j++);
751 lua_rawget(lua,-2);
752 t = lua_type(lua,-1);
753 if (t == LUA_TNIL) {
754 lua_pop(lua,1);
755 break;
756 }
757 luaReplyToRedisReply(c, script_client, lua);
758 mbulklen++;
759 }
760 setDeferredArrayLen(c,replylen,mbulklen);
761 break;
762 default:
763 addReplyNull(c);
764 }
765 lua_pop(lua,1);
766}
767
768/* ---------------------------------------------------------------------------
769 * Lua redis.* functions implementations.
770 * ------------------------------------------------------------------------- */
771void freeLuaRedisArgv(robj **argv, int argc, int argv_len);
772
773/* Cached argv array across calls. */
774static robj **lua_argv = NULL;
775static int lua_argv_size = 0;
776
777/* Cache of recently used small arguments to avoid malloc calls. */
778static robj *lua_args_cached_objects[LUA_CMD_OBJCACHE_SIZE];
779static size_t lua_args_cached_objects_len[LUA_CMD_OBJCACHE_SIZE];
780
781static robj **luaArgsToRedisArgv(lua_State *lua, int *argc, int *argv_len) {
782 int j;
783 /* Require at least one argument */
784 *argc = lua_gettop(lua);
785 if (*argc == 0) {
786 luaPushError(lua, "Please specify at least one argument for this redis lib call");
787 return NULL;
788 }
789
790 /* Build the arguments vector (reuse a cached argv from last call) */
791 if (lua_argv_size < *argc) {
792 lua_argv = zrealloc(lua_argv,sizeof(robj*)* *argc);
793 lua_argv_size = *argc;
794 }
795 *argv_len = lua_argv_size;
796
797 for (j = 0; j < *argc; j++) {
798 char *obj_s;
799 size_t obj_len;
800 char dbuf[64];
801
802 if (lua_type(lua,j+1) == LUA_TNUMBER) {
803 /* We can't use lua_tolstring() for number -> string conversion
804 * since Lua uses a format specifier that loses precision. */
805 lua_Number num = lua_tonumber(lua,j+1);
806 /* Integer printing function is much faster, check if we can safely use it.
807 * Since lua_Number is not explicitly an integer or a double, we need to make an effort
808 * to convert it as an integer when that's possible, since the string could later be used
809 * in a context that doesn't support scientific notation (e.g. 1e9 instead of 100000000). */
810 long long lvalue;
811 if (double2ll((double)num, &lvalue))
812 obj_len = ll2string(dbuf, sizeof(dbuf), lvalue);
813 else {
814 obj_len = fpconv_dtoa((double)num, dbuf);
815 dbuf[obj_len] = '\0';
816 }
817 obj_s = dbuf;
818 } else {
819 obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);
820 if (obj_s == NULL) break; /* Not a string. */
821 }
822 /* Try to use a cached object. */
823 if (j < LUA_CMD_OBJCACHE_SIZE && lua_args_cached_objects[j] &&
824 lua_args_cached_objects_len[j] >= obj_len)
825 {
826 sds s = lua_args_cached_objects[j]->ptr;
827 lua_argv[j] = lua_args_cached_objects[j];
828 lua_args_cached_objects[j] = NULL;
829 memcpy(s,obj_s,obj_len+1);
830 sdssetlen(s, obj_len);
831 } else {
832 lua_argv[j] = createStringObject(obj_s, obj_len);
833 }
834 }
835
836 /* Pop all arguments from the stack, we do not need them anymore
837 * and this way we guaranty we will have room on the stack for the result. */
838 lua_pop(lua, *argc);
839
840 /* Check if one of the arguments passed by the Lua script
841 * is not a string or an integer (lua_isstring() return true for
842 * integers as well). */
843 if (j != *argc) {
844 freeLuaRedisArgv(lua_argv, j, lua_argv_size);
845 luaPushError(lua, "Lua redis lib command arguments must be strings or integers");
846 return NULL;
847 }
848
849 return lua_argv;
850}
851
852void freeLuaRedisArgv(robj **argv, int argc, int argv_len) {
853 int j;
854 for (j = 0; j < argc; j++) {
855 robj *o = argv[j];
856
857 /* Try to cache the object in the lua_args_cached_objects array.
858 * The object must be small, SDS-encoded, and with refcount = 1
859 * (we must be the only owner) for us to cache it. */
860 if (j < LUA_CMD_OBJCACHE_SIZE &&
861 o->refcount == 1 &&
862 (o->encoding == OBJ_ENCODING_RAW ||
863 o->encoding == OBJ_ENCODING_EMBSTR) &&
864 sdslen(o->ptr) <= LUA_CMD_OBJCACHE_MAX_LEN)
865 {
866 sds s = o->ptr;
867 if (lua_args_cached_objects[j]) decrRefCount(lua_args_cached_objects[j]);
868 lua_args_cached_objects[j] = o;
869 lua_args_cached_objects_len[j] = sdsalloc(s);
870 } else {
871 decrRefCount(o);
872 }
873 }
874 if (argv != lua_argv || argv_len != lua_argv_size) {
875 /* The command changed argv, scrap the cache and start over. */
876 zfree(argv);
877 lua_argv = NULL;
878 lua_argv_size = 0;
879 }
880}
881
882static int luaRedisGenericCommand(lua_State *lua, int raise_error) {
883 int j;
884 scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME);
885 serverAssert(rctx); /* Only supported inside script invocation */
886 sds err = NULL;
887 client* c = rctx->c;
888 sds reply;
889
890 c->argv = luaArgsToRedisArgv(lua, &c->argc, &c->argv_len);
891 if (c->argv == NULL) {
892 return raise_error ? luaError(lua) : 1;
893 }
894
895 static int inuse = 0; /* Recursive calls detection. */
896
897 /* By using Lua debug hooks it is possible to trigger a recursive call
898 * to luaRedisGenericCommand(), which normally should never happen.
899 * To make this function reentrant is futile and makes it slower, but
900 * we should at least detect such a misuse, and abort. */
901 if (inuse) {
902 char *recursion_warning =
903 "luaRedisGenericCommand() recursive call detected. "
904 "Are you doing funny stuff with Lua debug hooks?";
905 serverLog(LL_WARNING,"%s",recursion_warning);
906 luaPushError(lua,recursion_warning);
907 return 1;
908 }
909 inuse++;
910
911 /* Log the command if debugging is active. */
912 if (ldbIsEnabled()) {
913 sds cmdlog = sdsnew("<redis>");
914 for (j = 0; j < c->argc; j++) {
915 if (j == 10) {
916 cmdlog = sdscatprintf(cmdlog," ... (%d more)",
917 c->argc-j-1);
918 break;
919 } else {
920 cmdlog = sdscatlen(cmdlog," ",1);
921 cmdlog = sdscatsds(cmdlog,c->argv[j]->ptr);
922 }
923 }
924 ldbLog(cmdlog);
925 }
926
927 scriptCall(rctx, &err);
928 if (err) {
929 luaPushError(lua, err);
930 sdsfree(err);
931 /* push a field indicate to ignore updating the stats on this error
932 * because it was already updated when executing the command. */
933 lua_pushstring(lua,"ignore_error_stats_update");
934 lua_pushboolean(lua, 1);
935 lua_settable(lua,-3);
936 goto cleanup;
937 }
938
939 /* Convert the result of the Redis command into a suitable Lua type.
940 * The first thing we need is to create a single string from the client
941 * output buffers. */
942 if (listLength(c->reply) == 0 && (size_t)c->bufpos < c->buf_usable_size) {
943 /* This is a fast path for the common case of a reply inside the
944 * client static buffer. Don't create an SDS string but just use
945 * the client buffer directly. */
946 c->buf[c->bufpos] = '\0';
947 reply = c->buf;
948 c->bufpos = 0;
949 } else {
950 reply = sdsnewlen(c->buf,c->bufpos);
951 c->bufpos = 0;
952 while(listLength(c->reply)) {
953 clientReplyBlock *o = listNodeValue(listFirst(c->reply));
954
955 reply = sdscatlen(reply,o->buf,o->used);
956 listDelNode(c->reply,listFirst(c->reply));
957 }
958 }
959 if (raise_error && reply[0] != '-') raise_error = 0;
960 redisProtocolToLuaType(lua,reply);
961
962 /* If the debugger is active, log the reply from Redis. */
963 if (ldbIsEnabled())
964 ldbLogRedisReply(reply);
965
966 if (reply != c->buf) sdsfree(reply);
967 c->reply_bytes = 0;
968
969cleanup:
970 /* Clean up. Command code may have changed argv/argc so we use the
971 * argv/argc of the client instead of the local variables. */
972 freeLuaRedisArgv(c->argv, c->argc, c->argv_len);
973 c->argc = c->argv_len = 0;
974 c->user = NULL;
975 c->argv = NULL;
976 c->all_argv_len_sum = 0;
977 resetClient(c, 1);
978 inuse--;
979
980 if (raise_error) {
981 /* If we are here we should have an error in the stack, in the
982 * form of a table with an "err" field. Extract the string to
983 * return the plain error. */
984 return luaError(lua);
985 }
986 return 1;
987}
988
989/* Our implementation to lua pcall.
990 * We need this implementation for backward
991 * comparability with older Redis versions.
992 *
993 * On Redis 7, the error object is a table,
994 * compare to older version where the error
995 * object is a string. To keep backward
996 * comparability we catch the table object
997 * and just return the error message. */
998static int luaRedisPcall(lua_State *lua) {
999 int argc = lua_gettop(lua);
1000 lua_pushboolean(lua, 1); /* result place holder */
1001 lua_insert(lua, 1);
1002 if (lua_pcall(lua, argc - 1, LUA_MULTRET, 0)) {
1003 /* Error */
1004 lua_remove(lua, 1); /* remove the result place holder, now we have room for at least one element */
1005 if (lua_istable(lua, -1)) {
1006 lua_getfield(lua, -1, "err");
1007 if (lua_isstring(lua, -1)) {
1008 lua_replace(lua, -2); /* replace the error message with the table */
1009 }
1010 }
1011 lua_pushboolean(lua, 0); /* push result */
1012 lua_insert(lua, 1);
1013 }
1014 return lua_gettop(lua);
1015
1016}
1017
1018/* redis.call() */
1019static int luaRedisCallCommand(lua_State *lua) {
1020 return luaRedisGenericCommand(lua,1);
1021}
1022
1023/* redis.pcall() */
1024static int luaRedisPCallCommand(lua_State *lua) {
1025 return luaRedisGenericCommand(lua,0);
1026}
1027
1028/* This adds redis.sha1hex(string) to Lua scripts using the same hashing
1029 * function used for sha1ing lua scripts. */
1030static int luaRedisSha1hexCommand(lua_State *lua) {
1031 int argc = lua_gettop(lua);
1032 char digest[41];
1033 size_t len;
1034 char *s;
1035
1036 if (argc != 1) {
1037 luaPushError(lua, "wrong number of arguments");
1038 return luaError(lua);
1039 }
1040
1041 s = (char*)lua_tolstring(lua,1,&len);
1042 sha1hex(digest,s,len);
1043 lua_pushstring(lua,digest);
1044 return 1;
1045}
1046
1047/* Returns a table with a single field 'field' set to the string value
1048 * passed as argument. This helper function is handy when returning
1049 * a Redis Protocol error or status reply from Lua:
1050 *
1051 * return redis.error_reply("ERR Some Error")
1052 * return redis.status_reply("ERR Some Error")
1053 */
1054static int luaRedisReturnSingleFieldTable(lua_State *lua, char *field) {
1055 if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) {
1056 luaPushError(lua, "wrong number or type of arguments");
1057 return 1;
1058 }
1059
1060 lua_newtable(lua);
1061 lua_pushstring(lua, field);
1062 lua_pushvalue(lua, -3);
1063 lua_settable(lua, -3);
1064 return 1;
1065}
1066
1067/* redis.error_reply() */
1068static int luaRedisErrorReplyCommand(lua_State *lua) {
1069 if (lua_gettop(lua) != 1 || lua_type(lua,-1) != LUA_TSTRING) {
1070 luaPushError(lua, "wrong number or type of arguments");
1071 return 1;
1072 }
1073
1074 /* add '-' if not exists */
1075 const char *err = lua_tostring(lua, -1);
1076 sds err_buff = NULL;
1077 if (err[0] != '-') {
1078 err_buff = sdscatfmt(sdsempty(), "-%s", err);
1079 } else {
1080 err_buff = sdsnew(err);
1081 }
1082 luaPushErrorBuff(lua, err_buff);
1083 return 1;
1084}
1085
1086/* redis.status_reply() */
1087static int luaRedisStatusReplyCommand(lua_State *lua) {
1088 return luaRedisReturnSingleFieldTable(lua,"ok");
1089}
1090
1091/* redis.set_repl()
1092 *
1093 * Set the propagation of write commands executed in the context of the
1094 * script to on/off for AOF and slaves. */
1095static int luaRedisSetReplCommand(lua_State *lua) {
1096 int flags, argc = lua_gettop(lua);
1097
1098 scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME);
1099 serverAssert(rctx); /* Only supported inside script invocation */
1100
1101 if (argc != 1) {
1102 luaPushError(lua, "redis.set_repl() requires one argument.");
1103 return luaError(lua);
1104 }
1105
1106 flags = lua_tonumber(lua,-1);
1107 if ((flags & ~(PROPAGATE_AOF|PROPAGATE_REPL)) != 0) {
1108 luaPushError(lua, "Invalid replication flags. Use REPL_AOF, REPL_REPLICA, REPL_ALL or REPL_NONE.");
1109 return luaError(lua);
1110 }
1111
1112 scriptSetRepl(rctx, flags);
1113 return 0;
1114}
1115
1116/* redis.acl_check_cmd()
1117 *
1118 * Checks ACL permissions for given command for the current user. */
1119static int luaRedisAclCheckCmdPermissionsCommand(lua_State *lua) {
1120 scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME);
1121 serverAssert(rctx); /* Only supported inside script invocation */
1122 int raise_error = 0;
1123
1124 int argc, argv_len;
1125 robj **argv = luaArgsToRedisArgv(lua, &argc, &argv_len);
1126
1127 /* Require at least one argument */
1128 if (argv == NULL) return luaError(lua);
1129
1130 /* Find command */
1131 struct redisCommand *cmd;
1132 if ((cmd = lookupCommand(argv, argc)) == NULL) {
1133 luaPushError(lua, "Invalid command passed to redis.acl_check_cmd()");
1134 raise_error = 1;
1135 } else {
1136 int keyidxptr;
1137 if (ACLCheckAllUserCommandPerm(rctx->original_client->user, cmd, argv, argc, NULL, &keyidxptr) != ACL_OK) {
1138 lua_pushboolean(lua, 0);
1139 } else {
1140 lua_pushboolean(lua, 1);
1141 }
1142 }
1143
1144 freeLuaRedisArgv(argv, argc, argv_len);
1145 if (raise_error)
1146 return luaError(lua);
1147 else
1148 return 1;
1149}
1150
1151
1152/* redis.log() */
1153static int luaLogCommand(lua_State *lua) {
1154 int j, argc = lua_gettop(lua);
1155 int level;
1156 sds log;
1157
1158 if (argc < 2) {
1159 luaPushError(lua, "redis.log() requires two arguments or more.");
1160 return luaError(lua);
1161 } else if (!lua_isnumber(lua,-argc)) {
1162 luaPushError(lua, "First argument must be a number (log level).");
1163 return luaError(lua);
1164 }
1165 level = lua_tonumber(lua,-argc);
1166 if (level < LL_DEBUG || level > LL_WARNING) {
1167 luaPushError(lua, "Invalid log level.");
1168 return luaError(lua);
1169 }
1170 if (level < server.verbosity) return 0;
1171
1172 /* Glue together all the arguments */
1173 log = sdsempty();
1174 for (j = 1; j < argc; j++) {
1175 size_t len;
1176 char *s;
1177
1178 s = (char*)lua_tolstring(lua,(-argc)+j,&len);
1179 if (s) {
1180 if (j != 1) log = sdscatlen(log," ",1);
1181 log = sdscatlen(log,s,len);
1182 }
1183 }
1184 serverLogRaw(level,log);
1185 sdsfree(log);
1186 return 0;
1187}
1188
1189/* redis.setresp() */
1190static int luaSetResp(lua_State *lua) {
1191 scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME);
1192 serverAssert(rctx); /* Only supported inside script invocation */
1193 int argc = lua_gettop(lua);
1194
1195 if (argc != 1) {
1196 luaPushError(lua, "redis.setresp() requires one argument.");
1197 return luaError(lua);
1198 }
1199
1200 int resp = lua_tonumber(lua,-argc);
1201 if (resp != 2 && resp != 3) {
1202 luaPushError(lua, "RESP version must be 2 or 3.");
1203 return luaError(lua);
1204 }
1205 scriptSetResp(rctx, resp);
1206 return 0;
1207}
1208
1209/* ---------------------------------------------------------------------------
1210 * Lua engine initialization and reset.
1211 * ------------------------------------------------------------------------- */
1212
1213static void luaLoadLib(lua_State *lua, const char *libname, lua_CFunction luafunc) {
1214 lua_pushcfunction(lua, luafunc);
1215 lua_pushstring(lua, libname);
1216 lua_call(lua, 1, 0);
1217}
1218
1219LUALIB_API int (luaopen_cjson) (lua_State *L);
1220LUALIB_API int (luaopen_struct) (lua_State *L);
1221LUALIB_API int (luaopen_cmsgpack) (lua_State *L);
1222LUALIB_API int (luaopen_bit) (lua_State *L);
1223
1224static void luaLoadLibraries(lua_State *lua) {
1225 luaLoadLib(lua, "", luaopen_base);
1226 luaLoadLib(lua, LUA_TABLIBNAME, luaopen_table);
1227 luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string);
1228 luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math);
1229 luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug);
1230 luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os);
1231 luaLoadLib(lua, "cjson", luaopen_cjson);
1232 luaLoadLib(lua, "struct", luaopen_struct);
1233 luaLoadLib(lua, "cmsgpack", luaopen_cmsgpack);
1234 luaLoadLib(lua, "bit", luaopen_bit);
1235
1236#if 0 /* Stuff that we don't load currently, for sandboxing concerns. */
1237 luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package);
1238#endif
1239}
1240
1241/* Return sds of the string value located on stack at the given index.
1242 * Return NULL if the value is not a string. */
1243sds luaGetStringSds(lua_State *lua, int index) {
1244 if (!lua_isstring(lua, index)) {
1245 return NULL;
1246 }
1247
1248 size_t len;
1249 const char *str = lua_tolstring(lua, index, &len);
1250 sds str_sds = sdsnewlen(str, len);
1251 return str_sds;
1252}
1253
1254static int luaProtectedTableError(lua_State *lua) {
1255 int argc = lua_gettop(lua);
1256 if (argc != 2) {
1257 serverLog(LL_WARNING, "malicious code trying to call luaProtectedTableError with wrong arguments");
1258 luaL_error(lua, "Wrong number of arguments to luaProtectedTableError");
1259 }
1260 if (!lua_isstring(lua, -1) && !lua_isnumber(lua, -1)) {
1261 luaL_error(lua, "Second argument to luaProtectedTableError must be a string or number");
1262 }
1263 const char *variable_name = lua_tostring(lua, -1);
1264 luaL_error(lua, "Script attempted to access nonexistent global variable '%s'", variable_name);
1265 return 0;
1266}
1267
1268/* Set a special metatable on the table on the top of the stack.
1269 * The metatable will raise an error if the user tries to fetch
1270 * an un-existing value.
1271 *
1272 * The function assumes the Lua stack have a least enough
1273 * space to push 2 element, its up to the caller to verify
1274 * this before calling this function. */
1275void luaSetErrorMetatable(lua_State *lua) {
1276 lua_newtable(lua); /* push metatable */
1277 lua_pushcfunction(lua, luaProtectedTableError); /* push get error handler */
1278 lua_setfield(lua, -2, "__index");
1279 lua_setmetatable(lua, -2);
1280}
1281
1282static int luaNewIndexAllowList(lua_State *lua) {
1283 int argc = lua_gettop(lua);
1284 if (argc != 3) {
1285 serverLog(LL_WARNING, "malicious code trying to call luaNewIndexAllowList with wrong arguments");
1286 luaL_error(lua, "Wrong number of arguments to luaNewIndexAllowList");
1287 }
1288 if (!lua_istable(lua, -3)) {
1289 luaL_error(lua, "first argument to luaNewIndexAllowList must be a table");
1290 }
1291 if (!lua_isstring(lua, -2) && !lua_isnumber(lua, -2)) {
1292 luaL_error(lua, "Second argument to luaNewIndexAllowList must be a string or number");
1293 }
1294 const char *variable_name = lua_tostring(lua, -2);
1295 /* check if the key is in our allow list */
1296
1297 char ***allow_l = allow_lists;
1298 for (; *allow_l ; ++allow_l){
1299 char **c = *allow_l;
1300 for (; *c ; ++c) {
1301 if (strcmp(*c, variable_name) == 0) {
1302 break;
1303 }
1304 }
1305 if (*c) {
1306 break;
1307 }
1308 }
1309
1310 int allowed = (*allow_l != NULL);
1311 /* If not explicitly allowed, check if it's a deprecated function. If so,
1312 * allow it only if 'lua_enable_deprecated_api' config is enabled. */
1313 int deprecated = 0;
1314 if (!allowed) {
1315 char **c = lua_builtins_deprecated;
1316 for (; *c; ++c) {
1317 if (strcmp(*c, variable_name) == 0) {
1318 deprecated = 1;
1319 allowed = server.lua_enable_deprecated_api ? 1 : 0;
1320 break;
1321 }
1322 }
1323 }
1324 if (!allowed) {
1325 /* Search the value on the back list, if its there we know that it was removed
1326 * on purpose and there is no need to print a warning. */
1327 char **c = deny_list;
1328 for ( ; *c ; ++c) {
1329 if (strcmp(*c, variable_name) == 0) {
1330 break;
1331 }
1332 }
1333 if (!*c && !deprecated) {
1334 serverLog(LL_WARNING, "A key '%s' was added to Lua globals which is not on the globals allow list nor listed on the deny list.", variable_name);
1335 }
1336 } else {
1337 lua_rawset(lua, -3);
1338 }
1339 return 0;
1340}
1341
1342/* Set a metatable with '__newindex' function that verify that
1343 * the new index appears on our globals while list.
1344 *
1345 * The metatable is set on the table which located on the top
1346 * of the stack.
1347 */
1348void luaSetAllowListProtection(lua_State *lua) {
1349 lua_newtable(lua); /* push metatable */
1350 lua_pushcfunction(lua, luaNewIndexAllowList); /* push get error handler */
1351 lua_setfield(lua, -2, "__newindex");
1352 lua_setmetatable(lua, -2);
1353}
1354
1355/* Set the readonly flag on the table located on the top of the stack
1356 * and recursively call this function on each table located on the original
1357 * table. Also, recursively call this function on the metatables.*/
1358void luaSetTableProtectionRecursively(lua_State *lua) {
1359 /* This protect us from a loop in case we already visited the table
1360 * For example, globals has '_G' key which is pointing back to globals. */
1361 if (lua_isreadonlytable(lua, -1)) {
1362 return;
1363 }
1364
1365 /* protect the current table */
1366 lua_enablereadonlytable(lua, -1, 1);
1367
1368 lua_checkstack(lua, 2);
1369 lua_pushnil(lua); /* Use nil to start iteration. */
1370 while (lua_next(lua,-2)) {
1371 /* Stack now: table, key, value */
1372 if (lua_istable(lua, -1)) {
1373 luaSetTableProtectionRecursively(lua);
1374 }
1375 lua_pop(lua, 1);
1376 }
1377
1378 /* protect the metatable if exists */
1379 if (lua_getmetatable(lua, -1)) {
1380 luaSetTableProtectionRecursively(lua);
1381 lua_pop(lua, 1); /* pop the metatable */
1382 }
1383}
1384
1385/* Set the readonly flag on the metatable of basic types (string, nil etc.) */
1386void luaSetTableProtectionForBasicTypes(lua_State *lua) {
1387 static const int types[] = {
1388 LUA_TSTRING,
1389 LUA_TNUMBER,
1390 LUA_TBOOLEAN,
1391 LUA_TNIL,
1392 LUA_TFUNCTION,
1393 LUA_TTHREAD,
1394 LUA_TLIGHTUSERDATA
1395 };
1396
1397 for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
1398 /* Push a dummy value of the type to get its metatable */
1399 switch (types[i]) {
1400 case LUA_TSTRING: lua_pushstring(lua, ""); break;
1401 case LUA_TNUMBER: lua_pushnumber(lua, 0); break;
1402 case LUA_TBOOLEAN: lua_pushboolean(lua, 0); break;
1403 case LUA_TNIL: lua_pushnil(lua); break;
1404 case LUA_TFUNCTION: lua_pushcfunction(lua, NULL); break;
1405 case LUA_TTHREAD: lua_newthread(lua); break;
1406 case LUA_TLIGHTUSERDATA: lua_pushlightuserdata(lua, (void*)lua); break;
1407 }
1408 if (lua_getmetatable(lua, -1)) {
1409 luaSetTableProtectionRecursively(lua);
1410 lua_pop(lua, 1); /* pop metatable */
1411 }
1412 lua_pop(lua, 1); /* pop dummy value */
1413 }
1414}
1415
1416void luaRegisterVersion(lua_State* lua) {
1417 lua_pushstring(lua,"REDIS_VERSION_NUM");
1418 lua_pushnumber(lua,REDIS_VERSION_NUM);
1419 lua_settable(lua,-3);
1420
1421 lua_pushstring(lua,"REDIS_VERSION");
1422 lua_pushstring(lua,REDIS_VERSION);
1423 lua_settable(lua,-3);
1424}
1425
1426void luaRegisterLogFunction(lua_State* lua) {
1427 /* redis.log and log levels. */
1428 lua_pushstring(lua,"log");
1429 lua_pushcfunction(lua,luaLogCommand);
1430 lua_settable(lua,-3);
1431
1432 lua_pushstring(lua,"LOG_DEBUG");
1433 lua_pushnumber(lua,LL_DEBUG);
1434 lua_settable(lua,-3);
1435
1436 lua_pushstring(lua,"LOG_VERBOSE");
1437 lua_pushnumber(lua,LL_VERBOSE);
1438 lua_settable(lua,-3);
1439
1440 lua_pushstring(lua,"LOG_NOTICE");
1441 lua_pushnumber(lua,LL_NOTICE);
1442 lua_settable(lua,-3);
1443
1444 lua_pushstring(lua,"LOG_WARNING");
1445 lua_pushnumber(lua,LL_WARNING);
1446 lua_settable(lua,-3);
1447}
1448
1449void luaRegisterRedisAPI(lua_State* lua) {
1450 lua_pushvalue(lua, LUA_GLOBALSINDEX);
1451 luaSetAllowListProtection(lua);
1452 lua_pop(lua, 1);
1453
1454 luaLoadLibraries(lua);
1455
1456 lua_pushcfunction(lua,luaRedisPcall);
1457 lua_setglobal(lua, "pcall");
1458
1459 /* Register the redis commands table and fields */
1460 lua_newtable(lua);
1461
1462 /* redis.call */
1463 lua_pushstring(lua,"call");
1464 lua_pushcfunction(lua,luaRedisCallCommand);
1465 lua_settable(lua,-3);
1466
1467 /* redis.pcall */
1468 lua_pushstring(lua,"pcall");
1469 lua_pushcfunction(lua,luaRedisPCallCommand);
1470 lua_settable(lua,-3);
1471
1472 luaRegisterLogFunction(lua);
1473
1474 luaRegisterVersion(lua);
1475
1476 /* redis.setresp */
1477 lua_pushstring(lua,"setresp");
1478 lua_pushcfunction(lua,luaSetResp);
1479 lua_settable(lua,-3);
1480
1481 /* redis.sha1hex */
1482 lua_pushstring(lua, "sha1hex");
1483 lua_pushcfunction(lua, luaRedisSha1hexCommand);
1484 lua_settable(lua, -3);
1485
1486 /* redis.error_reply and redis.status_reply */
1487 lua_pushstring(lua, "error_reply");
1488 lua_pushcfunction(lua, luaRedisErrorReplyCommand);
1489 lua_settable(lua, -3);
1490 lua_pushstring(lua, "status_reply");
1491 lua_pushcfunction(lua, luaRedisStatusReplyCommand);
1492 lua_settable(lua, -3);
1493
1494 /* redis.set_repl and associated flags. */
1495 lua_pushstring(lua,"set_repl");
1496 lua_pushcfunction(lua,luaRedisSetReplCommand);
1497 lua_settable(lua,-3);
1498
1499 lua_pushstring(lua,"REPL_NONE");
1500 lua_pushnumber(lua,PROPAGATE_NONE);
1501 lua_settable(lua,-3);
1502
1503 lua_pushstring(lua,"REPL_AOF");
1504 lua_pushnumber(lua,PROPAGATE_AOF);
1505 lua_settable(lua,-3);
1506
1507 lua_pushstring(lua,"REPL_SLAVE");
1508 lua_pushnumber(lua,PROPAGATE_REPL);
1509 lua_settable(lua,-3);
1510
1511 lua_pushstring(lua,"REPL_REPLICA");
1512 lua_pushnumber(lua,PROPAGATE_REPL);
1513 lua_settable(lua,-3);
1514
1515 lua_pushstring(lua,"REPL_ALL");
1516 lua_pushnumber(lua,PROPAGATE_AOF|PROPAGATE_REPL);
1517 lua_settable(lua,-3);
1518
1519 /* redis.acl_check_cmd */
1520 lua_pushstring(lua,"acl_check_cmd");
1521 lua_pushcfunction(lua,luaRedisAclCheckCmdPermissionsCommand);
1522 lua_settable(lua,-3);
1523
1524 /* Finally set the table as 'redis' global var. */
1525 lua_setglobal(lua,REDIS_API_NAME);
1526
1527 /* Replace math.random and math.randomseed with our implementations. */
1528 lua_getglobal(lua,"math");
1529
1530 lua_pushstring(lua,"random");
1531 lua_pushcfunction(lua,redis_math_random);
1532 lua_settable(lua,-3);
1533
1534 lua_pushstring(lua,"randomseed");
1535 lua_pushcfunction(lua,redis_math_randomseed);
1536 lua_settable(lua,-3);
1537
1538 lua_setglobal(lua,"math");
1539}
1540
1541/* Set an array of Redis String Objects as a Lua array (table) stored into a
1542 * global variable. */
1543static void luaCreateArray(lua_State *lua, robj **elev, int elec) {
1544 int j;
1545
1546 lua_newtable(lua);
1547 for (j = 0; j < elec; j++) {
1548 lua_pushlstring(lua,(char*)elev[j]->ptr,sdslen(elev[j]->ptr));
1549 lua_rawseti(lua,-2,j+1);
1550 }
1551}
1552
1553/* ---------------------------------------------------------------------------
1554 * Redis provided math.random
1555 * ------------------------------------------------------------------------- */
1556
1557/* We replace math.random() with our implementation that is not affected
1558 * by specific libc random() implementations and will output the same sequence
1559 * (for the same seed) in every arch. */
1560
1561/* The following implementation is the one shipped with Lua itself but with
1562 * rand() replaced by redisLrand48(). */
1563static int redis_math_random (lua_State *L) {
1564 /* the `%' avoids the (rare) case of r==1, and is needed also because on
1565 some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
1566 lua_Number r = (lua_Number)(redisLrand48()%REDIS_LRAND48_MAX) /
1567 (lua_Number)REDIS_LRAND48_MAX;
1568 switch (lua_gettop(L)) { /* check number of arguments */
1569 case 0: { /* no arguments */
1570 lua_pushnumber(L, r); /* Number between 0 and 1 */
1571 break;
1572 }
1573 case 1: { /* only upper limit */
1574 int u = luaL_checkint(L, 1);
1575 luaL_argcheck(L, 1<=u, 1, "interval is empty");
1576 lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
1577 break;
1578 }
1579 case 2: { /* lower and upper limits */
1580 int l = luaL_checkint(L, 1);
1581 int u = luaL_checkint(L, 2);
1582 luaL_argcheck(L, l<=u, 2, "interval is empty");
1583 lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
1584 break;
1585 }
1586 default: return luaL_error(L, "wrong number of arguments");
1587 }
1588 return 1;
1589}
1590
1591static int redis_math_randomseed (lua_State *L) {
1592 redisSrand48(luaL_checkint(L, 1));
1593 return 0;
1594}
1595
1596/* This is the Lua script "count" hook that we use to detect scripts timeout. */
1597static void luaMaskCountHook(lua_State *lua, lua_Debug *ar) {
1598 UNUSED(ar);
1599 scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME);
1600 serverAssert(rctx); /* Only supported inside script invocation */
1601 if (scriptInterrupt(rctx) == SCRIPT_KILL) {
1602 serverLog(LL_NOTICE,"Lua script killed by user with SCRIPT KILL.");
1603
1604 /*
1605 * Set the hook to invoke all the time so the user
1606 * will not be able to catch the error with pcall and invoke
1607 * pcall again which will prevent the script from ever been killed
1608 */
1609 lua_sethook(lua, luaMaskCountHook, LUA_MASKLINE, 0);
1610
1611 luaPushError(lua,"Script killed by user with SCRIPT KILL...");
1612 luaError(lua);
1613 }
1614}
1615
1616void luaErrorInformationDiscard(errorInfo *err_info) {
1617 if (err_info->msg) sdsfree(err_info->msg);
1618 if (err_info->source) sdsfree(err_info->source);
1619 if (err_info->line) sdsfree(err_info->line);
1620}
1621
1622void luaExtractErrorInformation(lua_State *lua, errorInfo *err_info) {
1623 if (lua_isstring(lua, -1)) {
1624 err_info->msg = sdscatfmt(sdsempty(), "ERR %s", lua_tostring(lua, -1));
1625 err_info->line = NULL;
1626 err_info->source = NULL;
1627 err_info->ignore_err_stats_update = 0;
1628 return;
1629 }
1630
1631 lua_getfield(lua, -1, "err");
1632 if (lua_isstring(lua, -1)) {
1633 err_info->msg = sdsnew(lua_tostring(lua, -1));
1634 } else {
1635 /* Ensure we never return a NULL msg. */
1636 err_info->msg = sdsnew("ERR unknown error");
1637 }
1638 lua_pop(lua, 1);
1639
1640 lua_getfield(lua, -1, "source");
1641 if (lua_isstring(lua, -1)) {
1642 err_info->source = sdsnew(lua_tostring(lua, -1));
1643 }
1644 lua_pop(lua, 1);
1645
1646 lua_getfield(lua, -1, "line");
1647 if (lua_isstring(lua, -1)) {
1648 err_info->line = sdsnew(lua_tostring(lua, -1));
1649 }
1650 lua_pop(lua, 1);
1651
1652 lua_getfield(lua, -1, "ignore_error_stats_update");
1653 if (lua_isboolean(lua, -1)) {
1654 err_info->ignore_err_stats_update = lua_toboolean(lua, -1);
1655 }
1656 lua_pop(lua, 1);
1657}
1658
1659void luaCallFunction(scriptRunCtx* run_ctx, lua_State *lua, robj** keys, size_t nkeys, robj** args, size_t nargs, int debug_enabled) {
1660 client* c = run_ctx->original_client;
1661 int delhook = 0;
1662
1663 /* We must set it before we set the Lua hook, theoretically the
1664 * Lua hook might be called wheneven we run any Lua instruction
1665 * such as 'luaSetGlobalArray' and we want the run_ctx to be available
1666 * each time the Lua hook is invoked. */
1667 luaSaveOnRegistry(lua, REGISTRY_RUN_CTX_NAME, run_ctx);
1668
1669 if (server.busy_reply_threshold > 0 && !debug_enabled) {
1670 lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000);
1671 delhook = 1;
1672 } else if (debug_enabled) {
1673 lua_sethook(lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000);
1674 delhook = 1;
1675 }
1676
1677 /* Populate the argv and keys table accordingly to the arguments that
1678 * EVAL received. */
1679 luaCreateArray(lua,keys,nkeys);
1680 /* On eval, keys and arguments are globals. */
1681 if (run_ctx->flags & SCRIPT_EVAL_MODE){
1682 /* open global protection to set KEYS */
1683 lua_enablereadonlytable(lua, LUA_GLOBALSINDEX, 0);
1684 lua_setglobal(lua,"KEYS");
1685 lua_enablereadonlytable(lua, LUA_GLOBALSINDEX, 1);
1686 }
1687 luaCreateArray(lua,args,nargs);
1688 if (run_ctx->flags & SCRIPT_EVAL_MODE){
1689 /* open global protection to set ARGV */
1690 lua_enablereadonlytable(lua, LUA_GLOBALSINDEX, 0);
1691 lua_setglobal(lua,"ARGV");
1692 lua_enablereadonlytable(lua, LUA_GLOBALSINDEX, 1);
1693 }
1694
1695 /* At this point whether this script was never seen before or if it was
1696 * already defined, we can call it.
1697 * On eval mode, we have zero arguments and expect a single return value.
1698 * In addition the error handler is located on position -2 on the Lua stack.
1699 * On function mode, we pass 2 arguments (the keys and args tables),
1700 * and the error handler is located on position -4 (stack: error_handler, callback, keys, args) */
1701 int err;
1702 if (run_ctx->flags & SCRIPT_EVAL_MODE) {
1703 err = lua_pcall(lua,0,1,-2);
1704 } else {
1705 err = lua_pcall(lua,2,1,-4);
1706 }
1707
1708 if (err) {
1709 /* Error object is a table of the following format:
1710 * {err='<error msg>', source='<source file>', line=<line>}
1711 * We can construct the error message from this information */
1712 if (!lua_istable(lua, -1)) {
1713 const char *msg = "execution failure";
1714 if (lua_isstring(lua, -1)) {
1715 msg = lua_tostring(lua, -1);
1716 }
1717 addReplyErrorFormat(c,"Error running script %s, %.100s\n", run_ctx->funcname, msg);
1718 } else {
1719 errorInfo err_info = {0};
1720 sds final_msg = sdsempty();
1721 luaExtractErrorInformation(lua, &err_info);
1722 final_msg = sdscatfmt(final_msg, "-%s",
1723 err_info.msg);
1724 if (err_info.line && err_info.source) {
1725 final_msg = sdscatfmt(final_msg, " script: %s, on %s:%s.",
1726 run_ctx->funcname,
1727 err_info.source,
1728 err_info.line);
1729 }
1730 addReplyErrorSdsEx(c, final_msg, err_info.ignore_err_stats_update? ERR_REPLY_FLAG_NO_STATS_UPDATE : 0);
1731 luaErrorInformationDiscard(&err_info);
1732 }
1733 lua_pop(lua,1); /* Consume the Lua error */
1734 } else {
1735 /* On success convert the Lua return value into Redis protocol, and
1736 * send it to * the client. */
1737 luaReplyToRedisReply(c, run_ctx->c, lua); /* Convert and consume the reply. */
1738 }
1739
1740 /* Perform some cleanup that we need to do both on error and success. */
1741 if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */
1742
1743 /* remove run_ctx from registry, its only applicable for the current script. */
1744 luaSaveOnRegistry(lua, REGISTRY_RUN_CTX_NAME, NULL);
1745}
1746
1747unsigned long luaMemory(lua_State *lua) {
1748 return lua_gc(lua, LUA_GCCOUNT, 0) * 1024LL;
1749}
1750
1751/* Call the Lua garbage collector from time to time to avoid a
1752 * full cycle performed by Lua, which adds too latency.
1753 *
1754 * The call is performed every LUA_GC_CYCLE_PERIOD executed commands
1755 * (and for LUA_GC_CYCLE_PERIOD collection steps) because calling it
1756 * for every command uses too much CPU.
1757 *
1758 * Each script VM / State (Eval and Functions) maintains its own unique `gc_count`
1759 * to control GC independently. */
1760#define LUA_GC_CYCLE_PERIOD 50
1761void luaGC(lua_State *lua, int *gc_count) {
1762 (*gc_count)++;
1763 if (*gc_count >= LUA_GC_CYCLE_PERIOD) {
1764 lua_gc(lua, LUA_GCSTEP, LUA_GC_CYCLE_PERIOD);
1765 *gc_count = 0;
1766 }
1767}