CVE-2022-44789: Out-of-bounds Write (CWE-787)
value: A logical issue in O_getOwnPropertyDescriptor() in Artifex MuJS 1.0.0 through 1.3.x before 1.3.2 allows an attacker to achieve Remote Code Execution through memory corruption, via the loading of a crafted JavaScript file.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.8
Published: 2022-11-23T21:15Z
Repository: https://github.com/ccxvii/mujs
Language: C
Files changed: jsobject.c
Commit date: 2022-11-07 13:44:24 +0100
Commit message: Bug 706057: Fix use-after-free in getOwnPropertyDescriptor.

getOwnPropertyDescriptor should create the descriptor object by
using [[DefineOwnProperty]], and not by looking through the prototype
chain where it may invoke getters and setters on the Object.prototype.

If there exists an Object.prototype.get property with a setter, that method is
invoked when it shouldn't. A malicious getter here can delete the property
currently being processed in getOwnPropertyDescriptor, and we'll end up
with a use-after-free bug.

Avoid this problem by following the spec and use js_defproperty rather than
js_setproperty to define own properties in getOwnPropertyDescriptor and
related functions.

Diff: diff --git a/jsobject.c b/jsobject.c
index 78ea3441..a58fc3a5 100644
--- a/jsobject.c
+++ b/jsobject.c
@@ -132,29 +132,29 @@ static void O_getOwnPropertyDescriptor(js_State *J)
 		js_pushundefined(J);
 	} else {
 		js_newobject(J);
 		if (!ref->getter && !ref->setter) {
 			js_pushvalue(J, ref->value);
-			js_setproperty(J, -2, "value");
+			js_defproperty(J, -2, "value", 0);
 			js_pushboolean(J, !(ref->atts & JS_READONLY));
-			js_setproperty(J, -2, "writable");
+			js_defproperty(J, -2, "writable", 0);
 		} else {
 			if (ref->getter)
 				js_pushobject(J, ref->getter);
 			else
 				js_pushundefined(J);
-			js_setproperty(J, -2, "get");
+			js_defproperty(J, -2, "get", 0);
 			if (ref->setter)
 				js_pushobject(J, ref->setter);
 			else
 				js_pushundefined(J);
-			js_setproperty(J, -2, "set");
+			js_defproperty(J, -2, "set", 0);
 		}
 		js_pushboolean(J, !(ref->atts & JS_DONTENUM));
-		js_setproperty(J, -2, "enumerable");
+		js_defproperty(J, -2, "enumerable", 0);
 		js_pushboolean(J, !(ref->atts & JS_DONTCONF));
-		js_setproperty(J, -2, "configurable");
+		js_defproperty(J, -2, "configurable", 0);
 	}
 }
 
 static int O_getOwnPropertyNames_walk(js_State *J, js_Property *ref, int i)
 {
@@ -246,11 +246,11 @@ static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name,
 		configurable = js_toboolean(J, -1);
 		js_pop(J, 1);
 	}
 	if (js_hasproperty(J, -1, "value")) {
 		hasvalue = 1;
-		js_setproperty(J, -3, name);
+		js_defproperty(J, -3, name, 0);
 	}
 
 	if (!writable) atts |= JS_READONLY;
 	if (!enumerable) atts |= JS_DONTENUM;
 	if (!configurable) atts |= JS_DONTCONF;

Vulnerable code: 			js_setproperty(J, -2, "value");
			js_setproperty(J, -2, "writable");
			js_setproperty(J, -2, "get");
			js_setproperty(J, -2, "set");
		js_setproperty(J, -2, "enumerable");
		js_setproperty(J, -2, "configurable");
		js_setproperty(J, -3, name);
Fixed code: 			js_defproperty(J, -2, "value", 0);
			js_defproperty(J, -2, "writable", 0);
			js_defproperty(J, -2, "get", 0);
			js_defproperty(J, -2, "set", 0);
		js_defproperty(J, -2, "enumerable", 0);
		js_defproperty(J, -2, "configurable", 0);
		js_defproperty(J, -3, name, 0);