diff --git a/src/load.c b/src/load.c index 263c958db..bb22a6130 100644 --- a/src/load.c +++ b/src/load.c @@ -59,7 +59,7 @@ str_to_double(mrb_state *mrb, const char *p) #endif static mrb_bool -read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp) +read_irep_record_1(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_t *len, uint8_t flags, mrb_irep **irepp) { int i; const uint8_t *src = bin; @@ -102,6 +102,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { return FALSE; } + if (src + data_len > end) return FALSE; if ((flags & FLAG_SRC_MALLOC) == 0) { irep->iseq = (mrb_code*)src; irep->flags |= MRB_ISEQ_NO_FREE; @@ -117,6 +118,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag /* POOL BLOCK */ plen = bin_to_uint16(src); /* number of pool */ src += sizeof(uint16_t); + if (src > end) return FALSE; if (plen > 0) { if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { return FALSE; @@ -130,6 +132,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag switch (tt) { /* pool data */ case IREP_TT_INT32: { + if (src + sizeof(uint32_t) > end) return FALSE; mrb_int v = (int32_t)bin_to_uint32(src); src += sizeof(uint32_t); #ifdef MRB_64BIT @@ -144,6 +147,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag case IREP_TT_INT64: #ifdef MRB_INT64 { + if (src + sizeof(uint32_t)*2 > end) return FALSE; uint64_t i64 = bin_to_uint32(src); src += sizeof(uint32_t); i64 <<= 32; @@ -160,6 +164,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag case IREP_TT_BIGINT: pool_data_len = bin_to_uint8(src); /* pool data length */ src += sizeof(uint8_t); + if (src + pool_data_len > end) return FALSE; { char *p; pool[i].tt = IREP_TT_BIGINT; @@ -172,6 +177,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag case IREP_TT_FLOAT: #ifndef MRB_NO_FLOAT + if (src + sizeof(double) > end) return FALSE; pool[i].tt = tt; pool[i].u.f = str_to_double(mrb, (const char*)src); src += sizeof(double); @@ -183,6 +189,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag case IREP_TT_STR: pool_data_len = bin_to_uint16(src); /* pool data length */ src += sizeof(uint16_t); + if (src + pool_data_len > end) return FALSE; if (st) { pool[i].tt = (pool_data_len<<2) | IREP_TT_SSTR; pool[i].u.str = (const char*)src; @@ -208,6 +215,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag /* SYMS BLOCK */ irep->slen = bin_to_uint16(src); /* syms length */ src += sizeof(uint16_t); + if (src > end) return FALSE; if (irep->slen > 0) { if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { return FALSE; @@ -223,6 +231,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag continue; } + if (src + snl > end) return FALSE; if (flags & FLAG_SRC_MALLOC) { syms[i] = mrb_intern(mrb, (char *)src, snl); } @@ -242,10 +251,10 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag } static mrb_bool -read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp) +read_irep_record(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_t *len, uint8_t flags, mrb_irep **irepp) { int ai = mrb_gc_arena_save(mrb); - mrb_bool readsuccess = read_irep_record_1(mrb, bin, len, flags, irepp); + mrb_bool readsuccess = read_irep_record_1(mrb, bin, end, len, flags, irepp); mrb_irep **reps; int i; @@ -261,7 +270,7 @@ read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, for (i=0; i<(*irepp)->rlen; i++) { size_t rlen; - readsuccess = read_irep_record(mrb, bin, &rlen, flags, &reps[i]); + readsuccess = read_irep_record(mrb, bin, end, &rlen, flags, &reps[i]); mrb_gc_arena_restore(mrb, ai); if (!readsuccess) { return FALSE; @@ -274,10 +283,9 @@ read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, } static mrb_irep* -read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags, struct RProc **proc) +read_section_irep(mrb_state *mrb, const uint8_t *bin, size_t size, uint8_t flags, struct RProc **proc) { - size_t len; - + if (size < sizeof(struct rite_section_irep_header)) return NULL; /* * This proc object keeps all the data in progress to avoid memory leaks * if something goes wrong while reading irep. @@ -285,8 +293,9 @@ read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags, struct RPro *proc = mrb_proc_new(mrb, NULL); mrb_irep **irepp = (mrb_irep**)&(*proc)->body.irep; + size_t len; bin += sizeof(struct rite_section_irep_header); - if (read_irep_record(mrb, bin, &len, flags, irepp)) { + if (read_irep_record(mrb, bin, bin+size, &len, flags, irepp)) { return *irepp; } else { @@ -295,7 +304,7 @@ read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags, struct RPro } static int -read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len) +read_debug_record(mrb_state *mrb, const uint8_t *start, const uint8_t *end, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len) { const uint8_t *bin = start; ptrdiff_t diff; @@ -313,13 +322,15 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * bin += sizeof(uint32_t); debug->flen = bin_to_uint16(bin); - debug->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*)); bin += sizeof(uint16_t); + if (bin > end) return MRB_DUMP_GENERAL_FAILURE; + debug->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*)); for (f_idx = 0; f_idx < debug->flen; ++f_idx) { mrb_irep_debug_info_file *file; uint16_t filename_idx; + if (bin > end) return MRB_DUMP_GENERAL_FAILURE; file = (mrb_irep_debug_info_file *)mrb_calloc(mrb, 1, sizeof(*file)); debug->files[f_idx] = file; @@ -338,9 +349,10 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * bin += sizeof(uint8_t); switch (file->line_type) { case mrb_debug_line_ary: { - uint32_t l; + size_t l = sizeof(uint16_t) * (size_t)file->line_entry_count; - file->lines.ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * (size_t)(file->line_entry_count)); + if (bin + l > end) return MRB_DUMP_GENERAL_FAILURE; + file->lines.ary = (uint16_t *)mrb_malloc(mrb, l); for (l = 0; l < file->line_entry_count; ++l) { file->lines.ary[l] = bin_to_uint16(bin); bin += sizeof(uint16_t); @@ -348,11 +360,12 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * } break; case mrb_debug_line_flat_map: { - uint32_t l; + size_t c = (size_t)file->line_entry_count; + size_t n = sizeof(mrb_irep_debug_info_line); - file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_calloc( - mrb, (size_t)(file->line_entry_count), sizeof(mrb_irep_debug_info_line)); - for (l = 0; l < file->line_entry_count; ++l) { + if (bin + c*n > end) return MRB_DUMP_GENERAL_FAILURE; + file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_calloc(mrb, c, n); + for (size_t l = 0; l < file->line_entry_count; ++l) { file->lines.flat_map[l].start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); file->lines.flat_map[l].line = bin_to_uint16(bin); @@ -361,7 +374,10 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * } break; case mrb_debug_line_packed_map: { - file->lines.packed_map = (uint8_t*)mrb_calloc(mrb, 1, (size_t)file->line_entry_count); + size_t l = (size_t)file->line_entry_count; + + if (bin + l > end) return MRB_DUMP_GENERAL_FAILURE; + file->lines.packed_map = (uint8_t*)mrb_calloc(mrb, 1, l); memcpy(file->lines.packed_map, bin, file->line_entry_count); bin += file->line_entry_count; } break; @@ -381,7 +397,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * size_t len; int ret; - ret = read_debug_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, filenames, filenames_len); + ret = read_debug_record(mrb, bin, end, (mrb_irep*)irep->reps[i], &len, filenames, filenames_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } @@ -394,9 +410,10 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * } static int -read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) +read_section_debug(mrb_state *mrb, const uint8_t *start, size_t size, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; + const uint8_t *end = start + size; ptrdiff_t diff; struct rite_section_debug_header *header; uint16_t i; @@ -412,11 +429,16 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t filenames_len = bin_to_uint16(bin); bin += sizeof(uint16_t); + if (bin > end) return MRB_DUMP_GENERAL_FAILURE; filenames_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)filenames_len); filenames = (mrb_sym*)RSTRING_PTR(filenames_obj); for (i = 0; i < filenames_len; ++i) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); + if (bin + f_len > end) { + result = MRB_DUMP_GENERAL_FAILURE; + goto debug_exit; + } if (flags & FLAG_SRC_MALLOC) { filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len); } @@ -426,7 +448,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t bin += f_len; } - result = read_debug_record(mrb, bin, irep, &len, filenames, filenames_len); + result = read_debug_record(mrb, bin, end, irep, &len, filenames, filenames_len); if (result != MRB_DUMP_OK) goto debug_exit; bin += len; @@ -449,6 +471,7 @@ read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *rec ptrdiff_t diff; int i; + if (irep->nlocals == 0) return MRB_DUMP_GENERAL_FAILURE; irep->lv = lv = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (irep->nlocals - 1)); for (i = 0; i + 1 < irep->nlocals; ++i) { @@ -482,9 +505,10 @@ read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *rec } static int -read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) +read_section_lv(mrb_state *mrb, const uint8_t *start, size_t size, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; + const uint8_t *end = start + size; ptrdiff_t diff; struct rite_section_lv_header const *header; uint32_t i; @@ -502,12 +526,13 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl syms_len = bin_to_uint32(bin); bin += sizeof(uint32_t); + if (bin > end) return MRB_DUMP_READ_FAULT; syms_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)syms_len); syms = (mrb_sym*)RSTRING_PTR(syms_obj); for (i = 0; i < syms_len; ++i) { uint16_t const str_len = bin_to_uint16(bin); bin += sizeof(uint16_t); - + if (bin > end) return MRB_DUMP_READ_FAULT; syms[i] = intern_func(mrb, (const char*)bin, str_len); bin += str_len; } @@ -580,20 +605,22 @@ read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags) bin_size -= sizeof(struct rite_binary_header); while (bin_size > sizeof(struct rite_section_header)) { section_header = (const struct rite_section_header *)bin; + uint32_t section_size = bin_to_uint32(section_header->section_size); + if (bin_size < section_size) return NULL; if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { - irep = read_section_irep(mrb, bin, flags, &proc); + irep = read_section_irep(mrb, bin, bin_size, flags, &proc); if (!irep) return NULL; } else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ - result = read_section_debug(mrb, bin, irep, flags); + result = read_section_debug(mrb, bin, bin_size, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; - result = read_section_lv(mrb, bin, irep, flags); + result = read_section_lv(mrb, bin, bin_size, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } @@ -602,7 +629,6 @@ read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags) break; } - uint32_t section_size = bin_to_uint32(section_header->section_size); bin += section_size; bin_size -= section_size; } @@ -615,7 +641,7 @@ mrb_proc_read_irep(mrb_state *mrb, const uint8_t *bin) { uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC; - return read_irep(mrb, bin, (size_t)-1, flags); + return read_irep(mrb, bin, (size_t)UINT32_MAX, flags); } DEFINE_READ_IREP_FUNC(