Freeciv-3.2
Loading...
Searching...
No Matches
luascript.c
Go to the documentation of this file.
1/*****************************************************************************
2 Freeciv - Copyright (C) 2005 - The Freeciv Project
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12*****************************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <stdarg.h>
19#include <stdlib.h>
20#include <time.h>
21
22/* dependencies/lua */
23#include "lua.h"
24#include "lualib.h"
25
26/* utility */
27#include "astring.h"
28#include "log.h"
29#include "registry.h"
30
31/* common */
32#include "map.h"
33
34/* common/scriptcore */
35#include "luascript_func.h"
36#include "luascript_signal.h"
37
38#include "luascript.h"
39
40/*****************************************************************************
41 Configuration for script execution time limits. Checkinterval is the
42 number of executed lua instructions between checking. Disabled if 0.
43*****************************************************************************/
44#define LUASCRIPT_MAX_EXECUTION_TIME_SEC 5.0
45#define LUASCRIPT_CHECKINTERVAL 10000
46
47/* The name used for the freeciv lua struct saved in the lua state. */
48#define LUASCRIPT_GLOBAL_VAR_NAME "__fcl"
49
50/*****************************************************************************
51 Unsafe Lua builtin symbols that we to remove access to.
52
53 If Freeciv's Lua version changes, you have to check how the set of
54 unsafe functions and modules changes in the new version. Update the list of
55 loaded libraries in luascript_lualibs, then update the unsafe symbols
56 blacklist in luascript_unsafe_symbols.
57
58 Once the variables are updated for the new version, update the value of
59 LUASCRIPT_SECURE_LUA_VERSION
60
61 In general, unsafe is all functionality that gives access to:
62 * Reading files and running processes
63 * Loading lua files or libraries
64*****************************************************************************/
65#define LUASCRIPT_SECURE_LUA_VERSION1 503
66#define LUASCRIPT_SECURE_LUA_VERSION2 504
67
68static const char *luascript_unsafe_symbols_secure[] = {
69 "debug",
70 "dofile",
71 "loadfile",
72 NULL
73};
74
76 "debug",
77 "dofile",
78 "loadfile",
79 NULL
80};
81
82#if LUA_VERSION_NUM != LUASCRIPT_SECURE_LUA_VERSION1 && LUA_VERSION_NUM != LUASCRIPT_SECURE_LUA_VERSION2
83#warning "The script runtime's unsafe symbols information is not up to date."
84#warning "This can be a big security hole!"
85#endif
86
87/*****************************************************************************
88 Lua libraries to load (all default libraries, excluding operating system
89 and library loading modules). See linit.c in Lua 5.1 for the default list.
90*****************************************************************************/
91#if LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
93 /* Using default libraries excluding: package, io, os, and bit32 */
94 {"_G", luaopen_base},
101 {NULL, NULL}
102};
103
105 /* Using default libraries excluding: package, and bit32 */
106 {"_G", luaopen_base},
115 {NULL, NULL}
116};
117#else /* LUA_VERSION_NUM */
118#error "Unsupported lua version"
119#endif /* LUA_VERSION_NUM */
120
121static int luascript_report(struct fc_lua *fcl, int status, const char *code);
125static void luascript_hook_start(lua_State *L);
126static void luascript_hook_end(lua_State *L);
127static void luascript_openlibs(lua_State *L, const luaL_Reg *llib);
128static void luascript_blacklist(lua_State *L, const char *lsymbols[]);
129
130/**********************************************************************/
133static int luascript_report(struct fc_lua *fcl, int status,
134 const char *code)
135{
137 fc_assert_ret_val(fcl->state, -1);
138
139 if (status) {
140 struct astring str = ASTRING_INIT;
141 const char *msg;
142 int lineno;
143
144 if (!(msg = lua_tostring(fcl->state, -1))) {
145 msg = "(error with no message)";
146 }
147
148 /* Add error message. */
149 astr_add_line(&str, "lua error:");
150 astr_add_line(&str, "\t%s", msg);
151
152 if (code) {
153 /* Add lines around the place the parse error is. */
154 if (sscanf(msg, "%*[^:]:%d:", &lineno) == 1) {
155 const char *begin, *end;
156 int i;
157
158 astr_add(&str, "\n");
159
160 i = 1;
161 for (begin = code; *begin != '\0';) {
162 int len;
163
164 end = strchr(begin, '\n');
165 if (end) {
166 len = end - begin;
167 } else {
168 len = strlen(begin);
169 }
170
171 if (abs(lineno - i) <= 3) {
172 const char *indicator;
173
174 indicator = (lineno == i) ? "-->" : " ";
175
176 astr_add_line(&str, "\t%s%3d:\t%*.*s",
177 indicator, i, len, len, begin);
178 }
179
180 i++;
181
182 if (end) {
183 begin = end + 1;
184 } else {
185 break;
186 }
187 }
188
189 astr_add(&str, "\n");
190 }
191 }
192
194
195 astr_free(&str);
196
197 lua_pop(fcl->state, 1);
198 }
199
200 return status;
201}
202
203/**********************************************************************/
207{
208 /* Find the debug.traceback function, if available */
209 lua_getglobal(L, "debug");
210 if (lua_istable(L, -1)) {
211 lua_getfield(L, -1, "traceback");
212 lua_setfield(L, LUA_REGISTRYINDEX, "freeciv_traceback");
213 }
214 lua_pop(L, 1); /* pop debug */
215}
216
217/**********************************************************************/
221{
222 lua_getfield(L, LUA_REGISTRYINDEX, "freeciv_traceback");
223}
224
225/**********************************************************************/
229{
231
232 lua_getfield(L, LUA_REGISTRYINDEX, "freeciv_exec_clock");
234 lua_pop(L, 1);
235 if ((float)(clock() - exec_clock)/CLOCKS_PER_SEC
237 luaL_error(L, "Execution time limit exceeded in script");
238 }
239}
240
241/**********************************************************************/
245{
246#if LUASCRIPT_CHECKINTERVAL
247 /* Store clock timestamp in the registry */
249 lua_setfield(L, LUA_REGISTRYINDEX, "freeciv_exec_clock");
251#endif
252}
253
254/**********************************************************************/
258{
259#if LUASCRIPT_CHECKINTERVAL
261#endif
262}
263
264/**********************************************************************/
268{
269 /* set results to global table */
270 for (; llib->func; llib++) {
271 luaL_requiref(L, llib->name, llib->func, 1);
272 lua_pop(L, 1); /* remove lib */
273 }
274}
275
276/**********************************************************************/
279static void luascript_blacklist(lua_State *L, const char *lsymbols[])
280{
281 int i;
282
283 for (i = 0; lsymbols[i] != NULL; i++) {
284 lua_pushnil(L);
286 }
287}
288
289/**********************************************************************/
292int luascript_error(lua_State *L, const char *format, ...)
293{
295 int ret;
296
297 va_start(vargs, format);
298 ret = luascript_error_vargs(L, format, vargs);
299 va_end(vargs);
300
301 return ret;
302}
303
304/**********************************************************************/
310{
311 fc_assert_ret_val(L != NULL, -1);
312
313 luaL_where(L, 1);
314 lua_pushvfstring(L, format, vargs);
315 lua_concat(L, 2);
316
317 return lua_error(L);
318}
319
320/**********************************************************************/
325int luascript_arg_error(lua_State *L, int narg, const char *msg)
326{
327 return luaL_argerror(L, narg, msg);
328}
329
330/**********************************************************************/
335{
336 struct fc_lua *fcl = fc_calloc(1, sizeof(*fcl));
337
338 fcl->state = luaL_newstate();
339 if (!fcl->state) {
340 FC_FREE(fcl);
341 return NULL;
342 }
343 fcl->output_fct = output_fct;
344 fcl->caller = NULL;
345
350 } else {
354 }
355
356 /* Save the freeciv lua struct in the lua state. */
360
361 return fcl;
362}
363
364/**********************************************************************/
368{
369 struct fc_lua *fcl;
370
372
373 /* Get the freeciv lua struct from the lua state. */
376 fcl = lua_touserdata(L, -1);
377
378 /* This is an error! */
380
381 return fcl;
382}
383
384/**********************************************************************/
388{
389 if (fcl) {
390 fc_assert_ret(fcl->caller == NULL);
391
392 /* Free function data. */
394
395 /* Free signal data. */
397
398 /* Free lua state. */
399 if (fcl->state) {
400 lua_gc(fcl->state, LUA_GCCOLLECT, 0); /* Collected garbage */
401 lua_close(fcl->state);
402 }
403 free(fcl);
404 }
405}
406
407/**********************************************************************/
411 const char *format, ...)
412{
413 va_list args;
414
415 va_start(args, format);
416 luascript_log_vargs(fcl, level, format, args);
417 va_end(args);
418}
419
420/**********************************************************************/
424 const char *format, va_list args)
425{
426 char buf[1024];
427
430
431 fc_vsnprintf(buf, sizeof(buf), format, args);
432
433 if (fcl->output_fct) {
434 fcl->output_fct(fcl, level, "%s", buf);
435 } else {
436 log_base(level, "%s", buf);
437 }
438}
439
440/**********************************************************************/
443void luascript_pop_returns(struct fc_lua *fcl, const char *func_name,
444 int nreturns, enum api_types *preturn_types,
445 va_list args)
446{
447 int i;
448 lua_State *L;
449
451 fc_assert_ret(fcl->state);
452 L = fcl->state;
453
454 for (i = 0; i < nreturns; i++) {
456
458
459 switch (type) {
460 case API_TYPE_INT:
461 {
462 int isnum;
463 int *pres = va_arg(args, int*);
464
465 *pres = lua_tointegerx(L, -1, &isnum);
466 if (!isnum) {
467 log_error("Return value from lua function %s is a %s, want int",
469 }
470 }
471 break;
472 case API_TYPE_BOOL:
473 {
474 bool *pres = va_arg(args, bool*);
475 *pres = lua_toboolean(L, -1);
476 }
477 break;
478 case API_TYPE_STRING:
479 {
480 char **pres = va_arg(args, char**);
481
482 if (lua_isstring(L, -1)) {
483 *pres = fc_strdup(lua_tostring(L, -1));
484 }
485 }
486 break;
487 default:
488 {
489 void **pres = va_arg(args, void**);
490
491 *pres = tolua_tousertype(fcl->state, -1, NULL);
492 }
493 break;
494 }
495 lua_pop(L, 1);
496 }
497}
498
499/**********************************************************************/
502void luascript_push_args(struct fc_lua *fcl, int nargs,
503 enum api_types *parg_types, va_list args)
504{
505 int i;
506
508 fc_assert_ret(fcl->state);
509
510 for (i = 0; i < nargs; i++) {
511 enum api_types type = parg_types[i];
512
514
515 switch (type) {
516 case API_TYPE_INT:
517 {
518 lua_Integer arg = va_arg(args, lua_Integer);
519 lua_pushinteger(fcl->state, arg);
520 }
521 break;
522 case API_TYPE_BOOL:
523 {
524 int arg = va_arg(args, int);
525 lua_pushboolean(fcl->state, arg);
526 }
527 break;
528 case API_TYPE_STRING:
529 {
530 const char *arg = va_arg(args, const char*);
531 lua_pushstring(fcl->state, arg);
532 }
533 break;
534 default:
535 {
536 const char *name;
537 void *arg;
538
540
541 arg = va_arg(args, void*);
542 tolua_pushusertype(fcl->state, arg, name);
543 }
544 break;
545 }
546 }
547}
548
549/**********************************************************************/
553bool luascript_check_function(struct fc_lua *fcl, const char *funcname)
554{
555 bool defined;
556
558 fc_assert_ret_val(fcl->state, FALSE);
559
560 lua_getglobal(fcl->state, funcname);
561 defined = lua_isfunction(fcl->state, -1);
562 lua_pop(fcl->state, 1);
563
564 return defined;
565}
566
567/**********************************************************************/
579int luascript_call(struct fc_lua *fcl, int narg, int nret,
580 const char *code)
581{
582 int status;
583 int base; /* Index of function to call */
584 int traceback = 0; /* Index of traceback function */
585
587 fc_assert_ret_val(fcl->state, -1);
588
589 base = lua_gettop(fcl->state) - narg;
590
591 /* Find the traceback function, if available */
593 if (lua_isfunction(fcl->state, -1)) {
594 lua_insert(fcl->state, base); /* insert traceback before function */
595 traceback = base;
596 } else {
597 lua_pop(fcl->state, 1); /* pop non-function traceback */
598 }
599
601 status = lua_pcall(fcl->state, narg, nret, traceback);
602 luascript_hook_end(fcl->state);
603
604 if (status) {
605 luascript_report(fcl, status, code);
606 }
607
608 if (traceback) {
609 lua_remove(fcl->state, traceback);
610 }
611
612 return status;
613}
614
615/**********************************************************************/
618int luascript_do_string(struct fc_lua *fcl, const char *str,
619 const char *name)
620{
621 int status;
622
624 fc_assert_ret_val(fcl->state, -1);
625
626 status = luaL_loadbuffer(fcl->state, str, strlen(str), name);
627 if (status) {
628 luascript_report(fcl, status, str);
629 } else {
630 status = luascript_call(fcl, 0, 0, str);
631 }
632 return status;
633}
634
635/**********************************************************************/
638int luascript_do_file(struct fc_lua *fcl, const char *filename)
639{
640 int status;
641
643 fc_assert_ret_val(fcl->state, -1);
644
645 status = luaL_loadfile(fcl->state, filename);
646 if (status) {
647 luascript_report(fcl, status, NULL);
648 } else {
649 status = luascript_call(fcl, 0, 0, NULL);
650 }
651 return status;
652}
653
654
655/**********************************************************************/
659 const char *callback_name,
660 int nargs, enum api_types *parg_types,
661 va_list args)
662{
663 bool stop_emission = FALSE;
664
666 fc_assert_ret_val(fcl->state, FALSE);
667
668 /* The function name */
670
671 if (!lua_isfunction(fcl->state, -1)) {
672 luascript_log(fcl, LOG_ERROR, "lua error: Unknown callback '%s'",
674 lua_pop(fcl->state, 1);
675 return FALSE;
676 }
677
678 luascript_log(fcl, LOG_DEBUG, "lua callback: '%s'", callback_name);
679
680 luascript_push_args(fcl, nargs, parg_types, args);
681
682 /* Call the function with nargs arguments, return 1 results */
683 if (luascript_call(fcl, nargs, 1, NULL)) {
684 return FALSE;
685 }
686
687 /* Shall we stop the emission of this signal? */
688 if (lua_isboolean(fcl->state, -1)) {
689 stop_emission = lua_toboolean(fcl->state, -1);
690 }
691 lua_pop(fcl->state, 1); /* pop return value */
692
693 return stop_emission;
694}
695
696/**********************************************************************/
701void luascript_remove_exported_object(struct fc_lua *fcl, void *object)
702{
703 if (fcl && fcl->state) {
704 fc_assert_ret(object != NULL);
705
706 /* The following is similar to tolua_release(..) in src/lib/tolua_map.c */
707 /* Find the userdata representing 'object' */
708 lua_pushstring(fcl->state, "tolua_ubox");
709 /* stack: ubox */
711 /* stack: ubox u */
712 lua_pushlightuserdata(fcl->state, object);
713 /* stack: ubox ubox[u] */
714 lua_rawget(fcl->state, -2);
715
716 if (!lua_isnil(fcl->state, -1)) {
717 fc_assert(object == tolua_tousertype(fcl->state, -1, NULL));
718 /* Change API type to 'Nonexistent' */
719 /* stack: ubox ubox[u] mt */
720 tolua_getmetatable(fcl->state, "Nonexistent");
721 lua_setmetatable(fcl->state, -2);
722 /* Set the userdata payload to NULL */
723 *((void **)lua_touserdata(fcl->state, -1)) = NULL;
724 /* Remove from ubox */
725 /* stack: ubox ubox[u] u */
726 lua_pushlightuserdata(fcl->state, object);
727 /* stack: ubox ubox[u] u nil */
728 lua_pushnil(fcl->state);
729 lua_rawset(fcl->state, -4);
730 }
731 lua_pop(fcl->state, 2);
732 }
733}
734
735/**********************************************************************/
738void luascript_vars_save(struct fc_lua *fcl, struct section_file *file,
739 const char *section)
740{
741 fc_assert_ret(file);
743 fc_assert_ret(fcl->state);
744
745 lua_getglobal(fcl->state, "_freeciv_state_dump");
746 if (luascript_call(fcl, 0, 1, NULL) == 0) {
747 const char *vars;
748
749 vars = lua_tostring(fcl->state, -1);
750 lua_pop(fcl->state, 1);
751
752 if (vars) {
754 }
755 } else {
756 /* _freeciv_state_dump in tolua_game.pkg is busted */
757 luascript_log(fcl, LOG_ERROR, "lua error: Failed to dump variables");
758 }
759}
760
761/**********************************************************************/
764void luascript_vars_load(struct fc_lua *fcl, struct section_file *file,
765 const char *section)
766{
767 const char *vars;
768
769 fc_assert_ret(file);
771 fc_assert_ret(fcl->state);
772
773 vars = secfile_lookup_str_default(file, "", "%s", section);
775}
776
777/* FIXME: tolua-5.2 does not create a destructor for dynamically
778 * allocated objects in non-C++ code but tries to call it. */
779/* Thus, avoid returning any non-basic types. If you need them, put here
780 * constructors returning them in lua_Object registering their destructor
781 * in tolua system. (None yet.) */
782
783/* To avoid the complexity and overheads of creating such objects for
784 * 4-bit enums, here is a helper function to return Direction objects. */
785/********************************************************************//******
786 Returns a pointer to a given value of enum direction8 (always the same
787 address for the same value), or NULL if the direction is invalid
788 on the current map.
789****************************************************************************/
791{
792 static const
796 if (is_valid_dir(dir)) {
797 return &etalon[dir];
798 } else {
799 return NULL;
800 }
801}
void astr_free(struct astring *astr)
Definition astring.c:153
void astr_add_line(struct astring *astr, const char *format,...)
Definition astring.c:299
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:287
#define str
Definition astring.c:76
static const char * astr_str(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:93
#define ASTRING_INIT
Definition astring.h:44
char * incite_cost
Definition comments.c:74
static void base(QVariant data1, QVariant data2)
Definition dialogs.cpp:2931
GType type
Definition repodlgs.c:1313
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:191
#define fc_assert(condition)
Definition log.h:176
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define log_base(level, message,...)
Definition log.h:94
log_level
Definition log.h:28
@ LOG_ERROR
Definition log.h:30
@ LOG_DEBUG
Definition log.h:34
#define log_error(message,...)
Definition log.h:103
static int luascript_report(struct fc_lua *fcl, int status, const char *code)
Definition luascript.c:133
void luascript_pop_returns(struct fc_lua *fcl, const char *func_name, int nreturns, enum api_types *preturn_types, va_list args)
Definition luascript.c:443
void luascript_push_args(struct fc_lua *fcl, int nargs, enum api_types *parg_types, va_list args)
Definition luascript.c:502
static void luascript_traceback_func_push(lua_State *L)
Definition luascript.c:220
static const char * luascript_unsafe_symbols_secure[]
Definition luascript.c:68
bool luascript_check_function(struct fc_lua *fcl, const char *funcname)
Definition luascript.c:553
int luascript_error_vargs(lua_State *L, const char *format, va_list vargs)
Definition luascript.c:309
#define LUASCRIPT_CHECKINTERVAL
Definition luascript.c:45
static void luascript_openlibs(lua_State *L, const luaL_Reg *llib)
Definition luascript.c:267
int luascript_arg_error(lua_State *L, int narg, const char *msg)
Definition luascript.c:325
#define LUASCRIPT_MAX_EXECUTION_TIME_SEC
Definition luascript.c:44
bool luascript_callback_invoke(struct fc_lua *fcl, const char *callback_name, int nargs, enum api_types *parg_types, va_list args)
Definition luascript.c:658
void luascript_remove_exported_object(struct fc_lua *fcl, void *object)
Definition luascript.c:701
int luascript_do_string(struct fc_lua *fcl, const char *str, const char *name)
Definition luascript.c:618
int luascript_error(lua_State *L, const char *format,...)
Definition luascript.c:292
int luascript_do_file(struct fc_lua *fcl, const char *filename)
Definition luascript.c:638
static void luascript_hook_end(lua_State *L)
Definition luascript.c:257
static void luascript_exec_check(lua_State *L, lua_Debug *ar)
Definition luascript.c:228
void luascript_vars_load(struct fc_lua *fcl, struct section_file *file, const char *section)
Definition luascript.c:764
const Direction * luascript_dir(enum direction8 dir)
Definition luascript.c:790
static void luascript_hook_start(lua_State *L)
Definition luascript.c:244
static const char * luascript_unsafe_symbols_permissive[]
Definition luascript.c:75
void luascript_log(struct fc_lua *fcl, enum log_level level, const char *format,...)
Definition luascript.c:410
void luascript_log_vargs(struct fc_lua *fcl, enum log_level level, const char *format, va_list args)
Definition luascript.c:423
void luascript_vars_save(struct fc_lua *fcl, struct section_file *file, const char *section)
Definition luascript.c:738
struct fc_lua * luascript_get_fcl(lua_State *L)
Definition luascript.c:367
struct fc_lua * luascript_new(luascript_log_func_t output_fct, bool secured_environment)
Definition luascript.c:333
static void luascript_blacklist(lua_State *L, const char *lsymbols[])
Definition luascript.c:279
int luascript_call(struct fc_lua *fcl, int narg, int nret, const char *code)
Definition luascript.c:579
#define LUASCRIPT_GLOBAL_VAR_NAME
Definition luascript.c:48
static void luascript_traceback_func_save(lua_State *L)
Definition luascript.c:206
void luascript_destroy(struct fc_lua *fcl)
Definition luascript.c:387
void(* luascript_log_func_t)(struct fc_lua *fcl, enum log_level level, const char *format,...) fc__attribute((__format__(__printf__
Definition luascript.h:39
void luascript_func_free(struct fc_lua *fcl)
void luascript_signal_free(struct fc_lua *fcl)
enum direction8 Direction
bool is_valid_dir(enum direction8 dir)
Definition map.c:1260
#define fc_calloc(n, esz)
Definition mem.h:38
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_strdup(str)
Definition mem.h:43
int len
Definition packhand.c:127
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
#define secfile_insert_str_noescape(secfile, string, path,...)
struct setting_list * level[OLEVELS_NUM]
Definition settings.c:190
luascript_log_func_t output_fct
Definition luascript.h:47
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:900
#define FALSE
Definition support.h:47
#define CLOCKS_PER_SEC
Definition timing.c:70