Skip to content

Commit 58ba4c3

Browse files
committed
py: Check explicitly for memory allocation failure in parser.
Previously, a failed malloc/realloc would throw an exception, which was not caught. I think it's better to keep the parser free from NLR (exception throwing), hence this patch.
1 parent ffa9bdd commit 58ba4c3

6 files changed

Lines changed: 86 additions & 16 deletions

File tree

py/malloc.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,26 @@ void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes) {
118118
return new_ptr;
119119
}
120120

121+
void *m_realloc_maybe(void *ptr, int old_num_bytes, int new_num_bytes) {
122+
void *new_ptr = realloc(ptr, new_num_bytes);
123+
if (new_ptr == NULL) {
124+
return NULL;
125+
}
126+
#if MICROPY_MEM_STATS
127+
// At first thought, "Total bytes allocated" should only grow,
128+
// after all, it's *total*. But consider for example 2K block
129+
// shrunk to 1K and then grown to 2K again. It's still 2K
130+
// allocated total. If we process only positive increments,
131+
// we'll count 3K.
132+
int diff = new_num_bytes - old_num_bytes;
133+
total_bytes_allocated += diff;
134+
current_bytes_allocated += diff;
135+
UPDATE_PEAK();
136+
#endif
137+
DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
138+
return new_ptr;
139+
}
140+
121141
void m_free(void *ptr, int num_bytes) {
122142
if (ptr != NULL) {
123143
free(ptr);

py/misc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ typedef unsigned int uint;
2727
#define m_new0(type, num) ((type*)(m_malloc0(sizeof(type) * (num))))
2828
#define m_new_obj(type) (m_new(type, 1))
2929
#define m_new_obj_var(obj_type, var_type, var_num) ((obj_type*)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num)))
30+
#define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type*)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num)))
3031
#if MICROPY_ENABLE_FINALISER
3132
#define m_new_obj_with_finaliser(type) ((type*)(m_malloc_with_finaliser(sizeof(type))))
3233
#else
3334
#define m_new_obj_with_finaliser(type) m_new_obj(type)
3435
#endif
3536
#define m_renew(type, ptr, old_num, new_num) ((type*)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num))))
37+
#define m_renew_maybe(type, ptr, old_num, new_num) ((type*)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num))))
3638
#define m_del(type, ptr, num) m_free(ptr, sizeof(type) * (num))
3739
#define m_del_obj(type, ptr) (m_del(type, ptr, 1))
3840
#define m_del_var(obj_type, var_type, var_num, ptr) (m_free(ptr, sizeof(obj_type) + sizeof(var_type) * (var_num)))
@@ -42,6 +44,7 @@ void *m_malloc_maybe(int num_bytes);
4244
void *m_malloc_with_finaliser(int num_bytes);
4345
void *m_malloc0(int num_bytes);
4446
void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes);
47+
void *m_realloc_maybe(void *ptr, int old_num_bytes, int new_num_bytes);
4548
void m_free(void *ptr, int num_bytes);
4649
void *m_malloc_fail(int num_bytes);
4750

py/objexcept.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ STATIC mp_obj_t mp_obj_exception_make_new(mp_obj_t type_in, uint n_args, uint n_
5454
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "%s does not take keyword arguments", mp_obj_get_type_str(type_in)));
5555
}
5656

