Commit graph

14 commits

Author SHA1 Message Date
Brad Jorsch 84d96e843c LuaStandalone: Release functions when no longer referenced
The LuaStandalone interpreter needs to keep a mapping from integers
returned to PHP to the corresponding function. But if it never releases
these functions when PHP no longer has any reference to them, it can
result in Lua running out of memory if a module with a large number of
functions is invoked many times in one page.

The fix here is to track which function ids are referenced from PHP, and
periodically send the list to Lua so it can remove any that are no
longer used from its cache.

This also takes care of another issue where having multiple interpreter
instances and passing function objects from one into another could call
the wrong function in Lua.

Bug: 51886
Change-Id: I4f15841051f7748d1d6df24080949e5cbd88f217
2014-02-25 22:59:05 +00:00
Brad Jorsch f52136eada Handle invalid keys in Lua-to-PHP calls for LuaStandalone
PHP can't handle having arrays/objects or functions as keys in its
arrays, so make sure we don't try to pass them from Lua. Booleans aren't
really well-handled either, so let's disallow them too.

Also, add tests for proper stringification of floats and infinities when
those are used as keys.

Note this behavior change is needed to match the change in LuaSandbox
for fixing bug 54527, but isn't itself a security issue.

Change-Id: I1e2951bbe8cb78358650ad377bf7119fcac4485d
2014-01-14 10:03:42 -05:00
Brad Jorsch 71d9f600dc (bug 46294) Fix for Windows text-mode file handles
On Windows for LuaStandalone, the lua executable's standard output is a
text-mode file handle, even if the pipe is opened from PHP with the
binary flag. Which means that when Lua returns a "\n", it gets silently
rewritten to "\r\n" and the unserialization fails.

So, change the protocol for Lua→PHP messages to encode \r and \n (and \
itself, as the escape character) to avoid this issue.

Bug: 46294
Change-Id: I73b5f44e8aa0334f5fd03013dc027d1a57318349
2013-04-02 23:05:38 +00:00
Brad Jorsch 6b4cfd5b94 Fix LuaStandalone nil handling
In Lua, a table entry with a nil value is the same as a table entry that
doesn't exist. So when serializing for transfer to PHP, these keys will
be skipped. For a table as an associative array this isn't much of a
problem, but for a table as a list it means we have missing indexes.
Some of Lua's functions for handling "lists" (i.e. tables with numeric
keys) also have a problem when the list contains nils.

To work around these issues when passing argument lists and return value
lists, pass the number of elements along with the sparse list. On the
PHP end we can use this to fill in the missing nulls, and on the Lua
end we can pass this count to unpack() to avoid the problems on the Lua
side.

Change-Id: I858e3905a06e377693301da2b8bc534808f00e3e
2013-01-30 10:12:23 -05:00
Brad Jorsch bdacaabf21 Fix string extending
Built-in modules (e.g. the upcoming ustring) should be able to extend
the string metatable, but user modules (and the debug console) should
not be able to.

But currently built-in modules cannot extend the string metatable in
LuaStandalone, while in LuaSandbox the string metatable can be extended
in the console. Fix this and add unit tests.

Change-Id: I15f5598fed318f2fe26b08ec47e16053dddc13c4
2013-01-09 15:54:44 +00:00
Brad Jorsch 374972c924 Add Scribunto_LuaInterpreter::wrapPhpFunction
Currently, the only way for PHP to pass a function to Lua is to pass
back a function that it received from Lua. This means, for example, that
PHP cannot implement a Lua iterator function except by registering a
library holding the function or by using loadString.

This changeset adds Scribunto_LuaInterpreter::wrapPhpFunction (and
implements it for both LuaSandbox and LuaStandalone), which takes a PHP
callable and returns a Lua function wrapping it.

Note that fallback code is included so this does not depend on
I2e552799.

Change-Id: Ic0a98eec7cc17ef4b1acee032c0f42d617b998d2
2013-01-09 05:54:36 +00:00
Brad Jorsch eabb081c4f Handle non-finite numbers
There's no reason LuaStandalone cannot handle NaN and Inf.

Also, add some unit tests to check this.

Change-Id: I8570242a792e212489ad24dfc04fb1f2940190a5
2012-12-13 13:40:18 -05:00
Tim Starling d5bd1102d0 Fixed transmission of \0 from Lua standalone
* string.format() truncates the string at a null character, causing a
  deadlock when Lua attempts to send null characters to PHP. Use
  concatenation instead.
* Added test.
* Fixed an error reporting issue in the console, which I happened to
  notice at the same time as the above bug.

Change-Id: I2e6061a04512557492bffbd04bc09ca3bc1d80d6
2012-12-03 17:46:11 +11:00
Tim Starling 9859591271 Allow time functions in LuaStandalone
As in LuaSandbox I7e116c76

Change-Id: I783e2ae802999701ebcd4d1a1b3d113a21b4ca9e
2012-09-06 11:01:53 +10:00
Tim Starling c9c0aa6fce Peak memory usage, debug.traceback
* Use LuaSandbox::getPeakMemoryUsage() from r115086
* Fixed the debug.traceback function from da06273e, was nil

Change-Id: Iae4b195ffe25a522d4c37f9c8341e1d6ea3ae106
2012-04-30 16:37:41 +10:00
Tim Starling da06273ede Nicer errors with backtraces etc.
* Added error backtrace collection to MWServer:handleCall()
* When there is an error on parse, show a short and simple inline error
  message to the user, which when clicked, expands to a full error with
  HTML-formatted backtrace.
* When an error is encountered during module validation, have the code
  editor jump directly to the line. Requires r115011.
* Expose the code location of most errors to Scribunto, by parsing the
  standard error message format.
* During module validation, abbreviate the error location if the error
  is in the same module.
* Do not execute the module during validation, just parse it. Execution
  does not really work without an active parse operation in progress.
  It already caused a fatal error if you called require() from the main
  chunk, and problems would have become more visible as more
  parser-related APIs were added.
* LuaSandbox does not yet provide backtraces, but this is planned.

Change-Id: Id9f6564a41b310792b3fe3ebb527cbf8f8771bd1
2012-04-23 21:58:30 +10:00
tstarling cebe775ee8 Added more Lua environment features
Package library:

* Added a simulation of the Lua 5.1 package library.
* Removed mw.import(), replaced it with a package loader. Packages can be
  retrieved from the wiki, using require('Module:Foo'), or from files
  distributed with Scribunto, using require('foo'). The "Module:" prefix allows
  for source compatibility with existing Lua code.
* Added a couple of libraries from LuaForge: luabit and stringtools.
* Made fetchModuleFromParser() return null on error instead of throwing an
  exception, to more easily support the desired behaviour of the package loader,
  which needs to return null on error.
* Renamed mw.setupEnvironment() to mw.setup() since it is setting up things
  other than the environment now.
* In MWServer:handleRegisterLibrary(), remove the feature which interprets dots
  in library names, since LuaSandbox doesn't support this.

Improved module isolation and related refactoring:

* Expose restricted versions of getfenv() and setfenv() to user Lua code.
  Requires luasandbox r114952.
* Don't cache the export list returned by module execution for later function
  calls. This breaks isolation of #invoke calls, since the local variables are
  persistent.
* Removed ScribuntoFunctionBase and its children, since it doesn't really have
  a purpose if it can't cache anything. Instead, invoke functions using a module
  method called invoke().
* Removed Module::initialize(), replaced it with a validate() function. This is
  a more elegant interface and works better with the new module caching scheme.
* Use a Status object for the return value of Engine::validate() instead of an
  array. Use the formatting facilities of the Status class.

Other:

* Removed "too many returns" error, doesn't fit in with Lua conventions.
* Use the standalone engine by default, so that the extension will work without
  configuration for more people.
* Added an accessor for $engine->interpreter
* Fix mw.clone() to correctly clone metatables
* If the standalone interpreter exits due to an error, there are some contexts
  where the initial error will be caught and ignored, and the user will see the
  error from checkValid() instead. In this case, rethrow the original error for
  a more informative message.
* Load mw.lua into the initial standalone environment, to reduce code
  duplication between mw.lua and MWServer.lua.
* Fixed a bug in Scribunto_LuaStandaloneInterpreter::handleCall() for functions
  that return no results.
* Fixed a bug in encodeLuaVar() for strings with "\r". Added test case.
* In MWServer.lua, don't call error() for internal errors, instead just print
  the error and exit. This avoids a protocol violation when an error is
  encountered from within handleCall().
* Added lots of documentation. Lua doc comments are in LuaDoc format.

Change-Id: Ie2fd572c362bedf02f45d3fa5352a5280e034740
2012-04-18 13:46:18 +10:00
tstarling b0f00103e2 Added tests and fixed bugs
* Added unit tests for the two Lua interpreter classes
* Fixed a bug in checkType()
* Have Scribunto_LuaSandboxInterpreter throw an exception on construct
  when the extension doesn't exist, to match the standalone behaviour.
* In Scribunto_LuaSandboxInterpreter, removed debugging statements
  accidentally left in.
* Convert LuaSandboxTimeoutError to the appropriate common error
  message.
* Moved the option munging from the sandbox engine to the interpreter,
  so that the interpreter can be unit tested separately.
* Use /bin/sh instead of bash for lua_ulimit.sh, since dash is smaller
  and still supports ulimit.
* Use exec to run the lua binary, so that the vsize of the shell doesn't
  add to the memory limit.
* Added a quit function to the standalone interpreter. Unused at present.
* Don't add a comma after the last element of a table in a Lua
  expression.
* Make the SIGXCPU detection work: proc_open() runs the command via a
  shell, which reports signals in the child via the exit status, so
  proc_get_status() will never return a valid termsig element.
* In MWServer:call(), fixed a bug causing the return values to be
  wrapped in an array.
* Fixed a misunderstanding of what select() does.
* In MWServer:getStatus(), fixed indexes so that vsize will be correct.
  Removed RSS, since it wasn't used anyway and turns out to be measured
  in multiples of the page size, and I couldn't be bothered trying to
  fetch that from getconf. Return the PID and vsize as numbers rather
  than strings.
* Added a simple table dump feature to MWServer:debug().
* Fixed brackets in MWServer:tostring().
* Added missing Linux 32-bit binary.

Change-Id: Ibf5f4656b1c0a9f81287d363184c3fe9d2abdafd
2012-04-16 14:41:08 +10:00
tstarling 54cedd69b8 Introduced standalone interpreter, implemented module isolation
* Introduced a Lua implementation based on shelling out to a standard Lua binary.
* Bundled several Lua binaries for common platforms. I haven't added a 32-bit Linux binary yet, but that will come.
* Refactored the existing Lua class, bringing out functionality common to all Lua implementations into a set of common base classes.
* Moved the bulk of the implementation-specific functionality into a set of "interpreter" classes.
* Renamed LuaSandboxEngine to Scribunto_LuaSandboxEngine
* Don't create an engine object unconditionally when the ParserLimitReport hook is called.
* Implemented isolation of module global variable namespaces. This means that separate {{#invoke}} calls can't pass data to each other -- this was a desired feature in planning since it allows more flexibility in wikitext parser design. Isolation for mw.import() means that modules cannot accidentally create global variables which affect other modules -- exports are solely via the return value.

Change-Id: I3fa35651fe5b1fbfd85adeadc220b1ea31cd6f0b
2012-04-13 20:45:26 +10:00