https://www.mediawiki.org/wiki/Special:Code/MediaWiki/115020
Revision: 115020
Author: tstarling
Date: 2012-04-24 10:39:43 +0000 (Tue, 24 Apr 2012)
Log Message:
-----------
Expose Lua backtraces via a new luaTrace property in LuaRuntimeError. The array
format is identical to the one implemented in the standalone interpreter in
da06273e.
Modified Paths:
--------------
trunk/php/luasandbox/data_conversion.c
trunk/php/luasandbox/luasandbox.c
trunk/php/luasandbox/php_luasandbox.h
Modified: trunk/php/luasandbox/data_conversion.c
===================================================================
--- trunk/php/luasandbox/data_conversion.c 2012-04-24 00:10:27 UTC (rev
115019)
+++ trunk/php/luasandbox/data_conversion.c 2012-04-24 10:39:43 UTC (rev
115020)
@@ -17,6 +17,7 @@
zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
static int luasandbox_free_zval_userdata(lua_State * L);
static int luasandbox_push_hashtable(lua_State * L, HashTable * ht);
+static int luasandbox_has_error_marker(lua_State * L, int index, void *
marker);
extern zend_class_entry *luasandboxfunction_ce;
extern zend_class_entry *luasandboxplaceholder_ce;
@@ -27,6 +28,10 @@
*/
int luasandbox_fatal_error_marker = 0;
+/**
+ * Same as luasandbox_fatal_error_marker but for trace errors
+ */
+int luasandbox_trace_error_marker = 0;
/** {{{ luasandbox_data_conversion_init
*
@@ -393,24 +398,32 @@
*/
int luasandbox_is_fatal(lua_State * L, int index)
{
+ return luasandbox_has_error_marker(L, index,
&luasandbox_fatal_error_marker);
+}
+/* }}} */
+
+/** {{{ luasandbox_is_trace_error
+ */
+int luasandbox_is_trace_error(lua_State * L, int index)
+{
+ return luasandbox_has_error_marker(L, index,
&luasandbox_trace_error_marker);
+}
+/* }}} */
+
+/** {{{
+ *
+ * Check if the error at the given stack index has a given marker userdata
+ */
+static int luasandbox_has_error_marker(lua_State * L, int index, void * marker)
+{
void * ud;
- int haveIndex2 = 0;
-
if (!lua_istable(L, index)) {
return 0;
}
-
lua_rawgeti(L, index, 1);
ud = lua_touserdata(L, -1);
lua_pop(L, 1);
- if (ud != &luasandbox_fatal_error_marker) {
- return 0;
- }
-
- lua_rawgeti(L, index, 2);
- haveIndex2 = !lua_isnil(L, -1);
- lua_pop(L, 1);
- return haveIndex2;
+ return ud == marker;
}
/* }}} */
@@ -428,7 +441,10 @@
const char * luasandbox_error_to_string(lua_State * L, int index)
{
const char * s;
- if (luasandbox_is_fatal(L, index)) {
+ if (index < 0) {
+ index += lua_gettop(L) + 1;
+ }
+ if (luasandbox_is_fatal(L, index) || luasandbox_is_trace_error(L,
index)) {
lua_rawgeti(L, index, 2);
s = lua_tostring(L, -1);
lua_pop(L, 1);
@@ -443,3 +459,64 @@
}
/* }}} */
+/** {{{ luasandbox_attach_trace
+ *
+ * Error callback function for lua_pcall(): wrap the error value in a table
that
+ * includes backtrace information.
+ */
+int luasandbox_attach_trace(lua_State * L)
+{
+ if (luasandbox_is_fatal(L, 1)) {
+ // Pass fatals through unaltered
+ return 1;
+ }
+
+ // Create the table and put the marker in it as element 1
+ lua_createtable(L, 0, 3);
+ lua_pushlightuserdata(L, &luasandbox_trace_error_marker);
+ lua_rawseti(L, -2, 1);
+
+ // Swap the table with the input value, so that the value is on the top,
+ // then put the value in the table as element 2
+ lua_insert(L, -2);
+ lua_rawseti(L, -2, 2);
+
+ // Put the backtrace in element 3
+ luasandbox_push_structured_trace(L, 1);
+ lua_rawseti(L, -2, 3);
+
+ return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_push_structured_trace
+ *
+ * Make a table representing the current backtrace and push it to the stack.
+ * "level" is the call stack level to start at, 1 for the current function.
+ */
+void luasandbox_push_structured_trace(lua_State * L, int level)
+{
+ lua_Debug ar;
+ lua_newtable(L);
+ int i;
+
+ for (i = level; lua_getstack(L, i, &ar); i++) {
+ lua_getinfo(L, "nSl", &ar);
+ lua_createtable(L, 0, 8);
+ lua_pushstring(L, ar.short_src);
+ lua_setfield(L, -2, "short_src");
+ lua_pushstring(L, ar.what);
+ lua_setfield(L, -2, "what");
+ lua_pushnumber(L, ar.currentline);
+ lua_setfield(L, -2, "currentline");
+ lua_pushstring(L, ar.name);
+ lua_setfield(L, -2, "name");
+ lua_pushstring(L, ar.namewhat);
+ lua_setfield(L, -2, "namewhat");
+ lua_pushnumber(L, ar.linedefined);
+ lua_setfield(L, -2, "linedefined");
+ lua_rawseti(L, -2, i - level + 1);
+ }
+}
+/* }}} */
+
Modified: trunk/php/luasandbox/luasandbox.c
===================================================================
--- trunk/php/luasandbox/luasandbox.c 2012-04-24 00:10:27 UTC (rev 115019)
+++ trunk/php/luasandbox/luasandbox.c 2012-04-24 10:39:43 UTC (rev 115020)
@@ -39,7 +39,7 @@
static void luasandbox_call_helper(lua_State * L, zval * sandbox_zval,
php_luasandbox_obj * sandbox,
zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC);
-static void luasandbox_handle_error(lua_State * L, int status);
+static void luasandbox_handle_error(php_luasandbox_obj * sandbox, int status);
static void luasandbox_handle_emergency_timeout(php_luasandbox_obj * sandbox);
static int luasandbox_call_php(lua_State * L);
static int luasandbox_dump_writer(lua_State * L, const void * p, size_t sz,
void * ud);
@@ -498,7 +498,7 @@
// Parse the string into a function on the stack
status = luaL_loadbuffer(L, code, codeLength, chunkName);
if (status != 0) {
- luasandbox_handle_error(L, status);
+ luasandbox_handle_error(sandbox, status);
return;
}
@@ -565,38 +565,67 @@
* status is returned and an error message pushed to the stack. Throws a
suitable
* exception.
*/
-static void luasandbox_handle_error(lua_State * L, int status)
+static void luasandbox_handle_error(php_luasandbox_obj * sandbox, int status)
{
+ lua_State * L = sandbox->state;
const char * errorMsg = luasandbox_error_to_string(L, -1);
zend_class_entry * ce;
- if (!EG(exception)) {
- if (luasandbox_is_fatal(L, -1) && !strcmp(errorMsg,
luasandbox_timeout_message)) {
- ce = luasandboxtimeouterror_ce;
- }
- switch (status) {
- case LUA_ERRRUN:
- if (luasandbox_is_fatal(L, -1)) {
- if (!strcmp(errorMsg,
luasandbox_timeout_message)) {
- ce = luasandboxtimeouterror_ce;
- } else {
- ce = luasandboxfatalerror_ce;
- }
+ zval *zex, *ztrace;
+
+ if (EG(exception)) {
+ lua_pop(L, 1);
+ return;
+ }
+
+ if (luasandbox_is_fatal(L, -1) && !strcmp(errorMsg,
luasandbox_timeout_message)) {
+ ce = luasandboxtimeouterror_ce;
+ }
+ switch (status) {
+ case LUA_ERRRUN:
+ if (luasandbox_is_fatal(L, -1)) {
+ if (!strcmp(errorMsg,
luasandbox_timeout_message)) {
+ ce = luasandboxtimeouterror_ce;
} else {
- ce = luasandboxruntimeerror_ce;
+ ce = luasandboxfatalerror_ce;
}
- break;
- case LUA_ERRSYNTAX:
- ce = luasandboxsyntaxerror_ce;
- break;
- case LUA_ERRMEM:
- ce = luasandboxmemoryerror_ce;
- break;
- case LUA_ERRERR:
- ce = luasandboxerrorerror_ce;
- break;
- }
- zend_throw_exception(ce, (char*)errorMsg, status);
+ } else {
+ ce = luasandboxruntimeerror_ce;
+ }
+ break;
+ case LUA_ERRSYNTAX:
+ ce = luasandboxsyntaxerror_ce;
+ break;
+ case LUA_ERRMEM:
+ ce = luasandboxmemoryerror_ce;
+ break;
+ case LUA_ERRERR:
+ ce = luasandboxerrorerror_ce;
+ break;
}
+
+ MAKE_STD_ZVAL(zex);
+ object_init_ex(zex, ce);
+
+ if (luasandbox_is_trace_error(L, -1)) {
+ // Push the trace on to the top of the stack
+ lua_rawgeti(L, -1, 3);
+ // Convert it to a zval
+ MAKE_STD_ZVAL(ztrace);
+ luasandbox_lua_to_zval(ztrace, L, -1, sandbox->current_zval,
NULL);
+ // Put it in the exception object
+ zend_update_property(ce, zex, "luaTrace", sizeof("luaTrace")-1,
ztrace TSRMLS_CC);
+ zval_ptr_dtor(&ztrace);
+ lua_pop(L, 1);
+ }
+
+ // Initialise standard properties
+ // We would get Zend to do this, but the code for it is wrapped inside
some
+ // very inconvenient interfaces (so inconvenient that Zend itself
+ // duplicates this code in several places).
+ zend_update_property_string(ce, zex, "message", sizeof("message")-1,
errorMsg TSRMLS_CC);
+ zend_update_property_long(ce, zex, "code", sizeof("code")-1, status
TSRMLS_CC);
+
+ zend_throw_exception_object(zex TSRMLS_CC);
lua_pop(L, 1);
}
/* }}} */
@@ -883,6 +912,8 @@
{
// Save the top position
int origTop = lua_gettop(L);
+ // Keep track of the stack index where the return values will appear
+ int retIndex = origTop + 2;
int status;
int cpu_limited = 0;
luasandbox_timer_set t;
@@ -897,6 +928,12 @@
RETURN_FALSE;
}
+ // Push the error function
+ lua_pushcfunction(L, luasandbox_attach_trace);
+
+ // Push the function to be called
+ lua_pushvalue(L, origTop);
+
// Push the arguments
for (i = 0; i < numArgs; i++) {
if (!luasandbox_push_zval(L, *(args[i]))) {
@@ -924,7 +961,7 @@
// Call the function
sandbox->in_lua++;
- status = lua_pcall(L, numArgs, LUA_MULTRET, 0);
+ status = lua_pcall(L, numArgs, LUA_MULTRET, origTop + 1);
sandbox->in_lua--;
sandbox->current_zval = old_zval;
@@ -939,20 +976,20 @@
// Handle normal errors
if (status) {
- luasandbox_handle_error(L, status);
+ luasandbox_handle_error(sandbox, status);
lua_settop(L, origTop - 1);
RETURN_FALSE;
}
// Calculate the number of results and create an array of that capacity
- numResults = lua_gettop(L) - origTop + 1;
+ numResults = lua_gettop(L) - retIndex + 1;
array_init_size(return_value, numResults);
// Fill the array with the results
for (i = 0; i < numResults; i++) {
zval * element;
MAKE_STD_ZVAL(element);
- luasandbox_lua_to_zval(element, L, origTop + i, sandbox_zval,
NULL TSRMLS_CC);
+ luasandbox_lua_to_zval(element, L, retIndex + i, sandbox_zval,
NULL TSRMLS_CC);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value),
(void*)&element,
sizeof(zval*), NULL);
Modified: trunk/php/luasandbox/php_luasandbox.h
===================================================================
--- trunk/php/luasandbox/php_luasandbox.h 2012-04-24 00:10:27 UTC (rev
115019)
+++ trunk/php/luasandbox/php_luasandbox.h 2012-04-24 10:39:43 UTC (rev
115020)
@@ -140,7 +140,10 @@
zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
void luasandbox_wrap_fatal(lua_State * L);
int luasandbox_is_fatal(lua_State * L, int index);
+int luasandbox_is_trace_error(lua_State * L, int index);
const char * luasandbox_error_to_string(lua_State * L, int index);
+int luasandbox_attach_trace(lua_State * L);
+void luasandbox_push_structured_trace(lua_State * L, int level);
#endif /* PHP_LUASANDBOX_H */
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs