CVE-2020-13649: NULL Pointer Dereference (CWE-476)
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 dereferences a pointer that it expects to be valid but is NULL.
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;
    }