Commit Diff


commit - 442f7ad87d59fba01c2e517a75032dfb69ccbd4b
commit + 069045d59af704cf8f0bf27432436e27b5712ede
blob - 39e35003aeb751ee86d026778836562e89c82661
blob + dc5c92c1a8050487908134d6172d991753dc5081
--- CHANGELOG.md
+++ CHANGELOG.md
@@ -23,3 +23,4 @@ and this project adheres to [Semantic Versioning](http
 ### Fixed
 
 - Fix searching Clang RT.
+- Stack overflow due to recursive traceback calls.
blob - 4f76421a49c64be403566d3f75ae3e1a88932534
blob + ac026f1d081536b6d23d317b6584b7a304a66405
--- luzer/CMakeLists.txt
+++ luzer/CMakeLists.txt
@@ -7,6 +7,7 @@ configure_file(
 )
 
 set(LUZER_SOURCES luzer.c
+                  compat.c
                   fuzzed_data_provider.cc
                   tracer.c
                   counters.c
blob - /dev/null
blob + 2bc398c993cfe44c971913beff31b3c6ba368a9b (mode 644)
--- /dev/null
+++ luzer/compat.c
@@ -0,0 +1,151 @@
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
+
+static int
+countlevels(lua_State *L) {
+	lua_Debug ar;
+	int li = 1, le = 1;
+	/* Find an upper bound. */
+	while (lua_getstack(L, le, &ar))
+		{ li = le; le *= 2; }
+	/* Do a binary search. */
+	while (li < le) {
+		int m = (li + le) / 2;
+		if (lua_getstack(L, m, &ar))
+			li = m + 1;
+		else
+			le = m;
+	}
+	return le - 1;
+}
+
+static int
+findfield(lua_State *L, int objidx, int level) {
+	if (level == 0 || !lua_istable(L, -1))
+		/* Not found. */
+		return 0;
+
+	/* Start 'next' loop. */
+	lua_pushnil(L);
+	/* For each pair in table. */
+	while (lua_next(L, -2)) {
+		/* Ignore non-string keys. */
+		if (lua_type(L, -2) == LUA_TSTRING) {
+			/* Found an object? */
+			if (lua_rawequal(L, objidx, -1)) {
+				/* Remove value (but keep name). */
+				lua_pop(L, 1);
+				return 1;
+			}
+			/* Try recursively. */
+			else if (findfield(L, objidx, level - 1)) {
+				/* Remove table (but keep name). */
+				lua_remove(L, -2);
+				lua_pushliteral(L, ".");
+				/* Place '.' between the two names. */
+				lua_insert(L, -2);
+				lua_concat(L, 3);
+				return 1;
+			}
+		}
+		/* Remove value. */
+		lua_pop(L, 1);
+	}
+	/* Not found. */
+	return 0;
+}
+
+int
+lua_absindex(lua_State *L, int i) {
+	if (i < 0 && i > LUA_REGISTRYINDEX)
+		i += lua_gettop(L) + 1;
+	return i;
+}
+
+void
+lua_copy(lua_State *L, int from, int to) {
+	int abs_to = lua_absindex(L, to);
+	luaL_checkstack(L, 1, "not enough stack slots");
+	lua_pushvalue(L, from);
+	lua_replace(L, abs_to);
+}
+
+static int
+pushglobalfuncname(lua_State *L, lua_Debug *ar) {
+	int top = lua_gettop(L);
+	/* Push function. */
+	lua_getinfo(L, "f", ar);
+	lua_pushvalue(L, LUA_GLOBALSINDEX);
+	if (findfield(L, top + 1, 2)) {
+		/* Move name to proper place. */
+		lua_copy(L, -1, top + 1);
+		/* Remove pushed values. */
+		lua_pop(L, 2);
+		return 1;
+	}
+	else {
+		/* Remove function and global table. */
+		lua_settop(L, top);
+		return 0;
+	}
+}
+
+static void
+pushfuncname(lua_State *L, lua_Debug *ar) {
+	/* Is there a name? */
+	if (*ar->namewhat != '\0')
+		lua_pushfstring(L, "function " LUA_QS, ar->name);
+	else if (*ar->what == 'm')  /* Main? */
+		lua_pushliteral(L, "main chunk");
+	else if (*ar->what == 'C') {
+		if (pushglobalfuncname(L, ar)) {
+			lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
+			/* Remove name. */
+			lua_remove(L, -2);
+		}
+		else
+			lua_pushliteral(L, "?");
+	}
+	else
+		lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+}
+
+/* Size of the first part of the stack. */
+#define LEVELS1 12
+/* Size of the second part of the stack. */
+#define LEVELS2 10
+
+void
+luaL_traceback(lua_State *L, lua_State *L1,
+			   const char *msg, int level) {
+	lua_Debug ar;
+	int top = lua_gettop(L);
+	int numlevels = countlevels(L1);
+	int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0;
+	if (msg) lua_pushfstring(L, "%s\n", msg);
+	lua_pushliteral(L, "stack traceback:");
+	while (lua_getstack(L1, level++, &ar)) {
+		/* Too many levels? */
+		if (level == mark) {
+			/* Add a '...'. */
+			lua_pushliteral(L, "\n\t...");
+			/* And skip to last ones. */
+			level = numlevels - LEVELS2;
+		}
+		else {
+			lua_getinfo(L1, "Slnt", &ar);
+			lua_pushfstring(L, "\n\t%s:", ar.short_src);
+			if (ar.currentline > 0)
+				lua_pushfstring(L, "%d:", ar.currentline);
+				lua_pushliteral(L, " in ");
+				pushfuncname(L, &ar);
+				lua_concat(L, lua_gettop(L) - top);
+		}
+	}
+	lua_concat(L, lua_gettop(L) - top);
+}
+
+#endif
blob - /dev/null
blob + 7859dbb4055b9153a40bf7f544d0a3b8a50a0285 (mode 644)
--- /dev/null
+++ luzer/compat.h
@@ -0,0 +1,7 @@
+#ifndef LUZER_COMPAT_H_
+#define LUZER_COMPAT_H_
+
+void luaL_traceback(lua_State *L, lua_State *L1,
+					const char *msg, int level);
+
+#endif  // LUZER_COMPAT_H_
blob - 4943749b01ea81ef9a32314750447497e724f0a8
blob + e740944321eb16fa400d8e86fb3e6cba3688772d
--- luzer/luzer.c
+++ luzer/luzer.c
@@ -21,6 +21,7 @@
 
 #include "fuzzed_data_provider.h"
 #include "counters.h"
+#include "compat.h"
 #include "macros.h"
 #include "tracer.h"
 #include "version.h"
@@ -48,27 +49,6 @@ get_global_lua_state(void)
 	return LL;
 }
 
-#if LUA_VERSION_NUM < 502
-static int
-luaL_traceback(lua_State *L) {
-	lua_getfield(L, LUA_GLOBALSINDEX, "debug");
-	if (!lua_istable(L, -1)) {
-		lua_pop(L, 1);
-		return 1;
-	}
-	lua_getfield(L, -1, "traceback");
-	if (!lua_isfunction(L, -1)) {
-		lua_pop(L, 2);
-		return 1;
-	}
-	lua_pushvalue(L, 1);
-	lua_pushinteger(L, 2);
-	lua_call(L, 2, 1);
-	fprintf(stderr, "%s\n", lua_tostring(L, -1));
-	return 1;
-}
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -109,20 +89,17 @@ __sanitizer_acquire_crash_state(void)
 }
 
 /**
- * Print the stack trace leading to this call. Useful for debugging user code.
- * See:
- * - https://github.com/keplerproject/lua-compat-5.2/blob/master/c-api/compat-5.2.c#L229
- * - http://www.lua.org/manual/5.2/manual.html#luaL_traceback
+ * Print a Lua stack trace leading to this call.
+ * Useful for debugging user code.
+ * See http://www.lua.org/manual/5.2/manual.html#luaL_traceback
  */
 NO_SANITIZE void
 __sanitizer_print_stack_trace(void)
 {
 	lua_State *L = get_global_lua_state();
-#if LUA_VERSION_NUM < 502
-	luaL_traceback(L);
-#else
-	luaL_traceback(L, L, "traceback", 3);
-#endif
+	lua_State *L1 = luaL_newstate();
+	luaL_traceback(L, L1, "traceback", 3);
+	lua_close(L1);
 }
 #ifdef __cplusplus
 } /* extern "C" */