From 02a7a84b48438a8dcffb7015da69d093d1ec47ea Mon Sep 17 00:00:00 2001
From: Stephen Hurd <shurd@sasktel.net>
Date: Wed, 18 Nov 2020 02:42:31 -0500
Subject: [PATCH] Eliminate crash in js.on_exit()

Have js.on_exit() throw an exciption with a useful(?) error when the
scope already has private data, and that data is not an on_exit list.

This fixes #182.
---
 src/sbbs3/js_internal.c | 48 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 40 insertions(+), 8 deletions(-)

diff --git a/src/sbbs3/js_internal.c b/src/sbbs3/js_internal.c
index f56a94feb4..2f3b3b9fbb 100644
--- a/src/sbbs3/js_internal.c
+++ b/src/sbbs3/js_internal.c
@@ -59,6 +59,11 @@ enum {
 	,PROP_GLOBAL
 };
 
+struct onexit_private {
+	char signature[7];
+	str_list_t list;
+};
+
 static JSBool js_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
 	jsval idval;
@@ -601,8 +606,9 @@ js_on_exit(JSContext *cx, uintN argc, jsval *arglist)
 	JSObject *glob=JS_GetGlobalObject(cx);
 	jsval *argv=JS_ARGV(cx, arglist);
 	global_private_t*	pd;
-	str_list_t	list;
+	struct onexit_private *oep = NULL;
 	str_list_t	oldlist;
+	str_list_t	list;
 	char		*p = NULL;
 
 	JS_SET_RVAL(cx, arglist, JSVAL_VOID);
@@ -615,11 +621,30 @@ js_on_exit(JSContext *cx, uintN argc, jsval *arglist)
 		list=pd->exit_func;
 	}
 	else {
-		list=(str_list_t)JS_GetPrivate(cx,scope);
-		if(list==NULL) {
-			list=strListInit();
-			JS_SetPrivate(cx,scope,list);
+		oep=(struct onexit_private *)JS_GetPrivate(cx,scope);
+		if(oep==NULL) {
+			oep = malloc(sizeof(*oep));
+			if (oep == NULL) {
+				JS_ReportError(cx, "Unable to allocate memory for on_exit() list");
+				return JS_FALSE;
+			}
+			memcpy(oep->signature, "onexit", 7);
+			oep->list=strListInit();
+			JS_SetPrivate(cx,scope,oep);
+		}
+		else {
+			if (oep->signature[0] != 'o'
+			    || oep->signature[0] != 'n'
+			    || oep->signature[0] != 'e'
+			    || oep->signature[0] != 'x'
+			    || oep->signature[0] != 'i'
+			    || oep->signature[0] != 't'
+			    || oep->signature[0] != 0) {
+				JS_ReportError(cx, "on_exit not available in %s scopes", JS_GetClass(cx, scope)->name);
+				return JS_FALSE;
+			 }
 		}
+		list = oep->list;
 	}
 
 	JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
@@ -633,7 +658,7 @@ js_on_exit(JSContext *cx, uintN argc, jsval *arglist)
 		if(glob==scope)
 			pd->exit_func=list;
 		else
-			JS_SetPrivate(cx,scope,list);
+			oep->list = list;
 	}
 	return(JS_TRUE);
 }
@@ -780,8 +805,15 @@ void DLLCALL js_EvalOnExit(JSContext *cx, JSObject *obj, js_callback_t* cb)
 		pt=(global_private_t *)JS_GetPrivate(cx,JS_GetGlobalObject(cx));		
 		list=pt->exit_func;
 	}
-	else
-		list=JS_GetPrivate(cx,obj);
+	else {
+		struct onexit_private *oep = JS_GetPrivate(cx,obj);
+		if (oep == NULL)
+			list = NULL;
+		else {
+			list = oep->list;
+			free(oep);
+		}
+	}
 
 	cb->auto_terminate=FALSE;
 
-- 
GitLab