Advertisement

Crash in try/catch blocks

Started by September 05, 2023 12:24 PM
10 comments, last by Miss 1 year, 1 month ago

I am getting a crash when destroying a script context:

This is using the latest WIP version from SVN. This only seems to happen when I am using try/catch and am catching exceptions within the catch handler. If I have a try/catch without any exceptions, this crash doesn't occur.

It only happens after the exception has been caught a bunch of times, though I'm not sure how many times. In my case, it's running in a 144 FPS loop every frame, and can crash after releasing the context after a couple seconds.

Feels like it might be writing past some buffer, corrupting memory?

Here's the original report with some example code: https://github.com/openplanet-nl/issues/issues/359​​ - you can see the user “fixed” it by simply making sure the exceptions are never thrown. Also note that in the link above, the first example code is missing a “yield();” call in the "while(true)" loop, which suspends the script context until the next frame.

Based on the callstack you posted it looks like the crash happens when attempting to free the space for stack. It appears to have gotten corrupted during the exception handling.

Possibly the root cause lies in the combination of the exception happening in a function call lying on the subsequent dynamic stack block. This is something that would happen too often, which may explain why it is intermittent.

I'll investigate this.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

I've not been able to reproduce this.

Would you be able to provide some more information? Could you for example write the engine configuration to a file, so I can take a look at how the interface is registered? You can use WriteConfigToFile from the helper functions add-on to do that.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

It's a little big, but definitely: https://drive.proton.me/urls/P1Y3WWWH6R#kNYFi4tBWPXe​

Thanks. I'll continue the investigation. Hopefully I'll be able to reproduce the problem.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Still no luck in reproducing the problem.

