From: Dmitry Volyntsev Date: Fri, 4 Sep 2026 01:48:39 +0000 (-0700) Subject: Fix reentrant object-to-primitive conversion X-Git-Tag: 1.0.2~73 X-Git-Url: https://git.kaiwu.me/%7B@url%7D?a=commitdiff_plain;h=9c20d77307a4bec4bd59b3535de28a328a18a402;p=njs.git Fix reentrant object-to-primitive conversion Previously, njs_value_to_primitive() retained a pointer to the caller's value slot while valueOf(), toString(), or property getters executed user code. Reentrant code could overwrite that slot, after which the conversion treated the replacement primitive as the original object. The fix is to snapshot the input value before the first observable property lookup and use the stable copy throughout conversion. The issue was introduced in 64dbf104 (0.3.4). This closes #1094 issue on GitHub. --- diff --git a/src/njs_value.c b/src/njs_value.c index 7d77e341..ae2f206a 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -37,7 +37,7 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, { njs_int_t ret; njs_uint_t tries, force_ordinary; - njs_value_t method, retval, arguments[2]; + njs_value_t input, method, retval, arguments[2]; njs_flathsh_query_t fhq; static const uint32_t atoms[] = { @@ -56,6 +56,9 @@ njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, return NJS_OK; } + input = *value; + value = &input; + tries = 0; fhq.proto = &njs_object_hash_proto; diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index d1901825..d7137c56 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -14367,6 +14367,15 @@ static njs_unit_test_t njs_test[] = { njs_str("false.__proto__ === Boolean.prototype"), njs_str("true") }, + { njs_str("var o = {valueOf() { o = 0; return {}; }," + " toString() { return '7'; }}; +o"), + njs_str("7") }, + + { njs_str("var original = {toString() { return '7'; }}, o = original;" + "Object.defineProperty(original, 'valueOf', {get() {" + " o = 0; return () => ({}); }}); +o"), + njs_str("7") }, + { njs_str("var b = Boolean(1); b.__proto__ === Boolean.prototype"), njs_str("true") },