Skip to content

Commit

Permalink
Fix memory leak when using continue or break statement with syntaxError
Browse files Browse the repository at this point in the history
This patch fixes #5062.

JerryScript-DCO-1.0-Signed-off-by: Gergo Csizi [email protected]
  • Loading branch information
gergocs committed Dec 11, 2024
1 parent b3fa5e0 commit 0bf29cb
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 16 deletions.
22 changes: 19 additions & 3 deletions jerry-core/parser/js/js-parser-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ typedef enum
* Get class option bits from ecma_parse_opts_t
*/
#define PARSER_RESTORE_STATUS_FLAGS(opts) \
(((opts) &PARSER_RESTORE_STATUS_FLAGS_MASK) << PARSER_RESTORE_STATUS_FLAGS_SHIFT)
(((opts) & PARSER_RESTORE_STATUS_FLAGS_MASK) << PARSER_RESTORE_STATUS_FLAGS_SHIFT)

/**
* All flags that affect exotic arguments object creation.
Expand All @@ -215,7 +215,7 @@ typedef enum

/* Checks whether unmapped arguments are needed. */
#define PARSER_NEEDS_MAPPED_ARGUMENTS(status_flags) \
(((status_flags) &PARSER_ARGUMENTS_RELATED_FLAGS) == PARSER_ARGUMENTS_NEEDED)
(((status_flags) & PARSER_ARGUMENTS_RELATED_FLAGS) == PARSER_ARGUMENTS_NEEDED)

/* The maximum of PARSER_CBC_STREAM_PAGE_SIZE is 127. */
#define PARSER_CBC_STREAM_PAGE_SIZE ((uint32_t) (64 - sizeof (void *)))
Expand Down Expand Up @@ -245,7 +245,7 @@ typedef struct
#define PARSER_CBC_UNAVAILABLE CBC_EXT_OPCODE

#define PARSER_TO_EXT_OPCODE(opcode) ((uint16_t) ((opcode) + 256))
#define PARSER_GET_EXT_OPCODE(opcode) ((opcode) -256)
#define PARSER_GET_EXT_OPCODE(opcode) ((opcode) - 256)
#define PARSER_IS_BASIC_OPCODE(opcode) ((opcode) < 256)
#define PARSER_IS_PUSH_LITERAL(opcode) \
((opcode) == CBC_PUSH_LITERAL || (opcode) == CBC_PUSH_TWO_LITERALS || (opcode) == CBC_PUSH_THREE_LITERALS)
Expand Down Expand Up @@ -366,6 +366,16 @@ typedef struct parser_branch_node_t
parser_branch_t branch; /**< branch */
} parser_branch_node_t;

/**
* All data allocated by the parser is
* stored in parser_data_pages in the memory.
*/
typedef struct parser_branch_list_t
{
struct parser_branch_list_t *next_p; /**< next page */
parser_branch_node_t *branch_node_p;
} parser_branch_list_t;

/**
* Items of scope stack.
*/
Expand Down Expand Up @@ -591,6 +601,7 @@ typedef struct
ecma_value_t tagged_template_literal_cp; /**< compessed pointer to the tagged template literal collection */
parser_private_context_t *private_context_p; /**< private context */
uint8_t stack_top_uint8; /**< top byte stored on the stack */
parser_branch_list_t *branch_list; /**< list of branches */

#ifndef JERRY_NDEBUG
/* Variables for debugging / logging. */
Expand Down Expand Up @@ -654,6 +665,11 @@ void *parser_list_get (parser_list_t *list_p, size_t index);
void parser_list_iterator_init (parser_list_t *list_p, parser_list_iterator_t *iterator_p);
void *parser_list_iterator_next (parser_list_iterator_t *iterator_p);

void parser_branch_list_init (parser_context_t *context_p);
void *parser_branch_list_append (parser_context_t *context_p);
void parser_branch_list_remove (const parser_context_t *context_p, const parser_branch_node_t *node_p);
void parser_branch_list_free (const parser_context_t *context_p);