57-
mp_obj_exception_t *o = m_malloc_maybe(sizeof(mp_obj_exception_t) + n_args * sizeof(mp_obj_t));
57+
mp_obj_exception_t *o = m_new_obj_var_maybe(mp_obj_exception_t, mp_obj_t, n_args);
5858
if (o == NULL) {
5959
// Couldn't allocate heap memory; use local data instead.
6060
o = &mp_emergency_exception_obj;
@@ -205,7 +205,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char
205205
assert(exc_type->make_new == mp_obj_exception_make_new);
206206

207207
// make exception object
208-
mp_obj_exception_t *o = m_malloc_maybe(sizeof(mp_obj_exception_t) + 1 * sizeof(mp_obj_t));
208+
mp_obj_exception_t *o = m_new_obj_var_maybe(mp_obj_exception_t, mp_obj_t, 1);
209209
if (o == NULL) {
210210
// Couldn't allocate heap memory; use local data instead.
211211
// Unfortunately, we won't be able to format the string...

py/parse.c

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ typedef struct _rule_stack_t {
8686
} rule_stack_t;
8787

8888
typedef struct _parser_t {
89+
bool had_memory_error;
90+
8991
uint rule_stack_alloc;
9092
uint rule_stack_top;
9193
rule_stack_t *rule_stack;
@@ -97,9 +99,21 @@ typedef struct _parser_t {
9799
mp_lexer_t *lexer;
98100
} parser_t;
99101

102+
STATIC inline void memory_error(parser_t *parser) {
103+
parser->had_memory_error = true;
104+
}
105+
100106
STATIC void push_rule(parser_t *parser, int src_line, const rule_t *rule, int arg_i) {
107+
if (parser->had_memory_error) {
108+
return;
109+
}
101110
if (parser->rule_stack_top >= parser->rule_stack_alloc) {
102-
parser->rule_stack = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc * 2);
111+
rule_stack_t *rs = m_renew_maybe(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc * 2);
112+
if (rs == NULL) {
113+
memory_error(parser);
114+
return;
115+
}
116+
parser->rule_stack = rs;
103117
parser->rule_stack_alloc *= 2;
104118
}
105119
rule_stack_t *rs = &parser->rule_stack[parser->rule_stack_top++];
@@ -116,6 +130,7 @@ STATIC void push_rule_from_arg(parser_t *parser, uint arg) {
116130
}
117131

118132
STATIC void pop_rule(parser_t *parser, const rule_t **rule, uint *arg_i, uint *src_line) {
133+
assert(!parser->had_memory_error);
119134
parser->rule_stack_top -= 1;
120135
*rule = rules[parser->rule_stack[parser->rule_stack_top].rule_id];
121136
*arg_i = parser->rule_stack[parser->rule_stack_top].arg_i;
@@ -129,15 +144,6 @@ mp_parse_node_t mp_parse_node_new_leaf(machine_int_t kind, machine_int_t arg) {
129144
return (mp_parse_node_t)(kind | (arg << 5));
130145
}
131146

132-
//int num_parse_nodes_allocated = 0;
133-
mp_parse_node_struct_t *parse_node_new_struct(int src_line, int rule_id, int num_args) {
134-
mp_parse_node_struct_t *pn = m_new_obj_var(mp_parse_node_struct_t, mp_parse_node_t, num_args);
135-
pn->source_line = src_line;
136-
pn->kind_num_nodes = (rule_id & 0xff) | (num_args << 8);
137-
//num_parse_nodes_allocated += 1;
138-
return pn;
139-
}
140-
141147
uint mp_parse_node_free(mp_parse_node_t pn) {
142148
uint cnt = 0;
143149
if (MP_PARSE_NODE_IS_STRUCT(pn)) {
@@ -211,18 +217,32 @@ STATIC void result_stack_show(parser_t *parser) {
211217
*/
212218

213219
STATIC mp_parse_node_t pop_result(parser_t *parser) {
220+
if (parser->had_memory_error) {
221+
return MP_PARSE_NODE_NULL;
222+
}
214223
assert(parser->result_stack_top > 0);
215224
return parser->result_stack[--parser->result_stack_top];
216225
}
217226

218227
STATIC mp_parse_node_t peek_result(parser_t *parser, int pos) {
228+
if (parser->had_memory_error) {
229+
return MP_PARSE_NODE_NULL;
230+
}
219231
assert(parser->result_stack_top > pos);
220232
return parser->result_stack[parser->result_stack_top - 1 - pos];
221233
}
222234

223235
STATIC void push_result_node(parser_t *parser, mp_parse_node_t pn) {
236+
if (parser->had_memory_error) {
237+
return;
238+
}
224239
if (parser->result_stack_top >= parser->result_stack_alloc) {
225-
parser->result_stack = m_renew(mp_parse_node_t, parser->result_stack, parser->result_stack_alloc, parser->result_stack_alloc * 2);
240+
mp_parse_node_t *pn = m_renew_maybe(mp_parse_node_t, parser->result_stack, parser->result_stack_alloc, parser->result_stack_alloc * 2);
241+
if (pn == NULL) {
242+
memory_error(parser);
243+
return;
244+
}
245+
parser->result_stack = pn;
226246
parser->result_stack_alloc *= 2;
227247
}
228248
parser->result_stack[parser->result_stack_top++] = pn;
@@ -283,7 +303,13 @@ STATIC void push_result_token(parser_t *parser, const mp_lexer_t *lex) {
283303
}
284304

285305
STATIC void push_result_rule(parser_t *parser, int src_line, const rule_t *rule, int num_args) {
286-
mp_parse_node_struct_t *pn = parse_node_new_struct(src_line, rule->rule_id, num_args);
306+
mp_parse_node_struct_t *pn = m_new_obj_var_maybe(mp_parse_node_struct_t, mp_parse_node_t, num_args);
307+
if (pn == NULL) {
308+
memory_error(parser);
309+
return;
310+
}
311+
pn->source_line = src_line;
312+
pn->kind_num_nodes = (rule->rule_id & 0xff) | (num_args << 8);
287313
for (int i = num_args; i > 0; i--) {
288314
pn->nodes[i - 1] = pop_result(parser);
289315
}
@@ -296,6 +322,8 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
296322

297323
parser_t *parser = m_new_obj(parser_t);
298324

325+
parser->had_memory_error = false;
326+
299327
parser->rule_stack_alloc = 64;
300328
parser->rule_stack_top = 0;
301329
parser->rule_stack = m_new(rule_stack_t, parser->rule_stack_alloc);
@@ -327,7 +355,7 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
327355

328356
for (;;) {
329357
next_rule:
330-
if (parser->rule_stack_top == 0) {
358+
if (parser->rule_stack_top == 0 || parser->had_memory_error) {
331359
break;
332360
}
333361

@@ -596,6 +624,16 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
596624
}
597625
}
598626

627+
mp_parse_node_t result;
628+
629+
// check if we had a memory error
630+
if (parser->had_memory_error) {
631+
*parse_error_kind_out = MP_PARSE_ERROR_MEMORY;
632+
result = MP_PARSE_NODE_NULL;
633+
goto finished;
634+
635+
}
636+
599637
// check we are at the end of the token stream
600638
if (!mp_lexer_is_kind(lex, MP_TOKEN_END)) {
601639
goto syntax_error;
@@ -609,7 +647,7 @@ mp_parse_node_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind, mp_p
609647

610648
// get the root parse node that we created
611649
assert(parser->result_stack_top == 1);
612-
mp_parse_node_t result = parser->result_stack[0];
650+
result = parser->result_stack[0];
613651

614652
finished:
615653
// free the memory that we don't need anymore

py/parse.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef enum {
6767
} mp_parse_input_kind_t;
6868

6969
typedef enum {
70+
MP_PARSE_ERROR_MEMORY,
7071
MP_PARSE_ERROR_UNEXPECTED_INDENT,
7172
MP_PARSE_ERROR_UNMATCHED_UNINDENT,
7273
MP_PARSE_ERROR_INVALID_SYNTAX,

py/parsehelper.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@
1111
#include "obj.h"
1212
#include "parsehelper.h"
1313

14+
#define STR_MEMORY "parser could not allocate enough memory"
1415
#define STR_UNEXPECTED_INDENT "unexpected indent"
1516
#define STR_UNMATCHED_UNINDENT "unindent does not match any outer indentation level"
1617
#define STR_INVALID_SYNTAX "invalid syntax"
1718

1819
void mp_parse_show_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_kind) {
1920
printf(" File \"%s\", line %d, column %d\n", qstr_str(mp_lexer_source_name(lex)), mp_lexer_cur(lex)->src_line, mp_lexer_cur(lex)->src_column);
2021
switch (parse_error_kind) {
22+
case MP_PARSE_ERROR_MEMORY:
23+
printf("MemoryError: %s\n", STR_MEMORY);
24+
break;
25+
2126
case MP_PARSE_ERROR_UNEXPECTED_INDENT:
2227
printf("IndentationError: %s\n", STR_UNEXPECTED_INDENT);
2328
break;
@@ -36,6 +41,9 @@ void mp_parse_show_exception(mp_lexer_t *lex, mp_parse_error_kind_t parse_error_
3641
mp_obj_t mp_parse_make_exception(mp_parse_error_kind_t parse_error_kind) {
3742
// TODO add source file and line number to exception?
3843
switch (parse_error_kind) {
44+
case MP_PARSE_ERROR_MEMORY:
45+
return mp_obj_new_exception_msg(&mp_type_MemoryError, STR_MEMORY);
46+
3947
case MP_PARSE_ERROR_UNEXPECTED_INDENT:
4048
return mp_obj_new_exception_msg(&mp_type_IndentationError, STR_UNEXPECTED_INDENT);
4149

0 commit comments

Comments
 (0)