From f33d31693ed2ab7d2a29320181b6aba2c12f76d3 Mon Sep 17 00:00:00 2001
From: Craig Robbins <kde.psych@gmail.com>
Date: Thu, 7 Aug 2014 15:39:12 +1000
Subject: [PATCH] Fix seg fault if popping from empty stack (L-system trees)

See: https://github.com/minetest/minetest/issues/1525

Background
Wuzzy2: If you attempt to spawn a L-system tree with minetest.spawn_tree, you can make Minetest crash if it is attempted to pop an empty stack.

ShadowNinja: This shouldn't cause a segmentation fault, but it should throw a Lua error

Commit Description
This commit throws a Lua error instead of causing a segmentation fault. The server will still "crash" but will include a Lua backtrace.

L-Systems fix randomness
Unless a random seed is provided (via Lua treedef) seed the PRNG with a different seed for each tree
Resolves: https://github.com/minetest/minetest/issues/1469

Fix l-system crash when treedef random_level not set by Lua
---
 doc/lua_api.txt              |  2 +-
 src/script/lua_api/l_env.cpp | 16 +++++++++++++---
 src/treegen.cpp              | 27 +++++++++++++++++++++++----
 src/treegen.h                | 10 ++++++++--
 4 files changed, 45 insertions(+), 10 deletions(-)

diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 74487edee..5412c4ccb 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -2208,7 +2208,7 @@ treedef={
   thin_branches, - boolean true -> use thin (1 node) branches
   fruit,         - string  fruit node name
   fruit_chance,  - num     chance (0-100) to replace leaves with fruit node
-  seed,          - num     random seed
+  seed,          - num     random seed; if no seed is provided, the engine will create one
   }
 
 Key for Special L-System Symbols used in Axioms
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index e6ca846d8..deeb5966f 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -747,7 +747,8 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
 		}
 		getintfield(L, 2, "angle", tree_def.angle);
 		getintfield(L, 2, "iterations", tree_def.iterations);
-		getintfield(L, 2, "random_level", tree_def.iterations_random_level);
+		if (!getintfield(L, 2, "random_level", tree_def.iterations_random_level))
+			tree_def.iterations_random_level = 0;
 		getstringfield(L, 2, "trunk_type", tree_def.trunk_type);
 		getboolfield(L, 2, "thin_branches", tree_def.thin_branches);
 		tree_def.fruit_chance=0;
@@ -757,11 +758,20 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
 			tree_def.fruitnode=ndef->getId(fruit);
 			getintfield(L, 2, "fruit_chance",tree_def.fruit_chance);
 		}
-		getintfield(L, 2, "seed", tree_def.seed);
+		tree_def.explicit_seed = getintfield(L, 2, "seed", tree_def.seed);
 	}
 	else
 		return 0;
-	treegen::spawn_ltree (env, p0, ndef, tree_def);
+
+	treegen::error e;
+	if ((e = treegen::spawn_ltree (env, p0, ndef, tree_def)) != treegen::SUCCESS) {
+		if (e == treegen::UNBALANCED_BRACKETS) {
+			luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket");
+		} else {
+			luaL_error(L, "spawn_tree(): unknown error");
+		}
+	}
+
 	return 1;
 }
 
diff --git a/src/treegen.cpp b/src/treegen.cpp
index 542bdf21c..bd4a2f2c9 100644
--- a/src/treegen.cpp
+++ b/src/treegen.cpp
@@ -118,14 +118,19 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
 }
 
 // L-System tree LUA spawner
-void spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
+treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
 {
 	ServerMap *map = &env->getServerMap();
 	std::map<v3s16, MapBlock*> modified_blocks;
 	ManualMapVoxelManipulator vmanip(map);
 	v3s16 tree_blockp = getNodeBlockPos(p0);
+	treegen::error e;
+
 	vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
-	make_ltree (vmanip, p0, ndef, tree_definition);
+	e = make_ltree (vmanip, p0, ndef, tree_definition);
+	if (e != SUCCESS)
+		return e;
+
 	vmanip.blitBackAll(&modified_blocks);
 
 	// update lighting
@@ -142,15 +147,25 @@ void spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDe
 		event.modified_blocks.insert(i->first);
 	}
 	map->dispatchEvent(&event);
+	return SUCCESS;
 }
 
 //L-System tree generator
-void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
+treegen::error make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
 		TreeDef tree_definition)
 {
 	MapNode dirtnode(ndef->getId("mapgen_dirt"));
+	int seed;
+	if (tree_definition.explicit_seed)
+	{
+		seed = tree_definition.seed+14002;
+	}
+	else
+	{
+		seed = p0.X*2 + p0.Y*4 + p0.Z;      // use the tree position to seed PRNG
+	}
+	PseudoRandom ps(seed);
 
-	PseudoRandom ps(tree_definition.seed+14002);
 	// chance of inserting abcd rules
 	double prop_a = 9;
 	double prop_b = 8;
@@ -354,6 +369,8 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
 			stack_position.push(position);
 			break;
 		case ']':
+			if (stack_orientation.empty())
+				return UNBALANCED_BRACKETS;
 			rotation=stack_orientation.top();
 			stack_orientation.pop();
 			position=stack_position.top();
@@ -393,6 +410,8 @@ void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *nd
 			break;
 		}
 	}
+
+	return SUCCESS;
 }
 
 void tree_node_placement(ManualMapVoxelManipulator &vmanip, v3f p0,
diff --git a/src/treegen.h b/src/treegen.h
index 55da6f9e5..a557ad975 100644
--- a/src/treegen.h
+++ b/src/treegen.h
@@ -30,6 +30,11 @@ class ServerEnvironment;
 
 namespace treegen {
 
+	enum error {
+		SUCCESS,
+		UNBALANCED_BRACKETS
+	};
+
 	struct TreeDef {
 		std::string initial_axiom;
 		std::string rules_a;
@@ -50,6 +55,7 @@ namespace treegen {
 		MapNode fruitnode;
 		int fruit_chance;
 		int seed;
+		bool explicit_seed;
 	};
 
 	// Add default tree
@@ -60,10 +66,10 @@ namespace treegen {
 		INodeDefManager *ndef, int seed);
 
 	// Add L-Systems tree (used by engine)
-	void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
+	treegen::error make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef,
 		TreeDef tree_definition);
 	// Spawn L-systems tree from LUA
-	void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
+	treegen::error spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
 		TreeDef tree_definition);
 
 	// L-System tree gen helper functions
-- 
GitLab