/* Parser stack. Optimized for pushing bytes.
* Pop functions never throws error. */

Expand Down
99 changes: 99 additions & 0 deletions jerry-core/parser/js/js-parser-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,105 @@ parser_list_iterator_next (parser_list_iterator_t *iterator_p) /**< iterator */
return result;
} /* parser_list_iterator_next */

/*
* Init branch linked list.
*/
void
parser_branch_list_init (parser_context_t *context_p)
{
context_p->branch_list = (parser_branch_list_t *) parser_malloc (context_p, sizeof (parser_branch_list_t));
context_p->branch_list->branch_node_p = NULL;
context_p->branch_list->next_p = NULL;
} /* parser_branch_list_init */

/**
* Append a branch node to the branch list.
*
* @return the newly created branch node.
*/
void *
parser_branch_list_append (parser_context_t *context_p)
{
parser_branch_node_t *result = parser_malloc (context_p, sizeof (parser_branch_node_t));

if (result == NULL)
{
return NULL;
}

parser_branch_list_t *last_pos = context_p->branch_list;
while (last_pos->next_p != NULL)
{
last_pos = last_pos->next_p;
}

last_pos->next_p = (parser_branch_list_t *) parser_malloc (context_p, sizeof (parser_branch_list_t));
last_pos->next_p->branch_node_p = result;
last_pos->next_p->next_p = NULL;

return result;
} /* parser_branch_list_append */

/**
* Remove a branch node from the branch list.
*/
void
parser_branch_list_remove (const parser_context_t *context_p, const parser_branch_node_t *node_p)
{
parser_branch_list_t *pos = context_p->branch_list;
parser_branch_list_t *prev_pos = NULL;

while (pos->next_p != NULL && pos->branch_node_p != node_p)
{
prev_pos = pos;
pos = pos->next_p;
}

if (pos->branch_node_p != node_p)
{
return;
}

parser_branch_list_t *next_pos = pos->next_p;

parser_free (pos->branch_node_p, sizeof (parser_branch_node_t));

if (prev_pos == NULL && next_pos == NULL)
{
context_p->branch_list->branch_node_p = NULL;
return;
}

parser_free (pos, sizeof (parser_branch_list_t));

if (prev_pos != NULL)
{
prev_pos->next_p = next_pos;
}
} /* parser_branch_list_remove */

/**
* Free the branch list.
*/
void
parser_branch_list_free (const parser_context_t *context_p)
{
parser_branch_list_t *pos = context_p->branch_list;
parser_branch_list_t *prev_pos = NULL;

while (pos != NULL)
{
prev_pos = pos;
pos = pos->next_p;
if (prev_pos->branch_node_p != NULL)
{
parser_free (prev_pos->branch_node_p, sizeof (parser_branch_node_t));
}

parser_free (prev_pos, sizeof (parser_branch_list_t));
}
} /* parser_branch_list_free */

/**********************************************************************/
/* Parser stack management functions */
/**********************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion jerry-core/parser/js/js-parser-statm.c
Original file line number Diff line number Diff line change
Expand Up @@ -2053,7 +2053,7 @@ parser_parse_case_statement (parser_context_t *context_p) /**< context */
parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t));

parser_set_branch_to_current_position (context_p, &branch_p->branch);
parser_free (branch_p, sizeof (parser_branch_node_t));
parser_branch_list_remove (context_p, branch_p);
} /* parser_parse_case_statement */

/**
Expand Down
4 changes: 2 additions & 2 deletions jerry-core/parser/js/js-parser-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ parser_emit_cbc_forward_branch_item (parser_context_t *context_p, /**< context *
* the branch is constructed locally, and copied later. */
parser_emit_cbc_forward_branch (context_p, opcode, &branch);

