diff --git a/compile.c b/compile.c index 7a88f81daa..df7f467c81 100644 --- a/compile.c +++ b/compile.c @@ -869,6 +869,13 @@ compile_data_alloc(rb_iseq_t *iseq, size_t size) 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 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 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) 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) 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, 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, 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, } if (argc > 0) { - argv = compile_data_alloc(iseq, sizeof(VALUE) * argc); + argv = compile_data_alloc2(iseq, sizeof(VALUE), argc); for (j=0; jcurrent_buffer->buff + offset, size); return buff; } @@ -9393,7 +9404,7 @@ ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, int size) #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 { 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 * 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, 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, 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 d20cf60a7f..e98fa25278 100644 --- a/dir.c +++ b/dir.c @@ -1343,8 +1343,20 @@ sys_enc_warning_in(const char *func, const char *mesg, rb_encoding *enc) #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 d6fc9349db..22fa5eec60 100644 --- a/encoding.c +++ b/encoding.c @@ -266,7 +266,7 @@ enc_table_expand(int newsize) 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 18154714db..2ff90942e6 100644 --- a/gc.c +++ b/gc.c @@ -80,6 +80,157 @@ #define rb_setjmp(env) RUBY_SETJMP(env) #define rb_jmp_buf rb_jmpbuf_t +#if defined(_MSC_VER) && defined(_WIN64) +#include +#pragma intrinsic(_umul128) +#endif + +/* Expecting this struct to be elminated by function inlinings */ +struct optional { + bool left; + size_t right; +}; + +static inline struct optional +size_mul_overflow(size_t x, size_t y) +{ + bool p; + size_t z; +#if 0 + +#elif defined(HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW) + p = __builtin_mul_overflow(x, y, &z); + +#elif defined(DSIZE_T) + RB_GNUC_EXTENSION DSIZE_T dx = x; + RB_GNUC_EXTENSION DSIZE_T dy = y; + RB_GNUC_EXTENSION DSIZE_T dz = dx * dy; + p = dz > SIZE_MAX; + z = (size_t)dz; + +#elif defined(_MSC_VER) && defined(_WIN64) + unsigned __int64 dp; + unsigned __int64 dz = _umul128(x, y, &dp); + p = (bool)dp; + z = (size_t)dz; + +#else + /* https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap */ + p = (y != 0) && (x > SIZE_MAX / y); + z = x * y; + +#endif + return (struct optional) { p, z, }; +} + +static inline struct optional +size_add_overflow(size_t x, size_t y) +{ + size_t z; + bool p; +#if 0 + +#elif defined(HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW) + p = __builtin_add_overflow(x, y, &z); + +#elif defined(DSIZE_T) + RB_GNUC_EXTENSION DSIZE_T dx = x; + RB_GNUC_EXTENSION DSIZE_T dy = x; + RB_GNUC_EXTENSION DSIZE_T dz = dx + dy; + p = dz > SIZE_MAX; + z = (size_t)dz; + +#else + z = x + y; + p = z < y; + +#endif + return (struct optional) { p, z, }; +} + +static inline struct optional +size_mul_add_overflow(size_t x, size_t y, size_t z) /* x * y + z */ +{ + struct optional t = size_mul_overflow(x, y); + struct optional u = size_add_overflow(t.right, z); + return (struct optional) { t.left || u.left, u.right }; +} + +static inline struct optional +size_mul_add_mul_overflow(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +{ + struct optional t = size_mul_overflow(x, y); + struct optional u = size_mul_overflow(z, w); + struct optional v = size_add_overflow(t.right, u.right); + return (struct optional) { t.left || u.left || v.left, v.right }; +} + +static inline size_t +size_mul_or_raise(size_t x, size_t y, VALUE exc) +{ + struct optional t = size_mul_overflow(x, y); + if (LIKELY(!t.left)) { + return t.right; + } + else { + rb_raise( + exc, + "integer overflow: %"PRIuSIZE + " * %"PRIuSIZE + " > %"PRIuSIZE, + x, y, SIZE_MAX); + } +} + +size_t +rb_size_mul_or_raise(size_t x, size_t y, VALUE exc) +{ + return size_mul_or_raise(x, y, exc); +} + +static inline size_t +size_mul_add_or_raise(size_t x, size_t y, size_t z, VALUE exc) +{ + struct optional t = size_mul_add_overflow(x, y, z); + if (LIKELY(!t.left)) { + return t.right; + } + else { + rb_raise( + exc, + "integer overflow: %"PRIuSIZE + " * %"PRIuSIZE + " + %"PRIuSIZE + " > %"PRIuSIZE, + x, y, z, SIZE_MAX); + } +} + +size_t +rb_size_mul_add_or_raise(size_t x, size_t y, size_t z, VALUE exc) +{ + return size_mul_add_or_raise(x, y, z, exc); +} + +static inline size_t +size_mul_add_mul_or_raise(size_t x, size_t y, size_t z, size_t w, VALUE exc) +{ + struct optional t = size_mul_add_mul_overflow(x, y, z, w); + if (LIKELY(!t.left)) { + return t.right; + } + else { + rb_raise( + exc, + "integer overflow: %"PRIdSIZE + " * %"PRIdSIZE + " + %"PRIdSIZE + " * %"PRIdSIZE + " > %"PRIdSIZE, + x, y, z, w, SIZE_MAX); + } +} + #if defined(HAVE_RB_GC_GUARDED_PTR_VAL) && HAVE_RB_GC_GUARDED_PTR_VAL /* trick the compiler into thinking a external signal handler uses this */ volatile VALUE rb_gc_guarded_val; @@ -1410,10 +1561,16 @@ RVALUE_WHITE_P(VALUE obj) --------------------------- ObjectSpace ----------------------------- */ +static inline void * +calloc1(size_t n) +{ + return calloc(1, n); +} + rb_objspace_t * rb_objspace_alloc(void) { - rb_objspace_t *objspace = calloc(1, sizeof(rb_objspace_t)); + rb_objspace_t *objspace = calloc1(sizeof(rb_objspace_t)); malloc_limit = gc_params.malloc_limit_min; list_head_init(&objspace->eden_heap.pages); list_head_init(&objspace->tomb_heap.pages); @@ -1466,7 +1623,7 @@ static void heap_pages_expand_sorted_to(rb_objspace_t *objspace, size_t next_length) { struct heap_page **sorted; - size_t size = next_length * sizeof(struct heap_page *); + size_t size = size_mul_or_raise(next_length, sizeof(struct heap_page *), rb_eRuntimeError); gc_report(3, objspace, "heap_pages_expand_sorted: next_length: %d, size: %d\n", (int)next_length, (int)size); @@ -1626,7 +1783,7 @@ heap_page_allocate(rb_objspace_t *objspace) } /* assign heap_page entry */ - page = (struct heap_page *)calloc(1, sizeof(struct heap_page)); + page = calloc1(sizeof(struct heap_page)); if (page == 0) { rb_aligned_free(page_body); rb_memerror(); @@ -7595,7 +7752,8 @@ static struct heap_page ** allocate_page_list(rb_objspace_t *objspace, page_compare_func_t *comparator) { size_t total_pages = heap_eden->total_pages; - struct heap_page *page = 0, **page_list = malloc(total_pages * sizeof(struct heap_page *)); + size_t size = size_mul_or_raise(total_pages, sizeof(struct heap_page *), rb_eRuntimeError); + struct heap_page *page = 0, **page_list = malloc(size); int i = 0; list_for_each(&heap_eden->pages, page, page_node) { @@ -9753,11 +9911,7 @@ objspace_xmalloc0(rb_objspace_t *objspace, size_t size) static inline size_t xmalloc2_size(const size_t count, const size_t elsize) { - size_t ret; - if (rb_mul_size_overflow(count, elsize, SSIZE_MAX, &ret)) { - ruby_malloc_size_overflow(count, elsize); - } - return ret; + return size_mul_or_raise(count, elsize, rb_eArgError); } static void * @@ -9897,7 +10051,7 @@ objspace_xfree(rb_objspace_t *objspace, void *ptr, size_t old_size) /* hit */ } else { - data = malloc(sizeof(size_t) * 2); + data = malloc(xmalloc2_size(2, sizeof(size_t))); if (data == NULL) rb_bug("objspace_xfree: can not allocate memory"); data[0] = data[1] = 0; st_insert(malloc_info_file_table, key, (st_data_t)data); @@ -9961,7 +10115,7 @@ objspace_xcalloc(rb_objspace_t *objspace, size_t size) void *mem; size = objspace_malloc_prepare(objspace, size); - TRY_WITH_GC(mem = calloc(1, size)); + TRY_WITH_GC(mem = calloc1(size)); return objspace_malloc_fixup(objspace, mem, size); } @@ -9996,10 +10150,7 @@ ruby_xrealloc_body(void *ptr, size_t new_size) void * ruby_sized_xrealloc2(void *ptr, size_t n, size_t size, size_t old_n) { - size_t len = size * n; - if (n != 0 && size != len / n) { - rb_raise(rb_eArgError, "realloc: possible integer overflow"); - } + size_t len = xmalloc2_size(n, size); return objspace_xrealloc(&rb_objspace, ptr, len, old_n * size); } @@ -10026,6 +10177,34 @@ ruby_xfree(void *x) ruby_sized_xfree(x, 0); } +void * +rb_xmalloc_mul_add(size_t x, size_t y, size_t z) /* x * y + z */ +{ + size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); + return ruby_xmalloc(w); +} + +void * +rb_xrealloc_mul_add(const void *p, size_t x, size_t y, size_t z) /* x * y + z */ +{ + size_t w = size_mul_add_or_raise(x, y, z, rb_eArgError); + return ruby_xrealloc((void *)p, w); +} + +void * +rb_xmalloc_mul_add_mul(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +{ + size_t u = size_mul_add_mul_or_raise(x, y, z, w, rb_eArgError); + return ruby_xmalloc(u); +} + +void * +rb_xcalloc_mul_add_mul(size_t x, size_t y, size_t z, size_t w) /* x * y + z * w */ +{ + size_t u = size_mul_add_mul_or_raise(x, y, z, w, rb_eArgError); + return ruby_xcalloc(u, 1); +} + /* Mimic ruby_xmalloc, but need not rb_objspace. * should return pointer suitable for ruby_xfree */ @@ -10276,7 +10455,7 @@ wmap_final_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing) return ST_DELETE; } if (j < i) { - ptr = ruby_sized_xrealloc2(ptr, j + 1, sizeof(VALUE), i); + SIZED_REALLOC_N(ptr, VALUE, j + 1, i); ptr[0] = j; *value = (st_data_t)ptr; } @@ -10491,7 +10670,7 @@ wmap_aset_update(st_data_t *key, st_data_t *val, st_data_t arg, int existing) if (existing) { size = (ptr = optr = (VALUE *)*val)[0]; ++size; - ptr = ruby_sized_xrealloc2(ptr, size + 1, sizeof(VALUE), size); + SIZED_REALLOC_N(ptr, VALUE, size + 1, size); } else { optr = 0; @@ -10636,12 +10815,12 @@ gc_prof_setup_new_record(rb_objspace_t *objspace, int reason) if (!objspace->profile.records) { objspace->profile.size = GC_PROFILE_RECORD_DEFAULT_SIZE; - objspace->profile.records = malloc(sizeof(gc_profile_record) * objspace->profile.size); + objspace->profile.records = malloc(xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size)); } if (index >= objspace->profile.size) { void *ptr; objspace->profile.size += 1000; - ptr = realloc(objspace->profile.records, sizeof(gc_profile_record) * objspace->profile.size); + ptr = realloc(objspace->profile.records, xmalloc2_size(sizeof(gc_profile_record), objspace->profile.size)); if (!ptr) rb_memerror(); objspace->profile.records = ptr; } diff --git a/internal.h b/internal.h index 3f57523bd3..6afd21d571 100644 --- a/internal.h +++ b/internal.h @@ -1627,7 +1627,7 @@ void *ruby_sized_xrealloc(void *ptr, size_t new_size, size_t old_size) RUBY_ATTR void *ruby_sized_xrealloc2(void *ptr, size_t new_count, size_t element_size, size_t old_count) RUBY_ATTR_ALLOC_SIZE((2, 3)); void ruby_sized_xfree(void *x, size_t size); RUBY_SYMBOL_EXPORT_END -#define SIZED_REALLOC_N(var,type,n,old_n) ((var)=(type*)ruby_sized_xrealloc((char*)(var), (n) * sizeof(type), (old_n) * sizeof(type))) +#define SIZED_REALLOC_N(var,type,n,old_n) ((var)=(type*)ruby_sized_xrealloc2((void*)(var), (n), sizeof(type), (old_n))) #endif /* optimized version of NEWOBJ() */ @@ -1647,6 +1647,13 @@ __attribute__((__alloc_align__(1))) void *rb_aligned_malloc(size_t, size_t) RUBY_ATTR_MALLOC RUBY_ATTR_ALLOC_SIZE((2)); void rb_aligned_free(void *); +size_t rb_size_mul_or_raise(size_t, size_t, VALUE); /* used in compile.c */ +size_t rb_size_mul_add_or_raise(size_t, size_t, size_t, VALUE); /* used in iseq.h */ +void *rb_xmalloc_mul_add(size_t, size_t, size_t) RUBY_ATTR_MALLOC; +void *rb_xrealloc_mul_add(const void *, size_t, size_t, size_t); +void *rb_xmalloc_mul_add_mul(size_t, size_t, size_t, size_t) RUBY_ATTR_MALLOC; +void *rb_xcalloc_mul_add_mul(size_t, size_t, size_t, size_t) RUBY_ATTR_MALLOC; + /* hash.c */ #if RHASH_CONVERT_TABLE_DEBUG struct st_table *rb_hash_tbl_raw(VALUE hash, const char *file, int line); diff --git a/iseq.c b/iseq.c index 281d5e4224..10d0723a83 100644 --- a/iseq.c +++ b/iseq.c @@ -3391,7 +3391,10 @@ succ_index_table_create(int max_pos, int *data, int size) { const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9; const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512; - struct succ_index_table *sd = ruby_xcalloc(imm_size * sizeof(uint64_t) + succ_size * sizeof(struct succ_dict_block), 1); /* zero cleared */ + struct succ_index_table *sd = + rb_xcalloc_mul_add_mul( + imm_size, sizeof(uint64_t), + succ_size, sizeof(struct succ_dict_block)); int i, j, k, r; r = 0; @@ -3426,7 +3429,7 @@ succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size) { const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9; const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512; - unsigned int *positions = ruby_xmalloc(sizeof(unsigned int) * size), *p; + unsigned int *positions = ALLOC_N(unsigned int, size), *p; int i, j, k, r = -1; p = positions; for (j = 0; j < imm_size; j++) { diff --git a/iseq.h b/iseq.h index 5d24b81601..ef0dd7176d 100644 --- a/iseq.h +++ b/iseq.h @@ -26,7 +26,9 @@ extern const ID rb_iseq_shared_exc_local_tbl[]; static inline size_t rb_call_info_kw_arg_bytes(int keyword_len) { - return sizeof(struct rb_call_info_kw_arg) + sizeof(VALUE) * (keyword_len - 1); + return rb_size_mul_add_or_raise( + keyword_len - 1, sizeof(VALUE), sizeof(struct rb_call_info_kw_arg), + rb_eRuntimeError); } #define ISEQ_COVERAGE(iseq) iseq->body->variable.coverage @@ -67,7 +69,7 @@ static inline VALUE * ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size) { return iseq->body->variable.original_iseq = - ruby_xmalloc2(size, sizeof(VALUE)); + ALLOC_N(VALUE, size); } #define ISEQ_TRACE_EVENTS (RUBY_EVENT_LINE | \ diff --git a/node.c b/node.c index 8f42f7e5a5..cd8d73171e 100644 --- a/node.c +++ b/node.c @@ -1145,8 +1145,13 @@ init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head) static node_buffer_t * rb_node_buffer_new(void) { - size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE); - node_buffer_t *nb = xmalloc(sizeof(node_buffer_t) + (bucket_size * 2)); + const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE); + const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size * 2); + STATIC_ASSERT( + integer_overflow, + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE) + > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t)); + node_buffer_t *nb = ruby_xmalloc(alloc_size); init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1]); init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size)); nb->mark_ary = Qnil; @@ -1179,7 +1184,7 @@ ast_newnode_in_bucket(node_buffer_list_t *nb) if (nb->idx >= nb->len) { long n = nb->len * 2; node_buffer_elem_t *nbe; - nbe = xmalloc(offsetof(node_buffer_elem_t, buf) + n * sizeof(NODE)); + nbe = rb_xmalloc_mul_add(n, sizeof(NODE), offsetof(node_buffer_elem_t, buf)); nbe->len = n; nb->idx = 0; nb->len = n; diff --git a/string.c b/string.c index 3f0e69689c..77f360b8d2 100644 --- a/string.c +++ b/string.c @@ -2175,7 +2175,7 @@ rb_str_modify_expand(VALUE str, long expand) if (expand < 0) { rb_raise(rb_eArgError, "negative expanding string size"); } - if (expand > LONG_MAX - len) { + if (expand >= LONG_MAX - len) { rb_raise(rb_eArgError, "string size too big"); } diff --git a/thread.c b/thread.c index 901667e1b3..ea0956eabd 100644 --- a/thread.c +++ b/thread.c @@ -3867,7 +3867,9 @@ rb_fd_set(int fd, rb_fdset_t *set) } if (set->fdset->fd_count >= (unsigned)set->capa) { set->capa = (set->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE; - set->fdset = xrealloc(set->fdset, sizeof(unsigned int) + sizeof(SOCKET) * set->capa); + set->fdset = + rb_xrealloc_mul_add( + set->fdset, set->capa, sizeof(SOCKET), sizeof(unsigned int)); } set->fdset->fd_array[set->fdset->fd_count++] = s; } diff --git a/transient_heap.c b/transient_heap.c index dcf65fc7ab..6cdd34284f 100644 --- a/transient_heap.c +++ b/transient_heap.c @@ -440,6 +440,9 @@ Init_TransientHeap(void) theap->promoted_objects_index = 0; /* should not use ALLOC_N to be free from GC */ theap->promoted_objects = malloc(sizeof(VALUE) * theap->promoted_objects_size); + STATIC_ASSERT( + integer_overflow, + sizeof(VALUE) <= SIZE_MAX / TRANSIENT_HEAP_PROMOTED_DEFAULT_SIZE); if (theap->promoted_objects == NULL) rb_bug("Init_TransientHeap: malloc failed."); } @@ -618,7 +621,13 @@ transient_heap_promote_add(struct transient_heap* theap, VALUE obj) if (theap->promoted_objects_size <= theap->promoted_objects_index) { theap->promoted_objects_size *= 2; if (TRANSIENT_HEAP_DEBUG >= 1) fprintf(stderr, "rb_transient_heap_promote: expand table to %d\n", theap->promoted_objects_size); - theap->promoted_objects = realloc(theap->promoted_objects, theap->promoted_objects_size * sizeof(VALUE)); + if (UNLIKELY((size_t)theap->promoted_objects_size > SIZE_MAX / sizeof(VALUE))) { + /* realloc failure due to integer overflow */ + theap->promoted_objects = NULL; + } + else { + theap->promoted_objects = realloc(theap->promoted_objects, theap->promoted_objects_size * sizeof(VALUE)); + } if (theap->promoted_objects == NULL) rb_bug("rb_transient_heap_promote: realloc failed"); } theap->promoted_objects[theap->promoted_objects_index++] = obj; diff --git a/vm_backtrace.c b/vm_backtrace.c index 44a4ac0784..b04f6dac31 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -526,7 +526,7 @@ bt_init(void *ptr, size_t size) struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; arg->btobj = backtrace_alloc(rb_cBacktrace); GetCoreDataFromValue(arg->btobj, rb_backtrace_t, arg->bt); - arg->bt->backtrace_base = arg->bt->backtrace = ruby_xmalloc(sizeof(rb_backtrace_location_t) * size); + arg->bt->backtrace_base = arg->bt->backtrace = ALLOC_N(rb_backtrace_location_t, size); arg->bt->backtrace_size = 0; }