summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile.c46
-rw-r--r--dir.c14
-rw-r--r--encoding.c2
-rw-r--r--gc.c217
-rw-r--r--internal.h9
-rw-r--r--iseq.c7
-rw-r--r--iseq.h6
-rw-r--r--node.c11
-rw-r--r--string.c2
-rw-r--r--thread.c4
-rw-r--r--transient_heap.c11
-rw-r--r--vm_backtrace.c2
12 files changed, 282 insertions, 49 deletions
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; 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
}
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)
#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 <intrin.h>
+#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;
}