CVE-2020-13649: Improper Check for Unusual or Exceptional Conditions (CWE-754)
value: parser/js/js-scanner.c in JerryScript 2.2.0 mishandles errors during certain out-of-memory conditions, as demonstrated by a scanner_reverse_info_list NULL pointer dereference and a scanner_scan_all assertion failure.
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: 2020-05-28T15:15Z
Repository: https://github.com/jerryscript-project/jerryscript
Language: C
Files changed: jerry-core/parser/js/js-scanner.c
Commit date: 2020-05-27 14:40:43 +0200
Commit message: Fix error handling in scanner when in case of OOM (#3793)

This patch fixes #3786 and fixes #3788.

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
Diff: diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c
index 45f643423..4dcc677da 100644
--- a/jerry-core/parser/js/js-scanner.c
+++ b/jerry-core/parser/js/js-scanner.c
@@ -3157,61 +3157,65 @@ scan_completed:
     scanner_context.context_status_flags |= PARSER_SCANNING_SUCCESSFUL;
 #endif /* !JERRY_NDEBUG */
   }
   PARSER_CATCH
   {
-    /* Ignore the errors thrown by the lexer. */
-    if (context_p->error != PARSER_ERR_OUT_OF_MEMORY)
-    {
-      context_p->error = PARSER_ERR_NO_ERROR;
-    }
-
 #if ENABLED (JERRY_ES2015)
     while (scanner_context.active_binding_list_p != NULL)
     {
       scanner_pop_binding_list (&scanner_context);
     }
 #endif /* ENABLED (JERRY_ES2015) */
 
-    /* The following code may allocate memory, so it is enclosed in a try/catch. */
-    PARSER_TRY (context_p->try_buffer)
+    if (JERRY_UNLIKELY (context_p->error != PARSER_ERR_OUT_OF_MEMORY))
     {
-#if ENABLED (JERRY_ES2015)
-      if (scanner_context.status_flags & SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION)
+      /* Ignore the errors thrown by the lexer. */
+      context_p->error = PARSER_ERR_NO_ERROR;
+
+      /* The following code may allocate memory, so it is enclosed in a try/catch. */
+      PARSER_TRY (context_p->try_buffer)
       {
-        JERRY_ASSERT (scanner_context.async_source_p != NULL);
+  #if ENABLED (JERRY_ES2015)
+        if (scanner_context.status_flags & SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION)
+        {
+          JERRY_ASSERT (scanner_context.async_source_p != NULL);
 
-        scanner_info_t *info_p;
-        info_p = scanner_insert_info (context_p, scanner_context.async_source_p, sizeof (scanner_info_t));
-        info_p->type = SCANNER_TYPE_ERR_ASYNC_FUNCTION;
-      }
-#endif /* ENABLED (JERRY_ES2015) */
+          scanner_info_t *info_p;
+          info_p = scanner_insert_info (context_p, scanner_context.async_source_p, sizeof (scanner_info_t));
+          info_p->type = SCANNER_TYPE_ERR_ASYNC_FUNCTION;
+        }
+  #endif /* ENABLED (JERRY_ES2015) */
 
-      while (scanner_context.active_literal_pool_p != NULL)
+        while (scanner_context.active_literal_pool_p != NULL)
+        {
+          scanner_pop_literal_pool (context_p, &scanner_context);
+        }
+      }
+      PARSER_CATCH
       {
-        scanner_pop_literal_pool (context_p, &scanner_context);
+        JERRY_ASSERT (context_p->error == PARSER_ERR_OUT_OF_MEMORY);
       }
+      PARSER_TRY_END
     }
-    PARSER_CATCH
-    {
-      JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR);
 
+    JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR || context_p->error == PARSER_ERR_OUT_OF_MEMORY);
+
+    if (context_p->error == PARSER_ERR_OUT_OF_MEMORY)
+    {
       while (scanner_context.active_literal_pool_p != NULL)
       {
         scanner_literal_pool_t *literal_pool_p = scanner_context.active_literal_pool_p;
 
         scanner_context.active_literal_pool_p = literal_pool_p->prev_p;
 
         parser_list_free (&literal_pool_p->literal_pool);
         scanner_free (literal_pool_p, sizeof (scanner_literal_pool_t));
       }
-    }
-    PARSER_TRY_END
 
-#if ENABLED (JERRY_ES2015)
-    context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION;
-#endif /* ENABLED (JERRY_ES2015) */
+      parser_stack_free (context_p);
+      return;
+    }
   }
   PARSER_TRY_END
 
   context_p->status_flags = scanner_context.context_status_flags;
   scanner_reverse_info_list (context_p);

Vulnerable code:     /* Ignore the errors thrown by the lexer. */
    if (context_p->error != PARSER_ERR_OUT_OF_MEMORY)
    {
      context_p->error = PARSER_ERR_NO_ERROR;
    }

    /* The following code may allocate memory, so it is enclosed in a try/catch. */
    PARSER_TRY (context_p->try_buffer)
#if ENABLED (JERRY_ES2015)
      if (scanner_context.status_flags & SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION)
        JERRY_ASSERT (scanner_context.async_source_p != NULL);
        scanner_info_t *info_p;
        info_p = scanner_insert_info (context_p, scanner_context.async_source_p, sizeof (scanner_info_t));
        info_p->type = SCANNER_TYPE_ERR_ASYNC_FUNCTION;
      }
#endif /* ENABLED (JERRY_ES2015) */
      while (scanner_context.active_literal_pool_p != NULL)
        scanner_pop_literal_pool (context_p, &scanner_context);
    PARSER_CATCH
    {
      JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR);
    }
    PARSER_TRY_END
#if ENABLED (JERRY_ES2015)
    context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION;
#endif /* ENABLED (JERRY_ES2015) */
Fixed code:     if (JERRY_UNLIKELY (context_p->error != PARSER_ERR_OUT_OF_MEMORY))
      /* Ignore the errors thrown by the lexer. */
      context_p->error = PARSER_ERR_NO_ERROR;

      /* The following code may allocate memory, so it is enclosed in a try/catch. */
      PARSER_TRY (context_p->try_buffer)
  #if ENABLED (JERRY_ES2015)
        if (scanner_context.status_flags & SCANNER_CONTEXT_THROW_ERR_ASYNC_FUNCTION)
        {
          JERRY_ASSERT (scanner_context.async_source_p != NULL);
          scanner_info_t *info_p;
          info_p = scanner_insert_info (context_p, scanner_context.async_source_p, sizeof (scanner_info_t));
          info_p->type = SCANNER_TYPE_ERR_ASYNC_FUNCTION;
        }
  #endif /* ENABLED (JERRY_ES2015) */
        while (scanner_context.active_literal_pool_p != NULL)
        {
          scanner_pop_literal_pool (context_p, &scanner_context);
        }
      }
      PARSER_CATCH
        JERRY_ASSERT (context_p->error == PARSER_ERR_OUT_OF_MEMORY);
      PARSER_TRY_END
    JERRY_ASSERT (context_p->error == PARSER_ERR_NO_ERROR || context_p->error == PARSER_ERR_OUT_OF_MEMORY);

    if (context_p->error == PARSER_ERR_OUT_OF_MEMORY)
    {
      parser_stack_free (context_p);
      return;
    }