ruby-changes:63472
From: Koichi <ko1@a...>
Date: Thu, 29 Oct 2020 23:43:19 +0900 (JST)
Subject: [ruby-changes:63472] 07c03bc309 (master): check isolated Proc more strictly
https://git.ruby-lang.org/ruby.git/commit/?id=07c03bc309 From 07c03bc30984a496558d9e830bc4fb2f8cfb1854 Mon Sep 17 00:00:00 2001 From: Koichi Sasada <ko1@a...> Date: Fri, 23 Oct 2020 13:27:21 +0900 Subject: check isolated Proc more strictly Isolated Proc prohibit to access outer local variables, but it was violated by binding and so on, so they should be error. diff --git a/compile.c b/compile.c index 767e6c4..e0d4bc5 100644 --- a/compile.c +++ b/compile.c @@ -1319,9 +1319,12 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node, https://github.com/ruby/ruby/blob/trunk/compile.c#L1319 ast.line_count = -1; debugs("[new_child_iseq]> ---------------------------------------\n"); + int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; ret_iseq = rb_iseq_new_with_opt(&ast, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option); + INT2FIX(line_no), parent, + isolated_depth ? isolated_depth + 1 : 0, + type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq]< ---------------------------------------\n"); return ret_iseq; } @@ -1601,13 +1604,50 @@ iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel) https://github.com/ruby/ruby/blob/trunk/compile.c#L1604 } static void -check_access_outer_variables(const rb_iseq_t *iseq, int level) +access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write) +{ + int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; + + if (isolated_depth && level >= isolated_depth) { + if (id == rb_intern("yield")) { + COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc", rb_id2name(id)); + } + else { + COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable `%s' from isolated Proc", rb_id2name(id)); + } + } + + for (int i=0; i<level; i++) { + VALUE val; + struct rb_id_table *ovs = iseq->body->outer_variables; + + if (!ovs) { + ovs = iseq->body->outer_variables = rb_id_table_create(8); + } + + if (rb_id_table_lookup(iseq->body->outer_variables, id, &val)) { + if (write && !val) { + rb_id_table_insert(iseq->body->outer_variables, id, Qtrue); + } + } + else { + rb_id_table_insert(iseq->body->outer_variables, id, write ? Qtrue : Qfalse); + } + + iseq = iseq->body->parent_iseq; + } +} + +static ID +iseq_lvar_id(const rb_iseq_t *iseq, int idx, int level) { - // set access_outer_variables for (int i=0; i<level; i++) { - iseq->body->access_outer_variables = TRUE; iseq = iseq->body->parent_iseq; } + + ID id = iseq->body->local_table[iseq->body->local_table_size - idx]; + // fprintf(stderr, "idx:%d level:%d ID:%s\n", idx, level, rb_id2name(id)); + return id; } static void @@ -1619,7 +1659,7 @@ iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, in https://github.com/ruby/ruby/blob/trunk/compile.c#L1659 else { ADD_INSN2(seq, line, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); } - check_access_outer_variables(iseq, level); + if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse); } static void @@ -1631,7 +1671,7 @@ iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, in https://github.com/ruby/ruby/blob/trunk/compile.c#L1671 else { ADD_INSN2(seq, line, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); } - check_access_outer_variables(iseq, level); + if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue); } @@ -8215,7 +8255,12 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in https://github.com/ruby/ruby/blob/trunk/compile.c#L8255 ADD_INSN(ret, line, pop); } - iseq->body->access_outer_variables = TRUE; + int level = 0; + const rb_iseq_t *tmp_iseq = iseq; + for (; tmp_iseq != iseq->body->local_iseq; level++ ) { + tmp_iseq = tmp_iseq->body->parent_iseq; + } + if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true); break; } case NODE_LVAR:{ diff --git a/iseq.c b/iseq.c index 85ced12..449d338 100644 --- a/iseq.c +++ b/iseq.c @@ -130,6 +130,7 @@ rb_iseq_free(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/iseq.c#L130 ruby_xfree((void *)body->param.keyword); } compile_data_free(ISEQ_COMPILE_DATA(iseq)); + if (body->outer_variables) rb_id_table_free(body->outer_variables); ruby_xfree(body); } @@ -575,8 +576,7 @@ new_arena(void) https://github.com/ruby/ruby/blob/trunk/iseq.c#L576 static VALUE prepare_iseq_build(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id, - const rb_iseq_t *parent, enum iseq_type type, - const rb_compile_option_t *option) + const rb_iseq_t *parent, int isolated_depth, enum iseq_type type, const rb_compile_option_t *option) { VALUE coverage = Qfalse; VALUE err_info = Qnil; @@ -603,11 +603,11 @@ prepare_iseq_build(rb_iseq_t *iseq, https://github.com/ruby/ruby/blob/trunk/iseq.c#L603 ISEQ_COMPILE_DATA(iseq)->node.storage_head = ISEQ_COMPILE_DATA(iseq)->node.storage_current = new_arena(); ISEQ_COMPILE_DATA(iseq)->insn.storage_head = ISEQ_COMPILE_DATA(iseq)->insn.storage_current = new_arena(); + ISEQ_COMPILE_DATA(iseq)->isolated_depth = isolated_depth; ISEQ_COMPILE_DATA(iseq)->option = option; - ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL; - ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table; + if (option->coverage_enabled) { VALUE coverages = rb_get_coverages(); @@ -794,8 +794,8 @@ rb_iseq_t * https://github.com/ruby/ruby/blob/trunk/iseq.c#L794 rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum iseq_type type) { - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, type, - &COMPILE_OPTION_DEFAULT); + return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, + 0, type, &COMPILE_OPTION_DEFAULT); } rb_iseq_t * @@ -810,8 +810,8 @@ rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath https://github.com/ruby/ruby/blob/trunk/iseq.c#L810 } } - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, ISEQ_TYPE_TOP, - &COMPILE_OPTION_DEFAULT); + return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0, + ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT); } rb_iseq_t * @@ -819,7 +819,14 @@ rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_ https://github.com/ruby/ruby/blob/trunk/iseq.c#L819 { return rb_iseq_new_with_opt(ast, rb_fstring_lit("<main>"), path, realpath, INT2FIX(0), - parent, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT); + parent, 0, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT); +} + +rb_iseq_t * +rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth) +{ + return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, + parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT); } static inline rb_iseq_t * @@ -838,8 +845,8 @@ iseq_translate(rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/iseq.c#L845 rb_iseq_t * rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, - VALUE first_lineno, const rb_iseq_t *parent, - enum iseq_type type, const rb_compile_option_t *option) + VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, + enum iseq_type type, const rb_compile_option_t *option) { const NODE *node = ast ? ast->root : 0; /* TODO: argument check */ @@ -854,7 +861,7 @@ rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE rea https://github.com/ruby/ruby/blob/trunk/iseq.c#L861 } if (ast && ast->compile_option) rb_iseq_make_compile_option(&new_opt, ast->compile_option); - prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1, parent, type, &new_opt); + prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1, parent, isolated_depth, type, &new_opt); rb_iseq_compile_node(iseq, node); finish_iseq_build(iseq); @@ -873,7 +880,7 @@ rb_iseq_new_with_callback( https://github.com/ruby/ruby/blob/trunk/iseq.c#L880 rb_iseq_t *iseq = iseq_alloc(); if (!option) option = &COMPILE_OPTION_DEFAULT; - prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, type, option); + prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, 0, type, option); rb_iseq_compile_callback(iseq, ifunc); finish_iseq_build(iseq); @@ -986,7 +993,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt) https://github.com/ruby/ruby/blob/trunk/iseq.c#L993 make_compile_option(&option, opt); option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */ prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id), - parent, (enum iseq_type)iseq_type, &option); + parent, 0, (enum iseq_type)iseq_type, &option); rb_iseq_build_from_ary(iseq, misc, locals, params, except (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/