-- luatex-core security and io overloads .. -- if not modules then modules = { } end modules ['luatex-core'] = { -- version = 1.112, -- comment = 'companion to luatex', -- author = 'Hans Hagen & Luigi Scarso', -- copyright = 'LuaTeX Development Team', -- } LUATEXCOREVERSION = 1.161 -- we reflect the luatex version where changes happened -- This file overloads some Lua functions. The readline variants provide the same -- functionality as LuaTeX <= 1.04 and doing it this way permits us to keep the -- original io libraries clean. Performance is probably even a bit better now. -- We test for functions already being defined so that we don't overload ones that -- are provided in the startup script. local saferoption = status.safer_option local shellescape = status.shell_escape -- 0 (disabled) 1 (anything) 2 (restricted) local kpseused = status.kpse_used -- 0 1 if kpseused == 1 then local type = type local gsub = string.gsub local find = string.find local mt = getmetatable(io.stderr) local mt_lines = mt.lines local kpse_checkpermission = kpse.check_permission local kpse_recordinputfile = kpse.record_input_file local kpse_recordoutputfile = kpse.record_output_file local kpse_outputnameok = kpse.out_name_ok local kpse_inputnameok = kpse.in_name_ok local io_open = io.open local io_popen = kpse.popen or io.popen -- should have been be in the kpse namespace local io_lines = io.lines local fio_readline = fio.readline local write_nl = texio.write_nl io.saved_lines = io_lines -- always readonly mt.saved_lines = mt_lines -- always readonly -- The version below is different from the luatex one (which is probably made for -- LaTeX) because read should not be the default (e.g. when how == 'q'). Also in -- that version record doesn't always work for write. How about append? local function validinput(name,how) if type(how) ~= "string" or how == "" then how = "r" end return not find(how,"w") and kpse_inputnameok(name) and io_open(name,how) end local function validoutput(name,how) return type(how) == "string" and find(how,"w") and kpse_outputnameok(name) and io_open(name,how) end local function luatex_io_open(name,how) local handle = validinput(name,how) if handle then kpse_recordinputfile(name,"r") else handle = validoutput(name,how) if handle then kpse_recordoutputfile(name,"w") end end return handle end local function luatex_io_open_readonly(name,how) local handle = validinput(name,how) if handle then kpse_recordinputfile(name,"r") end return handle end -- local function luatex_io_popen(name,...) -- local okay, found = kpse_checkpermission(name) -- if okay and found then -- return io_popen(found,...) -- end -- end -- For some reason the gc doesn't kick in so we need to close explicitly -- so that the handle is flushed. local error, type = error, type local function luatex_io_lines(name,how) if type(name) == "string" then local handle = validinput(name,how) if handle then kpse_recordinputfile(name,"r") return function() local l = fio_readline(f) if not l then f:close() end return l end else -- for those who like it this way: error("patched 'io.lines' can't open '" .. name .. "'") end else return io_lines() end end local function luatex_io_readline(f) return function() return fio_readline(f) end end io.lines = luatex_io_lines mt.lines = luatex_io_readline io.open = luatex_io_open -- io.popen = luatex_io_popen io.popen = io_popen else -- we assume management elsewhere end -- maybe also only when in kpse mode if saferoption == 1 then local write_nl = texio.write_nl local format = string.format local function installdummy(str,f) local reported = false return function(...) if not reported then write_nl(format("safer option set, function %q is %s", str,f and "limited" or "disabled")) reported = true end if f then return f(...) end end end local function installlimit(str,f) local reported = false end os.execute = installdummy("os.execute") os.spawn = installdummy("os.spawn") os.exec = installdummy("os.exec") os.setenv = installdummy("os.setenv") os.tempdir = installdummy("os.tempdir") io.popen = installdummy("io.popen") io.open = installdummy("io.open",luatex_io_open_readonly) os.kpsepopen = io.popen -- because it's in the os namespace ... brr os.rename = installdummy("os.rename") os.remove = installdummy("os.remove") io.tmpfile = installdummy("io.tmpfile") io.output = installdummy("io.output") lfs.chdir = installdummy("lfs.chdir") lfs.lock = installdummy("lfs.lock") lfs.touch = installdummy("lfs.touch") lfs.rmdir = installdummy("lfs.rmdir") lfs.mkdir = installdummy("lfs.mkdir") debug = nil package.loaded.debug = nil -- os.[execute|os.spawn|os.exec] already are shellescape aware) end -- maybe also only when in kpse mode if saferoption == 1 or shellescape ~= 1 then package.loadlib = function() end package.searchers[4] = nil package.searchers[3] = nil if os.setenv then os.setenv = function(...) end -- Great, this will fail for some usage. end ffi = require('ffi') if ffi then for k, v in next, ffi do if k ~= 'gc' then ffi[k] = nil end end end ffi = nil -- A patch by LS for LaTeX per April 2022 but I'm, not sure if that breaks ConTeXt MKIV. I have -- no time nor motivation to test that right now so we'll see where it breaks. We have different -- loaders anyway and we don't test plain. package.loaded .ffi = nil -- Isn't that still nil then? package.preload.ffi = error -- Do errors always go there? end if md5 then local sum = md5.sum local gsub = string.gsub local format = string.format local byte = string.byte if not md5.sumhexa then function md5.sumhexa(k) return (gsub(sum(k), ".", function(c) return format("%02x",byte(c)) end)) end end if not md5.sumHEXA then function md5.sumHEXA(k) return (gsub(sum(k), ".", function(c) return format("%02X",byte(c)) end)) end end end -- compatibility: this might go away if not unpack then unpack = table.unpack end if not package.loaders then package.loaders = package.searchers end if not loadstring then loadstring = load end -- compatibility: this might stay if bit32 then -- lua 5.2: we're okay elseif utf8 then -- lua 5.3: bitwise.lua, v 1.24 2014/12/26 17:20:53 roberto bit32 = load ( [[ local select = select -- instead of: arg = { ... } bit32 = { bnot = function (a) return ~a & 0xFFFFFFFF end, band = function (x, y, z, ...) if not z then return ((x or -1) & (y or -1)) & 0xFFFFFFFF else local res = x & y & z for i=1,select("#",...) do res = res & select(i,...) end return res & 0xFFFFFFFF end end, bor = function (x, y, z, ...) if not z then return ((x or 0) | (y or 0)) & 0xFFFFFFFF else local res = x | y | z for i=1,select("#",...) do res = res | select(i,...) end return res & 0xFFFFFFFF end end, bxor = function (x, y, z, ...) if not z then return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF else local res = x ~ y ~ z for i=1,select("#",...) do res = res ~ select(i,...) end return res & 0xFFFFFFFF end end, btest = function (x, y, z, ...) if not z then return (((x or -1) & (y or -1)) & 0xFFFFFFFF) ~= 0 else local res = x & y & z for i=1,select("#",...) do res = res & select(i,...) end return (res & 0xFFFFFFFF) ~= 0 end end, lshift = function (a, b) return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF end, rshift = function (a, b) return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF end, arshift = function (a, b) a = a & 0xFFFFFFFF if b <= 0 or (a & 0x80000000) == 0 then return (a >> b) & 0xFFFFFFFF else return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF end end, lrotate = function (a ,b) b = b & 31 a = a & 0xFFFFFFFF a = (a << b) | (a >> (32 - b)) return a & 0xFFFFFFFF end, rrotate = function (a, b) b = -b & 31 a = a & 0xFFFFFFFF a = (a << b) | (a >> (32 - b)) return a & 0xFFFFFFFF end, extract = function (a, f, w) return (a >> f) & ~(-1 << (w or 1)) end, replace = function (a, v, f, w) local mask = ~(-1 << (w or 1)) return ((a & ~(mask << f)) | ((v & mask) << f)) & 0xFFFFFFFF end, } ]] ) elseif bit then -- luajit (for now) bit32 = load ( [[ local band, bnot, rshift, lshift = bit.band, bit.bnot, bit.rshift, bit.lshift bit32 = { arshift = bit.arshift, band = band, bnot = bnot, bor = bit.bor, bxor = bit.bxor, btest = function(...) return band(...) ~= 0 end, extract = function(a,f,w) return band(rshift(a,f),2^(w or 1)-1) end, lrotate = bit.rol, lshift = lshift, replace = function(a,v,f,w) local mask = 2^(w or 1)-1 return band(a,bnot(lshift(mask,f)))+lshift(band(v,mask),f) end, rrotate = bit.ror, rshift = rshift, } ]] ) else -- hope for the best or fail bit32 = require("bit32") end -- this is needed for getting require("socket") right do local loaded = package.loaded if not loaded.socket then loaded.socket = loaded["socket.core"] end if not loaded.mime then loaded.mime = loaded["mime.core"] end if not loaded.lfs then loaded.lfs = lfs end end do local lfsattributes = lfs.attributes local symlinkattributes = lfs.symlinkattributes -- these can now be done using lfs (was dead slow before) if not lfs.isfile then function lfs.isfile(name) local m = lfsattributes(name,"mode") return m == "file" or m == "link" end end if not lfs.isdir then function lfs.isdir(name) local m = lfsattributes(name,"mode") return m == "directory" end end -- shortnames have also be sort of dropped from kpse if not lfs.shortname then function lfs.shortname(name) return name end end -- now there is a target field, so ... if not lfs.readlink then function lfs.readlink(name) return symlinkattributes(name,"target") or nil end end end -- start omit if utilities and utilities.merger and utilities.merger.compact then local byte, format, gmatch, gsub = string.byte, string.format, string.gmatch, string.gsub local concat = table.concat local data = io.loaddata('luatex-core.lua') data = gsub(data,'%-%-%s*start%s*omit.-%-%-%s*stop%s*omit%s*','') data = gsub(data,'\r\n','\n') local t = { } local r = { } local n = 0 local s = utilities.merger.compact(data) -- no comments and less spaces t[#t+1] = '/* generated from and by luatex-core.lua */' t[#t+1] = '' t[#t+1] = '#include "lua.h"' t[#t+1] = '#include "lauxlib.h"' t[#t+1] = '' t[#t+1] = 'int load_luatex_core_lua (lua_State * L);' t[#t+1] = '' t[#t+1] = 'int load_luatex_core_lua (lua_State * L)' t[#t+1] = '{' t[#t+1] = ' static unsigned char luatex_core_lua[] = {' for c in gmatch(data,'.') do if n == 16 then n = 1 t[#t+1] = ' ' .. concat(r,', ') .. ',' else n = n + 1 end r[n] = format('0x%02x',byte(c)) end n = n + 1 r[n] = '0x00' t[#t+1] = ' ' .. concat(r,', ',1,n) t[#t+1] = ' };' -- t[#t+1] = format('unsigned int luatex_core_lua_len = 0x%x;',#d+1) t[#t+1] = ' return luaL_dostring(L, (const char*) luatex_core_lua);' t[#t+1] = '}' io.savedata('luatex-core.c',concat(t,'\n')) io.savedata('luatex-core-stripped.lua',s) end -- stop omit