I've tried to replicate the script and the registered interface with your configuration. This is what I have:

	// Test crash due to exception
	// https://www.gamedev.net/forums/topic/714882-crash-in-trycatch-blocks/5458952/
	// https://github.com/openplanet-nl/issues/issues/359
	{
		asIScriptEngine* engine = asCreateScriptEngine();
		engine->SetMessageCallback(asMETHOD(CBufferedOutStream, Callback), &bout, asCALL_THISCALL);
		bout.buffer = "";

		engine->SetEngineProperty(asEP_ALLOW_IMPLICIT_HANDLE_TYPES, true);
		engine->SetEngineProperty(asEP_PROPERTY_ACCESSOR_MODE, 2);

		RegisterScriptString(engine);
		engine->RegisterGlobalFunction("void trace(const string &in)", asFUNCTION(print), asCALL_GENERIC);

		engine->SetDefaultNamespace("Time");
		engine->RegisterGlobalFunction("int64 get_Stamp()", asFUNCTION(get_Stamp), asCALL_CDECL);
		engine->SetDefaultNamespace("");

		engine->RegisterObjectType("MwNodPool<T>", 1, asOBJ_VALUE | asOBJ_TEMPLATE); // 66
		engine->RegisterObjectBehaviour("MwNodPool<T>", asBEHAVE_CONSTRUCT, "void MwNodPool(int&in)", asFUNCTION(Construct), asCALL_CDECL_OBJLAST);
		engine->RegisterObjectBehaviour("MwNodPool<T>", asBEHAVE_DESTRUCT, "void MwNodPool()", asFUNCTION(Destruct), asCALL_CDECL_OBJLAST);
		engine->RegisterObjectMethod("MwNodPool<T>", "uint get_Length() const", asFUNCTION(get_Length), asCALL_CDECL_OBJLAST);
		engine->RegisterObjectType("CGameCtnEditorScriptAnchoredObject", 0, asOBJ_REF | asOBJ_NOCOUNT); // 262145 = 1 + 1<<18 = asOBJ_REF + asOBJ_NOCOUNT


		engine->RegisterObjectType("CSmEditorPluginMapType", 0, asOBJ_REF | asOBJ_NOCOUNT); // 262145 = 1 + 1<<18 = asOBJ_REF + asOBJ_NOCOUNT
		engine->RegisterObjectMethod("CSmEditorPluginMapType", "const MwNodPool<CGameCtnEditorScriptAnchoredObject@>&get_Items()", asFUNCTION(get_Items), asCALL_CDECL_OBJLAST);

		engine->RegisterObjectType("CGameEditorPluginMapMapType", 0, asOBJ_REF | asOBJ_NOCOUNT); // 262145 = 1 + 1<<18 = asOBJ_REF + asOBJ_NOCOUNT
		engine->RegisterObjectMethod("CGameEditorPluginMapMapType", "void opCast(?&out)", asFUNCTION(opCast), asCALL_CDECL_OBJLAST);

		engine->RegisterObjectType("CGameCtnEditorFree", 0, asOBJ_REF | asOBJ_NOCOUNT); // 262145
		engine->RegisterObjectMethod("CGameCtnEditorFree", "CGameEditorPluginMapMapType@ get_PluginMapType()", asFUNCTIONPR(get_PluginMapType, (Dummy*), Dummy*), asCALL_CDECL_OBJLAST);

		engine->RegisterObjectType("CGameCtnApp", 0, asOBJ_REF | asOBJ_NOCOUNT); // 262145
		engine->RegisterObjectMethod("CGameCtnApp", "CGameCtnEditorFree @get_Editor()", asFUNCTION(get_Editor), asCALL_CDECL_OBJLAST);

		engine->RegisterGlobalFunction("CGameCtnApp @GetApp()", asFUNCTION(GetApp), asCALL_CDECL);

		app = new Dummy();
		app->next = new Dummy();
		app->next->next = new Dummy();


		asIScriptModule* mod = engine->GetModule("test", asGM_ALWAYS_CREATE);
		mod->AddScriptSection("test",
			"uint lastLog = Time::Stamp;\n"
			"int count = 5;"
			"void MLCoro() {\n"
			"	bool logNow = false; \n"
			"	while (--count > 0) {\n"
			"		logNow = false; \n"
			"		if (lastLog < Time::Stamp) {\n"
			"			lastLog = Time::Stamp; \n"
			"			trace('ML sec'); \n"
			"			logNow = true; \n"
			"		}\n"
			"		try {\n"
			"			auto editor = cast<CGameCtnEditorFree>(GetApp().Editor); \n"
			"			auto pmt = cast<CSmEditorPluginMapType>(editor.PluginMapType); \n"
			"			if (logNow) {\n"
			"				trace('Items length: ' + pmt.Items.Length); \n"
			"			}\n"
			"		}\n"
			"		catch {\n"
			"		}\n"
			"	}\n"
			"}\n");
		r = mod->Build();
		if (r < 0)
			TEST_FAILED;

		asIScriptContext* ctx = engine->CreateContext();
		ctx->Prepare(mod->GetFunctionByDecl("void MLCoro()"));
		r = ctx->Execute();
		if (r != asEXECUTION_FINISHED)
			TEST_FAILED;

		if (bout.buffer != "test (2, 15) : Info    : Compiling void MLCoro()\n"
						   "test (6, 15) : Warning : Signed/Unsigned mismatch\n")
		{
			PRINTF("%s", bout.buffer.c_str());
			TEST_FAILED;
		}

		app->Release();
		app = 0;

		ctx->Release();

		engine->ShutDownAndRelease();
	}

I've tried causing the exception at different levels as there are multiple places that can have null pointer exception.

The memory invasion corrupting the stack blocks might come from somewhere else. It may not even be caused by AngelScript.

Can you reproduce it in your engine and set debug memory breakpoints where the stack blocks get corrupted? That may be the only way we'll ever find out what is actually causing this.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

I'm still investigating. It's worth noting that the following code also causes a crash:

class Foo { void Bar() {} }
void Main() {
	Foo@ f;
	for (int i = 0; i < 1000; i++) {
		try {
			f.Bar();
		} catch {}
	}
}

I have identified a potential buffer underrun here: (this is writing 4 bytes before the stack memory, note that the write is likely happening on line 2828, not 2829 – I guess this is an unchecked stack overflow in Angelscript's stack?)

I can't trigger the data breakpoint when I change the number of loops down to 10.

Great. With this code it eliminates the dependency on the application interface. Surely I will be able to reproduce it now.

It is curious that the problem only happens after very many iterations in the loop.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Miss said:

I can't trigger the data breakpoint when I change the number of loops down to 10.

That was the key. I hadn't tested with enough iterations before.

The root cause was that the exception handler didn't restore the stack pointer in this case, thus the stack would grow with each iteration and eventually overflow the stack block.

I've fixed this in revision 2865.

Thanks,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement