environ.lua 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. --[[
  2. SaferLua [safer_lua]
  3. ====================
  4. Copyright (C) 2017-2020 Joachim Stolberg
  5. AGPL v3
  6. See LICENSE.txt for more information
  7. environ.lua:
  8. ]]--
  9. safer_lua.MaxCodeSize = 5000 -- size if source code in bytes
  10. safer_lua.MaxTableSize = 1000 -- sum over all table sizes
  11. safer_lua.MaxExeTime = 5000 -- max. execution time in us
  12. local function memsize()
  13. return safer_lua.MaxTableSize
  14. end
  15. local function range(from, to)
  16. return function(expired_at,last)
  17. assert(expired_at > minetest.get_us_time(), "Runtime limit exceeded")
  18. if last >= to then
  19. return nil
  20. else
  21. return last+1
  22. end
  23. end, minetest.get_us_time() + safer_lua.MaxExeTime, from-1
  24. end
  25. local BASE_ENV = {
  26. Array = safer_lua.Array,
  27. Store = safer_lua.Store,
  28. Set = safer_lua.Set,
  29. memsize = memsize,
  30. range = range,
  31. math = {
  32. floor = math.floor,
  33. abs = math.abs,
  34. max = math.max,
  35. min = math.min,
  36. random = math.random,
  37. },
  38. string = {
  39. byte = string.byte,
  40. char = string.char,
  41. find = string.find,
  42. format = string.format,
  43. gmatch = string.gmatch,
  44. gsub = string.gsub,
  45. len = string.len,
  46. lower = string.lower,
  47. match = string.match,
  48. rep = string.rep,
  49. sub = string.sub,
  50. upper = string.upper,
  51. split = function(str, separator, include_empty, max_splits, sep_is_pattern)
  52. if separator == "" then separator = " " end
  53. return safer_lua.Array(unpack(string.split(str, separator, include_empty, max_splits, sep_is_pattern)))
  54. end,
  55. trim = string.trim,
  56. },
  57. tonumber = tonumber,
  58. tostring = tostring,
  59. unpack = unpack,
  60. type = type,
  61. ticks = 0,
  62. }
  63. local function map(dest, source)
  64. for k,v in pairs(source) do
  65. dest[k] = v
  66. end
  67. return dest
  68. end
  69. local function calc_used_mem_size(env)
  70. local size = 0
  71. for key,val in pairs(env) do
  72. if type(val) == "table" and val.size ~= nil then
  73. size = size + val.size() or 0
  74. end
  75. end
  76. return size
  77. end
  78. function safer_lua.config(max_code_size, max_table_size)
  79. safer_lua.MaxCodeSize = max_code_size
  80. safer_lua.MaxTableSize = max_table_size
  81. end
  82. local function format_error_str(str, label)
  83. local tbl = {}
  84. for s in str:gmatch("[^\r\n]+") do
  85. s = s:match("^%s*(.-)%s*$")
  86. if s:find("function 'xpcall'") then
  87. break
  88. elseif s:find(".-%.lua:%d+:(.+)") then
  89. local err = s:gsub(".-%.lua:%d+:%s*(.+)", "extern: %1")
  90. table.insert(tbl, err)
  91. elseif s:find('%[string ".-"%]') then
  92. local line, err = s:match('^%[string ".-"%]:(%d+): (.+)$')
  93. table.insert(tbl, label..":"..line..": "..err)
  94. elseif s:find('%(load%):(%d+):') then
  95. local line, err = s:match('%(load%):(%d+): (.+)$')
  96. table.insert(tbl, label..":"..line..": "..err)
  97. end
  98. end
  99. return "Error: "..table.concat(tbl, "\n >> ")
  100. end
  101. local function format_error(err, label)
  102. if err:find("stack overflow") then
  103. return "Error: Stack overflow due to recursive function calls!"
  104. end
  105. return format_error_str(err, label)
  106. end
  107. local function compile(pos, text, label, err_clbk)
  108. if safer_lua:check(pos, text, label, err_clbk) == 0 then
  109. text = text:gsub("%$", "S:")
  110. local code, err = loadstring(text)
  111. if not code then
  112. err_clbk(pos, format_error(err, label))
  113. else
  114. return code
  115. end
  116. end
  117. end
  118. -------------------------------------------------------------------------------
  119. -- Standard init/loop controller
  120. -------------------------------------------------------------------------------
  121. function safer_lua.init(pos, init, loop, environ, err_clbk)
  122. if (#init + #loop) > safer_lua.MaxCodeSize then
  123. err_clbk(pos, "Error: Code size limit exceeded")
  124. return
  125. end
  126. local code = compile(pos, init, "init", err_clbk, 0)
  127. if code then
  128. local env = table.copy(BASE_ENV)
  129. env.S = {}
  130. env.S = map(env.S, environ)
  131. setfenv(code, env)
  132. local res, err = xpcall(code, debug.traceback)
  133. if not res then
  134. err_clbk(pos, format_error(err, "init"))
  135. else
  136. env = getfenv(code)
  137. code = compile(pos, loop, "loop", err_clbk)
  138. if code then
  139. setfenv(code, env)
  140. return code
  141. end
  142. end
  143. end
  144. end
  145. function safer_lua.run_loop(pos, elapsed, code, err_clbk)
  146. local env = getfenv(code)
  147. env.elapsed = elapsed
  148. if elapsed < 0 then -- event?
  149. env.event = true
  150. else
  151. env.event = false
  152. env.ticks = env.ticks + 1
  153. end
  154. local res, err = xpcall(code, debug.traceback)
  155. if calc_used_mem_size(env) > safer_lua.MaxTableSize then
  156. err_clbk(pos, "Error: Data memory limit exceeded")
  157. return false
  158. end
  159. if not res then
  160. err_clbk(pos, format_error(err, "loop"))
  161. return false
  162. end
  163. return true
  164. end
  165. -------------------------------------------------------------------------------
  166. -- Endless/Coroutine controller
  167. -------------------------------------------------------------------------------
  168. local function thread(pos, code, err_clbk)
  169. while true do
  170. local res, err = xpcall(code, debug.traceback)
  171. if not res then
  172. err_clbk(pos, format_error(err, "loop"))
  173. return false
  174. end
  175. local env = getfenv(code)
  176. if calc_used_mem_size(env) > safer_lua.MaxTableSize then
  177. err_clbk(pos, "Error: Memory limit exceeded")
  178. return false
  179. end
  180. coroutine.yield()
  181. end
  182. end
  183. function safer_lua.co_create(pos, init, loop, environ, err_clbk)
  184. local code = safer_lua.init(pos, init, loop, environ, err_clbk)
  185. return coroutine.create(thread), code
  186. end
  187. function safer_lua.co_resume(pos, co, code, err_clbk)
  188. local res, err = coroutine.resume(co, pos, code, err_clbk)
  189. if not res then
  190. err_clbk(pos, format_error(err, "loop"))
  191. return false
  192. end
  193. return true
  194. end