CVE-2022-29369: Improper Check for Unusual or Exceptional Conditions (CWE-754)
value: Nginx NJS v0.7.2 was discovered to contain a segmentation violation via njs_lvlhsh_bucket_find at njs_lvlhsh.c.
CWE description: The product does not check or incorrectly checks for unusual or exceptional conditions that are not expected to occur frequently during day to day operation of the product.
CVSS v3 base score: 7.5
Published: 2022-05-12T19:15Z
Repository: https://github.com/nginx/njs
Language: C
Files changed: src/njs_vmcode.c, src/test/njs_unit_test.c
Commit date: 2022-03-28 16:22:17 +0000
Commit message: Fixed njs_vmcode_interpreter() when "toString" conversion fails.

Previously, while interpreting a user function, njs_vmcode_interpreter()
might return prematurely when an error happens.  This is not correct
because the current frame has to be unwound (or exception caught)
first.

The fix is exit through only 5 appropriate exit points to ensure
proper unwinding.

This closes #467 issue on Github.

Diff: diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c
index 0b07ec67e..6b10200ee 100644
--- a/src/njs_vmcode.c
+++ b/src/njs_vmcode.c
@@ -698,11 +698,11 @@ next:
                 }
 
                 ret = njs_object_prop_define(vm, value1, &name, function,
                                              accessor->type);
                 if (njs_slow_path(ret != NJS_OK)) {
-                    return NJS_ERROR;
+                    goto error;
                 }
 
                 ret = sizeof(njs_vmcode_prop_accessor_t);
                 break;
 
@@ -777,16 +777,16 @@ next:
                 if (njs_slow_path(ret == NJS_ERROR)) {
                     goto error;
                 }
 
                 if (njs_slow_path(!njs_is_function(&dst))) {
-                    ret = njs_value_to_key(vm, value2, value2);
+                    ret = njs_value_to_key(vm, &dst, value2);
                     if (njs_slow_path(ret != NJS_OK)) {
-                        return NJS_ERROR;
+                        goto error;
                     }
 
-                    njs_key_string_get(vm, value2, &string);
+                    njs_key_string_get(vm, &dst, &string);
                     njs_type_error(vm,
                                "(intermediate value)[\"%V\"] is not a function",
                                &string);
                     goto error;
                 }
@@ -948,11 +948,12 @@ next:
                 value1 = njs_scope_value(vm, var->dst);
 
                 if (njs_is_valid(value1)) {
                     value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
                     if (njs_slow_path(value1 == NULL)) {
-                        return NJS_ERROR;
+                        njs_memory_error(vm);
+                        goto error;
                     }
 
                     njs_scope_value_set(vm, var->dst, value1);
                 }
 
@@ -965,11 +966,12 @@ next:
                 var = (njs_vmcode_variable_t *) pc;
                 value2 = njs_scope_value(vm, var->dst);
 
                 value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
                 if (njs_slow_path(value1 == NULL)) {
-                    return NJS_ERROR;
+                    njs_memory_error(vm);
+                    goto error;
                 }
 
                 *value1 = *value2;
 
                 njs_scope_value_set(vm, var->dst, value1);
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index ef5b3ca45..186defa1a 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -3407,10 +3407,15 @@ static njs_unit_test_t  njs_test[] =
                  "}; njs.dump([f(0), f(1), f(3)])"),
       njs_str("[[1,1,0],[1,1,0],[1,1,1]]") },
 
     /**/
 
+    { njs_str("function f() { Object.prototype.toString = 1; };"
+              "Object.prototype.toString = f;"
+              "(function () { try { 's'[{}](); } catch (e) { throw e; } })()"),
+      njs_str("TypeError: Cannot convert object to primitive value") },
+
     { njs_str("var i; for (i = 0; i < 10; i++) { i += 1 } i"),
       njs_str("10") },
 
     /* Factorial. */
 

Vulnerable code:                     return NJS_ERROR;
                    ret = njs_value_to_key(vm, value2, value2);
                        return NJS_ERROR;
                    njs_key_string_get(vm, value2, &string);
                        return NJS_ERROR;
                    return NJS_ERROR;
Fixed code:                     goto error;
                    ret = njs_value_to_key(vm, &dst, value2);
                        goto error;
                    njs_key_string_get(vm, &dst, &string);
                        njs_memory_error(vm);
                        goto error;
                    njs_memory_error(vm);
                    goto error;
    { njs_str("function f() { Object.prototype.toString = 1; };"
              "Object.prototype.toString = f;"
              "(function () { try { 's'[{}](); } catch (e) { throw e; } })()"),
      njs_str("TypeError: Cannot convert object to primitive value") },