new_item = (parser_branch_node_t *) parser_malloc (context_p, sizeof (parser_branch_node_t));
new_item = (parser_branch_node_t *) parser_branch_list_append (context_p);
new_item->branch = branch;
new_item->next_p = next_p;
return new_item;
Expand Down Expand Up @@ -730,7 +730,7 @@ parser_set_breaks_to_current_position (parser_context_t *context_p, /**< context
{
parser_set_branch_to_current_position (context_p, &current_p->branch);
}
parser_free (current_p, sizeof (parser_branch_node_t));
parser_branch_list_remove (context_p, current_p);
current_p = next_p;
}
} /* parser_set_breaks_to_current_position */
Expand Down
8 changes: 3 additions & 5 deletions jerry-core/parser/js/js-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,7 @@ parser_parse_source (void *source_p, /**< source code */
context.scope_stack_global_end = 0;
context.tagged_template_literal_cp = JMEM_CP_NULL;
context.private_context_p = NULL;
parser_branch_list_init (&context);

#ifndef JERRY_NDEBUG
context.context_stack_depth = 0;
Expand Down Expand Up @@ -2293,6 +2294,7 @@ parser_parse_source (void *source_p, /**< source code */
JERRY_ASSERT (!(context.status_flags & PARSER_HAS_LATE_LIT_INIT));

compiled_code_p = parser_post_processing (&context);
parser_branch_list_free (&context);
parser_list_free (&context.literal_pool);

/* When parsing is successful, only the dummy value can be remained on the stack. */
Expand Down Expand Up @@ -2366,11 +2368,6 @@ parser_parse_source (void *source_p, /**< source code */
}
PARSER_CATCH
{
if (context.last_statement.current_p != NULL)
{
parser_free_jumps (context.last_statement);
}

parser_free_allocated_buffer (&context);

scanner_cleanup (&context);
Expand All @@ -2383,6 +2380,7 @@ parser_parse_source (void *source_p, /**< source code */
#endif /* JERRY_MODULE_SYSTEM */

compiled_code_p = NULL;
parser_branch_list_free (&context);
parser_free_literals (&context.literal_pool);
parser_cbc_stream_free (&context.byte_code);

Expand Down
15 changes: 15 additions & 0 deletions tests/jerry/fail/regression-test-issue-5062.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright JS Foundation and other contributors, http://js.foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

( async ( ) => { for await ( const b of n ) { continue ;
11 changes: 6 additions & 5 deletions tools/run-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def skip_if(condition, desc):
OPTIONS_DOCTESTS = ['--doctests=on', '--jerry-cmdline=off', '--error-messages=on',
'--snapshot-save=on', '--snapshot-exec=on', '--vm-exec-stop=on']
OPTIONS_PROMISE_CALLBACK = ['--promise-callback=on']
OPTIONS_HEAP_SIZE = ['--mem-heap=1024']

# Test options for unittests
JERRY_UNITTESTS_OPTIONS = [
Expand All @@ -67,15 +68,15 @@ def skip_if(condition, desc):
# Test options for jerry-tests
JERRY_TESTS_OPTIONS = [
Options('jerry_tests',
OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT + OPTIONS_MEM_STRESS),
OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_HEAP_SIZE + OPTIONS_GC_MARK_LIMIT + OPTIONS_MEM_STRESS),
Options('jerry_tests-snapshot',
OPTIONS_COMMON + OPTIONS_SNAPSHOT + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT,
OPTIONS_COMMON + OPTIONS_SNAPSHOT + OPTIONS_HEAP_SIZE + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT,
['--snapshot']),
Options('jerry_tests-cpointer_32bit',
OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT
+ ['--cpointer-32bit=on', '--mem-heap=1024']),
OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_HEAP_SIZE + OPTIONS_GC_MARK_LIMIT
+ ['--cpointer-32bit=on']),
Options('jerry_tests-external_context',
OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_GC_MARK_LIMIT
OPTIONS_COMMON + OPTIONS_STACK_LIMIT + OPTIONS_HEAP_SIZE + OPTIONS_GC_MARK_LIMIT
+ ['--external-context=on']),
]

Expand Down

0 comments on commit 0bf29cb

Please sign in to comment.