Freeciv-3.3
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_CHECKINTERVAL 10000
45
46/* The name used for the freeciv lua struct saved in the lua state. */
47#define LUASCRIPT_GLOBAL_VAR_NAME "__fcl"
48
49/*****************************************************************************
50 Unsafe Lua builtin symbols that we to remove access to.
51
52 If Freeciv's Lua version changes, you have to check how the set of
53 unsafe functions and modules changes in the new version. Update the list of
54 loaded libraries in luascript_lualibs, then update the unsafe symbols
55 blacklist in luascript_unsafe_symbols.
56
57 Once the variables are updated for the new version, update the value of
58 LUASCRIPT_SECURE_LUA_VERSION
59
60 In general, unsafe is all functionality that gives access to:
61 * Reading files and running processes
62 * Loading lua files or libraries
63*****************************************************************************/
64#define LUASCRIPT_SECURE_LUA_VERSION1 503
65#define LUASCRIPT_SECURE_LUA_VERSION2 504
66
67static const char *luascript_unsafe_symbols_secure[] = {
68 "debug",
69 "dofile",
70 "loadfile",
71 NULL
72};
73
75 "debug",
76 "dofile",
77 "loadfile",
78 NULL
79};
80
81#if LUA_VERSION_NUM != LUASCRIPT_SECURE_LUA_VERSION1 && LUA_VERSION_NUM != LUASCRIPT_SECURE_LUA_VERSION2
82#warning "The script runtime's unsafe symbols information is not up to date."
83#warning "This can be a big security hole!"
84#endif
85
86/*****************************************************************************
87 Lua libraries to load (all default libraries, excluding operating system
88 and library loading modules). See linit.c in Lua 5.1 for the default list.
89*****************************************************************************/
90#if LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504
92 /* Using default libraries excluding: package, io, os, and bit32 */
93 {"_G", luaopen_base},
100 {NULL, NULL}
101};
102
104 /* Using default libraries excluding: package, and bit32 */
105 {"_G", luaopen_base},
114 {NULL, NULL}
115};
116#else /* LUA_VERSION_NUM */
117#error "Unsupported lua version"
118#endif /* LUA_VERSION_NUM */
119
120static int luascript_report(struct fc_lua *fcl, int status, const char *code);
124static void luascript_hook_start(lua_State *L);
125static void luascript_hook_end(lua_State *L);
126static void luascript_openlibs(lua_State *L, const luaL_Reg *llib);
127static void luascript_blacklist(lua_State *L, const char *lsymbols[]);
128
129/**********************************************************************/
132static int luascript_report(struct fc_lua *fcl, int status,
133 const char *code)
134{
136 fc_assert_ret_val(fcl->state, -1);
137
138 if (status) {
139 struct astring str = ASTRING_INIT;
140 const char *msg;
141 int lineno;
142
143 if (!(msg = lua_tostring(fcl->state, -1))) {
144 msg = "(error with no message)";
145 }
146
147 /* Add error message. */
148 astr_add_line(&str, "lua error:");
149 astr_add_line(&str, "\t%s", msg);
150
151 if (code) {
152 /* Add lines around the place the parse error is. */
153 if (sscanf(msg, "%*[^:]:%d:", &lineno) == 1) {
154 const char *begin, *end;
155 int i;
156
157 astr_add(&str, "\n");
158
159 i = 1;
160 for (begin = code; *begin != '\0';) {
161 int len;
162
163 end = strchr(begin, '\n');
164 if (end) {
165 len = end - begin;
166 } else {
167 len = strlen(begin);
168 }
169
170 if (abs(lineno - i) <= 3) {
171 const char *indicator;
172
173 indicator = (lineno == i) ? "-->" : " ";
174
175 astr_add_line(&str, "\t%s%3d:\t%*.*s",
176 indicator, i, len, len, begin);
177 }
178
179 i++;
180
181 if (end) {
182 begin = end + 1;
183 } else {
184 break;
185 }
186 }
187
188 astr_add(&str, "\n");
189 }
190 }
191
193
194 astr_free(&str);
195
196 lua_pop(fcl->state, 1);
197 }
198
199 return status;
200}
201
202/**********************************************************************/
206{
207 /* Find the debug.traceback function, if available */
208 lua_getglobal(L, "debug");
209 if (lua_istable(L, -1)) {
210 lua_getfield(L, -1, "traceback");
211 lua_setfield(L, LUA_REGISTRYINDEX, "freeciv_traceback");
212 }
213 lua_pop(L, 1); /* pop debug */
214}
215
216/**********************************************************************/
220{
221 lua_getfield(L, LUA_REGISTRYINDEX, "freeciv_traceback");
222}
223
224/**********************************************************************/
228{
230
231 lua_getfield(L, LUA_REGISTRYINDEX, "freeciv_exec_clock");
233 lua_pop(L, 1);
234 if ((float)(clock() - exec_clock) / CLOCKS_PER_SEC
235 > game.lua_timeout) {
236 luaL_error(L, _("Execution time limit exceeded in script"));
237 }
238}
239
240/**********************************************************************/
244{
245#if LUASCRIPT_CHECKINTERVAL
246 /* Store clock timestamp in the registry */
248 lua_setfield(L, LUA_REGISTRYINDEX, "freeciv_exec_clock");
250#endif
251}
252
253/**********************************************************************/
257{
258#if LUASCRIPT_CHECKINTERVAL
260#endif
261}
262
263/**********************************************************************/
267{
268 /* set results to global table */
269 for (; llib->func; llib++) {
270 luaL_requiref(L, llib->name, llib->func, 1);
271 lua_pop(L, 1); /* remove lib */
272 }
273}
274
275/**********************************************************************/
278static void luascript_blacklist(lua_State *L, const char *lsymbols[])
279{
280 int i;
281
282 for (i = 0; lsymbols[i] != NULL; i++) {
283 lua_pushnil(L);
285 }
286}
287
288/**********************************************************************/
291int luascript_error(lua_State *L, const char *format, ...)
292{
294 int ret;
295
296 va_start(vargs, format);
297 ret = luascript_error_vargs(L, format, vargs);
298 va_end(vargs);
299
300 return ret;
301}
302
303/**********************************************************************/
309{
310 fc_assert_ret_val(L != NULL, -1);
311
312 luaL_where(L, 1);
313 lua_pushvfstring(L, format, vargs);
314 lua_concat(L, 2);
315
316 return lua_error(L);
317}
318
319/**********************************************************************/
324int luascript_arg_error(lua_State *L, int narg, const char *msg)
325{
326 return luaL_argerror(L, narg, msg);
327}
328
329/**********************************************************************/
334{
335 struct fc_lua *fcl = fc_calloc(1, sizeof(*fcl));
336
337 fcl->state = luaL_newstate();
338 if (!fcl->state) {
339 FC_FREE(fcl);
340 return NULL;
341 }
342 fcl->output_fct = output_fct;
343 fcl->caller = NULL;
344
349 } else {
353 }
354
355 /* Save the freeciv lua struct in the lua state. */
359
360 return fcl;
361}
362
363/**********************************************************************/
367{
368 struct fc_lua *fcl;
369
371
372 /* Get the freeciv lua struct from the lua state. */
375 fcl = lua_touserdata(L, -1);
376
377 /* This is an error! */
379
380 return fcl;
381}
382
383/**********************************************************************/
387{
388 if (fcl) {
389 fc_assert_ret(fcl->caller == NULL);
390
391 /* Free function data. */
393
394 /* Free signal data. */
396
397 /* Free lua state. */
398 if (fcl->state) {
399 lua_gc(fcl->state, LUA_GCCOLLECT, 0); /* Collected garbage */
400 lua_close(fcl->state);
401 }
402 free(fcl);
403 }
404}
405
406/**********************************************************************/
410 const char *format, ...)
411{
412 va_list args;
413
414 va_start(args, format);
415 luascript_log_vargs(fcl, level, format, args);
416 va_end(args);
417}
418
419/**********************************************************************/
423 const char *format, va_list args)
424{
425 char buf[1024];
426
429
430 fc_vsnprintf(buf, sizeof(buf), format, args);
431
432 if (fcl->output_fct) {
433 fcl->output_fct(fcl, level, "%s", buf);
434 } else {
435 log_base(level, "%s", buf);
436 }
437}
438
439/**********************************************************************/
442void luascript_pop_returns(struct fc_lua *fcl, const char *func_name,
443 int nreturns, enum api_types *preturn_types,
444 va_list args)
445{
446 int i;
447 lua_State *L;
448
450 fc_assert_ret(fcl->state);
451 L = fcl->state;
452
453 for (i = 0; i < nreturns; i++) {
455
457
458 switch (type) {
459 case API_TYPE_INT:
460 {
461 int isnum;
462 int *pres = va_arg(args, int*);
463
464 *pres = lua_tointegerx(L, -1, &isnum);
465 if (!isnum) {
466 log_error("Return value from lua function %s is a %s, want int",
468 }
469 }
470 break;
471 case API_TYPE_BOOL:
472 {
473 bool *pres = va_arg(args, bool*);
474 *pres = lua_toboolean(L, -1);
475 }
476 break;
477 case API_TYPE_STRING:
478 {
479 char **pres = va_arg(args, char**);
480
481 if (lua_isstring(L, -1)) {
482 *pres = fc_strdup(lua_tostring(L, -1));
483 }
484 }
485 break;
486 default:
487 {
488 void **pres = va_arg(args, void**);
489
490 *pres = tolua_tousertype(fcl->state, -1, NULL);
491 }
492 break;
493 }
494 lua_pop(L, 1);
495 }
496}
497
498/**********************************************************************/
501void luascript_push_args(struct fc_lua *fcl, int nargs,
502 enum api_types *parg_types, va_list args)
503{
504 int i;
505
507 fc_assert_ret(fcl->state);
508
509 for (i = 0; i < nargs; i++) {
510 enum api_types type = parg_types[i];
511
513
514 switch (type) {
515 case API_TYPE_INT:
516 {
517 lua_Integer arg = va_arg(args, lua_Integer);
518 lua_pushinteger(fcl->state, arg);
519 }
520 break;
521 case API_TYPE_BOOL:
522 {
523 int arg = va_arg(args, int);
524 lua_pushboolean(fcl->state, arg);
525 }
526 break;
527 case API_TYPE_STRING:
528 {
529 const char *arg = va_arg(args, const char*);
530 lua_pushstring(fcl->state, arg);
531 }
532 break;
533 default:
534 {
535 const char *name;
536 void *arg;
537
539
540 arg = va_arg(args, void*);
541 tolua_pushusertype(fcl->state, arg, name);
542 }
543 break;
544 }
545 }
546}
547
548/**********************************************************************/
552bool luascript_check_function(struct fc_lua *fcl, const char *funcname)
553{
554 bool defined;
555
557 fc_assert_ret_val(fcl->state, FALSE);
558
559 lua_getglobal(fcl->state, funcname);
560 defined = lua_isfunction(fcl->state, -1);
561 lua_pop(fcl->state, 1);
562
563 return defined;
564}
565
566/**********************************************************************/
578int luascript_call(struct fc_lua *fcl, int narg, int nret,
579 const char *code)
580{
581 int status;
582 int base; /* Index of function to call */
583 int traceback = 0; /* Index of traceback function */
584
586 fc_assert_ret_val(fcl->state, -1);
587
588 base = lua_gettop(fcl->state) - narg;
589
590 /* Find the traceback function, if available */
592 if (lua_isfunction(fcl->state, -1)) {
593 lua_insert(fcl->state, base); /* insert traceback before function */
594 traceback = base;
595 } else {
596 lua_pop(fcl->state, 1); /* pop non-function traceback */
597 }
598
600 status = lua_pcall(fcl->state, narg, nret, traceback);
601 luascript_hook_end(fcl->state);
602
603 if (status) {
604 luascript_report(fcl, status, code);
605 }
606
607 if (traceback) {
608 lua_remove(fcl->state, traceback);
609 }
610
611 return status;
612}
613
614/**********************************************************************/
617int luascript_do_string(struct fc_lua *fcl, const char *str,
618 const char *name)
619{
620 int status;
621
623 fc_assert_ret_val(fcl->state, -1);
624
625 status = luaL_loadbuffer(fcl->state, str, strlen(str), name);
626 if (status) {
627 luascript_report(fcl, status, str);
628 } else {
629 status = luascript_call(fcl, 0, 0, str);
630 }
631 return status;
632}
633
634/**********************************************************************/
637int luascript_do_file(struct fc_lua *fcl, const char *filename)
638{
639 int status;
640
642 fc_assert_ret_val(fcl->state, -1);
643
644 status = luaL_loadfile(fcl->state, filename);
645 if (status) {
646 luascript_report(fcl, status, NULL);
647 } else {
648 status = luascript_call(fcl, 0, 0, NULL);
649 }
650 return status;
651}
652
653
654/**********************************************************************/
658 const char *callback_name,
659 int nargs, enum api_types *parg_types,
660 va_list args)
661{
662 bool stop_emission = FALSE;
663
665 fc_assert_ret_val(fcl->state, FALSE);
666
667 /* The function name */
669
670 if (!lua_isfunction(fcl->state, -1)) {
671 luascript_log(fcl, LOG_ERROR, "lua error: Unknown callback '%s'",
673 lua_pop(fcl->state, 1);
674 return FALSE;
675 }
676
677 luascript_log(fcl, LOG_DEBUG, "lua callback: '%s'", callback_name);
678
679 luascript_push_args(fcl, nargs, parg_types, args);
680
681 /* Call the function with nargs arguments, return 1 results */
682 if (luascript_call(fcl, nargs, 1, NULL)) {
683 return FALSE;
684 }
685
686 /* Shall we stop the emission of this signal? */
687 if (lua_isboolean(fcl->state, -1)) {
688 stop_emission = lua_toboolean(fcl->state, -1);
689 }
690 lua_pop(fcl->state, 1); /* pop return value */
691
692 return stop_emission;
693}
694
695/**********************************************************************/
700void luascript_remove_exported_object(struct fc_lua *fcl, void *object)
701{
702 if (fcl && fcl->state) {
703 fc_assert_ret(object != NULL);
704
705 /* The following is similar to tolua_release(..) in src/lib/tolua_map.c */
706 /* Find the userdata representing 'object' */
707 lua_pushstring(fcl->state, "tolua_ubox");
708 /* stack: ubox */
710 /* stack: ubox u */
711 lua_pushlightuserdata(fcl->state, object);
712 /* stack: ubox ubox[u] */
713 lua_rawget(fcl->state, -2);
714
715 if (!lua_isnil(fcl->state, -1)) {
716 fc_assert(object == tolua_tousertype(fcl->state, -1, NULL));
717 /* Change API type to 'Nonexistent' */
718 /* stack: ubox ubox[u] mt */
719 tolua_getmetatable(fcl->state, "Nonexistent");
720 lua_setmetatable(fcl->state, -2);
721 /* Set the userdata payload to NULL */
722 *((void **)lua_touserdata(fcl->state, -1)) = NULL;
723 /* Remove from ubox */
724 /* stack: ubox ubox[u] u */
725 lua_pushlightuserdata(fcl->state, object);
726 /* stack: ubox ubox[u] u nil */
727 lua_pushnil(fcl->state);
728 lua_rawset(fcl->state, -4);
729 }
730 lua_pop(fcl->state, 2);
731 }
732}
733
734/**********************************************************************/
737void luascript_vars_save(struct fc_lua *fcl, struct section_file *file,
738 const char *section)
739{
740 fc_assert_ret(file);
742 fc_assert_ret(fcl->state);
743
744 lua_getglobal(fcl->state, "_freeciv_state_dump");
745 if (luascript_call(fcl, 0, 1, NULL) == 0) {
746 const char *vars;
747
748 vars = lua_tostring(fcl->state, -1);
749 lua_pop(fcl->state, 1);
750
751 if (vars) {
753 }
754 } else {
755 /* _freeciv_state_dump in tolua_game.pkg is busted */
756 luascript_log(fcl, LOG_ERROR, "lua error: Failed to dump variables");
757 }
758}
759
760/**********************************************************************/
763void luascript_vars_load(struct fc_lua *fcl, struct section_file *file,
764 const char *section)
765{
766 const char *vars;
767
768 fc_assert_ret(file);
770 fc_assert_ret(fcl->state);
771
772 vars = secfile_lookup_str_default(file, "", "%s", section);
774}
775
776/* FIXME: tolua-5.2 does not create a destructor for dynamically
777 * allocated objects in non-C++ code but tries to call it. */
778/* Thus, avoid returning any non-basic types. If you need them, put here
779 * constructors returning them in lua_Object registering their destructor
780 * in tolua system. (None yet.) */
781
782/* To avoid the complexity and overheads of creating such objects for
783 * 4-bit enums, here is a helper function to return Direction objects. */
784/********************************************************************//******
785 Returns a pointer to a given value of enum direction8 (always the same
786 address for the same value), or NULL if the direction is invalid
787 on the current map.
788****************************************************************************/
790{
791 static const
795 if (is_valid_dir(dir)) {
796 return &etalon[dir];
797 } else {
798 return NULL;
799 }
800}
void astr_free(struct astring *astr)
Definition astring.c:148
void astr_add_line(struct astring *astr, const char *format,...)
Definition astring.c:283
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:271
#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:76
static void base(QVariant data1, QVariant data2)
Definition dialogs.cpp:2956
#define _(String)
Definition fcintl.h:67
struct civ_game game
Definition game.c:61
GType type
Definition repodlgs.c:1313
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:192
#define fc_assert(condition)
Definition log.h:177
#define fc_assert_ret_val(condition, val)
Definition log.h:195
#define log_base(level, message,...)
Definition log.h:95
log_level
Definition log.h:29
@ LOG_ERROR
Definition log.h:31
@ LOG_DEBUG
Definition log.h:35
#define log_error(message,...)
Definition log.h:104
static int luascript_report(struct fc_lua *fcl, int status, const char *code)
Definition luascript.c:132
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:442
void luascript_push_args(struct fc_lua *fcl, int nargs, enum api_types *parg_types, va_list args)
Definition luascript.c:501
static void luascript_traceback_func_push(lua_State *L)
Definition luascript.c:219
static const char * luascript_unsafe_symbols_secure[]
Definition luascript.c:67
bool luascript_check_function(struct fc_lua *fcl, const char *funcname)
Definition luascript.c:552
int luascript_error_vargs(lua_State *L, const char *format, va_list vargs)
Definition luascript.c:308
#define LUASCRIPT_CHECKINTERVAL
Definition luascript.c:44
static void luascript_openlibs(lua_State *L, const luaL_Reg *llib)
Definition luascript.c:266
int luascript_arg_error(lua_State *L, int narg, const char *msg)
Definition luascript.c:324
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:657
void luascript_remove_exported_object(struct fc_lua *fcl, void *object)
Definition luascript.c:700
int luascript_do_string(struct fc_lua *fcl, const char *str, const char *name)
Definition luascript.c:617
int luascript_error(lua_State *L, const char *format,...)
Definition luascript.c:291
int luascript_do_file(struct fc_lua *fcl, const char *filename)
Definition luascript.c:637
static void luascript_hook_end(lua_State *L)
Definition luascript.c:256
static void luascript_exec_check(lua_State *L, lua_Debug *ar)
Definition luascript.c:227
void luascript_vars_load(struct fc_lua *fcl, struct section_file *file, const char *section)
Definition luascript.c:763
const Direction * luascript_dir(enum direction8 dir)
Definition luascript.c:789
static void luascript_hook_start(lua_State *L)
Definition luascript.c:243
static const char * luascript_unsafe_symbols_permissive[]
Definition luascript.c:74
void luascript_log(struct fc_lua *fcl, enum log_level level, const char *format,...)
Definition luascript.c:409
void luascript_log_vargs(struct fc_lua *fcl, enum log_level level, const char *format, va_list args)
Definition luascript.c:422
void luascript_vars_save(struct fc_lua *fcl, struct section_file *file, const char *section)
Definition luascript.c:737
struct fc_lua * luascript_get_fcl(lua_State *L)
Definition luascript.c:366
struct fc_lua * luascript_new(luascript_log_func_t output_fct, bool secured_environment)
Definition luascript.c:332
static void luascript_blacklist(lua_State *L, const char *lsymbols[])
Definition luascript.c:278
int luascript_call(struct fc_lua *fcl, int narg, int nret, const char *code)
Definition luascript.c:578
#define LUASCRIPT_GLOBAL_VAR_NAME
Definition luascript.c:47
static void luascript_traceback_func_save(lua_State *L)
Definition luascript.c:205
void luascript_destroy(struct fc_lua *fcl)
Definition luascript.c:386
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:1401
#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
int lua_timeout
Definition game.h:105
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:886
#define FALSE
Definition support.h:47
#define CLOCKS_PER_SEC
Definition timing.c:70