ruby-changes:58167
From: =E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3 <ko1@a...>
Date: Wed, 9 Oct 2019 12:12:46 +0900 (JST)
Subject: [ruby-changes:58167] 7e0ae1698d (master): avoid overflow in integer multiplication
https://git.ruby-lang.org/ruby.git/commit/?id=7e0ae1698d From 7e0ae1698d4db0baec858a46de8d1ae875360cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= <shyouhei@r...> Date: Mon, 7 Oct 2019 16:56:08 +0900 Subject: avoid overflow in integer multiplication This changeset basically replaces `ruby_xmalloc(x * y)` into `ruby_xmalloc2(x, y)`. Some convenient functions are also provided for instance `rb_xmalloc_mul_add(x, y, z)` which allocates x * y + z byes. diff --git a/compile.c b/compile.c index 7a88f81..df7f467 100644 --- a/compile.c +++ b/compile.c @@ -869,6 +869,13 @@ compile_data_alloc(rb_iseq_t *iseq, size_t size) https://github.com/ruby/ruby/blob/trunk/compile.c#L869 return compile_data_alloc_with_arena(arena, size); } +static inline void * +compile_data_alloc2(rb_iseq_t *iseq, size_t x, size_t y) +{ + size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError); + return compile_data_alloc(iseq, size); +} + static INSN * compile_data_alloc_insn(rb_iseq_t *iseq) { @@ -1127,7 +1134,7 @@ new_insn_body(rb_iseq_t *iseq, int line_no, enum ruby_vminsn_type insn_id, int a https://github.com/ruby/ruby/blob/trunk/compile.c#L1134 if (argc > 0) { int i; va_init_list(argv, argc); - operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * argc); + operands = compile_data_alloc2(iseq, sizeof(VALUE), argc); for (i = 0; i < argc; i++) { VALUE v = va_arg(argv, VALUE); operands[i] = v; @@ -1168,7 +1175,7 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_cal https://github.com/ruby/ruby/blob/trunk/compile.c#L1175 static INSN * new_insn_send(rb_iseq_t *iseq, int line_no, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_call_info_kw_arg *keywords) { - VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 3); + VALUE *operands = compile_data_alloc2(iseq, sizeof(VALUE), 3); operands[0] = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL); operands[1] = Qfalse; /* cache */ operands[2] = (VALUE)blockiseq; @@ -2080,8 +2087,10 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) https://github.com/ruby/ruby/blob/trunk/compile.c#L2087 insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num); positions = ALLOC_N(unsigned int, insn_num); body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size); - body->ci_entries = (struct rb_call_info *)ruby_xmalloc(sizeof(struct rb_call_info) * body->ci_size + - sizeof(struct rb_call_info_with_kwarg) * body->ci_kw_size); + body->ci_entries = + rb_xmalloc_mul_add_mul( + sizeof(struct rb_call_info), body->ci_size, + sizeof(struct rb_call_info_with_kwarg), body->ci_kw_size); MEMZERO(body->ci_entries + body->ci_size, struct rb_call_info_with_kwarg, body->ci_kw_size); /* need to clear ci_kw entries */ body->cc_entries = ZALLOC_N(struct rb_call_cache, body->ci_size + body->ci_kw_size); @@ -3197,7 +3206,7 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) https://github.com/ruby/ruby/blob/trunk/compile.c#L3206 if (insn_id == BIN(opt_neq)) { VALUE *old_operands = iobj->operands; iobj->operand_size = 4; - iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE)); + iobj->operands = compile_data_alloc2(iseq, iobj->operand_size, sizeof(VALUE)); iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); iobj->operands[1] = Qfalse; /* CALL_CACHE */ iobj->operands[2] = old_operands[0]; @@ -3367,7 +3376,7 @@ new_unified_insn(rb_iseq_t *iseq, https://github.com/ruby/ruby/blob/trunk/compile.c#L3376 if (argc > 0) { ptr = operands = - (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * argc); + compile_data_alloc2(iseq, sizeof(VALUE), argc); } /* copy operands */ @@ -3823,7 +3832,8 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, https://github.com/ruby/ruby/blob/trunk/compile.c#L3832 node = root_node->nd_head; { int len = (int)node->nd_alen / 2; - struct rb_call_info_kw_arg *kw_arg = (struct rb_call_info_kw_arg *)ruby_xmalloc(sizeof(struct rb_call_info_kw_arg) + sizeof(VALUE) * (len - 1)); + struct rb_call_info_kw_arg *kw_arg = + rb_xmalloc_mul_add(len - 1, sizeof(VALUE), sizeof(struct rb_call_info_kw_arg)); VALUE *keywords = kw_arg->keywords; int i = 0; kw_arg->keyword_len = len; @@ -8776,7 +8786,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, https://github.com/ruby/ruby/blob/trunk/compile.c#L8786 } if (argc > 0) { - argv = compile_data_alloc(iseq, sizeof(VALUE) * argc); + argv = compile_data_alloc2(iseq, sizeof(VALUE), argc); for (j=0; j<argc; j++) { VALUE op = rb_ary_entry(obj, j+1); switch (insn_op_type((VALUE)insn_id, j)) { @@ -9381,9 +9391,10 @@ ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long of https://github.com/ruby/ruby/blob/trunk/compile.c#L9391 } static void * -ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, int size) +ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y) { - void *buff = ruby_xmalloc(size); + void *buff = ruby_xmalloc2(x, y); + size_t size = x * y; memcpy(buff, load->current_buffer->buff + offset, size); return buff; } @@ -9393,7 +9404,7 @@ ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, int size) https://github.com/ruby/ruby/blob/trunk/compile.c#L9404 #define IBF_W(b, type, n) (IBF_W_ALIGN(type), (type *)(VALUE)IBF_WP(b, type, n)) #define IBF_WV(variable) ibf_dump_write(dump, &(variable), sizeof(variable)) #define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n)) -#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type) * (n)) +#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type), (n)) #define IBF_ZERO(variable) memset(&(variable), 0, sizeof(variable)) static int @@ -9667,7 +9678,7 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, ibf_offset_t b https://github.com/ruby/ruby/blob/trunk/compile.c#L9678 { unsigned int code_index; ibf_offset_t reading_pos = bytecode_offset; - VALUE *code = ruby_xmalloc(sizeof(VALUE) * iseq_size); + VALUE *code = ALLOC_N(VALUE, iseq_size); struct rb_iseq_constant_body *load_body = iseq->body; struct rb_call_info *ci_entries = load_body->ci_entries; @@ -9884,7 +9895,7 @@ static unsigned int * https://github.com/ruby/ruby/blob/trunk/compile.c#L9895 ibf_load_insns_info_positions(const struct ibf_load *load, ibf_offset_t positions_offset, unsigned int size) { ibf_offset_t reading_pos = positions_offset; - unsigned int *positions = ruby_xmalloc(sizeof(unsigned int) * size); + unsigned int *positions = ALLOC_N(unsigned int, size); unsigned int last = 0; unsigned int i; @@ -10039,8 +10050,10 @@ ibf_load_ci_entries(const struct ibf_load *load, https://github.com/ruby/ruby/blob/trunk/compile.c#L10050 unsigned int i; - struct rb_call_info *ci_entries = ruby_xmalloc(sizeof(struct rb_call_info) * ci_size + - sizeof(struct rb_call_info_with_kwarg) * ci_kw_size); + struct rb_call_info *ci_entries = + rb_xmalloc_mul_add_mul( + sizeof(struct rb_call_info), ci_size, + sizeof(struct rb_call_info_with_kwarg), ci_kw_size); struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&ci_entries[ci_size]; for (i = 0; i < ci_size; i++) { @@ -10060,7 +10073,8 @@ ibf_load_ci_entries(const struct ibf_load *load, https://github.com/ruby/ruby/blob/trunk/compile.c#L10073 int keyword_len = (int)ibf_load_small_value(load, &reading_pos); - ci_kw_entries[i].kw_arg = ruby_xmalloc(sizeof(struct rb_call_info_kw_arg) + sizeof(VALUE) * (keyword_len - 1)); + ci_kw_entries[i].kw_arg = + rb_xmalloc_mul_add(keyword_len - 1, sizeof(VALUE), sizeof(struct rb_call_info_kw_arg)); ci_kw_entries[i].kw_arg->keyword_len = keyword_len; diff --git a/dir.c b/dir.c index d20cf60..e98fa25 100644 --- a/dir.c +++ b/dir.c @@ -1343,8 +1343,20 @@ sys_enc_warning_in(const char *func, const char *mesg, rb_encoding *enc) https://github.com/ruby/ruby/blob/trunk/dir.c#L1343 #define sys_warning(val, enc) \ ((flags & GLOB_VERBOSE) ? sys_enc_warning_in(RUBY_FUNCTION_NAME_STRING, (val), (enc)) :(void)0) +static inline void * +glob_alloc_n(size_t x, size_t y) +{ + size_t z; + if (rb_mul_size_overflow(x, y, SSIZE_MAX, &z)) { + rb_memerror(); /* or...? */ + } + else { + return malloc(z); + } +} + #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type))) -#define GLOB_ALLOC_N(type, n) ((type *)malloc(sizeof(type) * (n))) +#define GLOB_ALLOC_N(type, n) ((type *)glob_alloc_n(sizeof(type), n)) #define GLOB_REALLOC(ptr, size) realloc((ptr), (size)) #define GLOB_FREE(ptr) free(ptr) #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status)) diff --git a/encoding.c b/encoding.c index d6fc934..22fa5ee 100644 --- a/encoding.c +++ b/encoding.c @@ -266,7 +266,7 @@ enc_table_expand(int newsize) https://github.com/ruby/ruby/blob/trunk/encoding.c#L266 if (enc_table.size >= newsize) return newsize; newsize = (newsize + 7) / 8 * 8; - ent = xrealloc(enc_table.list, sizeof(*enc_table.list) * newsize); + ent = REALLOC_N(enc_table.list, struct rb_encoding_entry, newsize); memset(ent + enc_table.size, 0, sizeof(*ent)*(newsize - enc_table.size)); enc_table.list = ent; enc_table.size = newsize; diff --git a/gc.c b/gc.c index 1815471..2ff9094 100644 --- a/gc.c +++ b/gc.c @@ -80,6 +80,157 @@ https://github.com/ruby/ruby/blob/trunk/gc.c#L80 #define rb_setjmp(env) RUBY (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/