From fea88e64fd1ae64b24c9b6cedb3ae872fadb7273 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Wed, 8 Nov 2017 00:58:15 +0200 Subject: [PATCH] Further cleanup for memory helpers * Convert memory helpers like duk_memcmp() to macros where possible: for some reason automatic inlining with -Os doesn't do the right thing and the footprint impact is over 1kB. The duk_memcmp() helper is an inlined function because it returns a value and assertions would otherwise lead to multiple evaluation of arguments. * Differentiate between C99+ and "unsafe" call sites. For example, use duk_memcpy() when the call site obeys C99+ restrictions, and duk_memcpy_unsafe() when either pointer may be NULL when the size is zero. Same for all helpers. --- src-input/duk_api_bytecode.c | 9 +- src-input/duk_api_stack.c | 12 ++- src-input/duk_bi_buffer.c | 46 ++++----- src-input/duk_bi_string.c | 6 +- src-input/duk_bi_symbol.c | 3 +- src-input/duk_debug_fixedbuffer.c | 2 +- src-input/duk_debug_vsnprintf.c | 1 + src-input/duk_debugger.c | 1 + src-input/duk_error.h | 2 +- src-input/duk_hbufobj.h | 3 +- src-input/duk_heap_memory.c | 1 - src-input/duk_heap_stringtable.c | 5 +- src-input/duk_hobject_props.c | 6 +- src-input/duk_js_ops.c | 8 +- src-input/duk_numconv.c | 1 + src-input/duk_util.h | 149 ++++++++++++++++++++++++++---- src-input/duk_util_bufwriter.c | 47 ++++++---- src-input/duk_util_memory.c | 71 ++++---------- 18 files changed, 239 insertions(+), 134 deletions(-) diff --git a/src-input/duk_api_bytecode.c b/src-input/duk_api_bytecode.c index b56b176b..b2e0e7e3 100644 --- a/src-input/duk_api_bytecode.c +++ b/src-input/duk_api_bytecode.c @@ -74,9 +74,10 @@ DUK_LOCAL duk_uint8_t *duk__dump_hbuffer_raw(duk_hthread *thr, duk_uint8_t *p, d DUK_ASSERT(len <= 0xffffffffUL); /* buffer limits */ tmp32 = (duk_uint32_t) len; DUK_RAW_WRITE_U32_BE(p, tmp32); - duk_memcpy((void *) p, - (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h), - len); + /* When len == 0, buffer data pointer may be NULL. */ + duk_memcpy_unsafe((void *) p, + (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h), + len); p += len; return p; } @@ -288,7 +289,7 @@ static duk_uint8_t *duk__dump_func(duk_hthread *thr, duk_hcompfunc *func, duk_bu ins_end = DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func); DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr); #if defined(DUK_USE_INTEGER_BE) - duk_memcpy((void *) p, (const void *) ins, (size_t) (ins_end - ins)); + duk_memcpy_unsafe((void *) p, (const void *) ins, (size_t) (ins_end - ins)); p += (size_t) (ins_end - ins); #else while (ins != ins_end) { diff --git a/src-input/duk_api_stack.c b/src-input/duk_api_stack.c index 9f1f050d..d985a86f 100644 --- a/src-input/duk_api_stack.c +++ b/src-input/duk_api_stack.c @@ -1392,8 +1392,8 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_hthread *to_thr, duk_hthread *from_thr, DUK_WO_NORETURN(return;); } - /* copy values (no overlap even if to_thr == from_thr; that's not - * allowed now anyway) + /* Copy values (no overlap even if to_thr == from_thr; that's not + * allowed now anyway). */ DUK_ASSERT(nbytes > 0); duk_memcpy((void *) to_thr->valstack_top, (const void *) src, (size_t) nbytes); @@ -3417,7 +3417,8 @@ DUK_EXTERNAL void *duk_to_buffer_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t } dst_data = (duk_uint8_t *) duk_push_buffer(thr, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/); - duk_memcpy((void *) dst_data, (const void *) src_data, (size_t) src_size); + /* dst_data may be NULL if size is zero. */ + duk_memcpy_unsafe((void *) dst_data, (const void *) src_data, (size_t) src_size); duk_replace(thr, idx); skip_copy: @@ -5293,6 +5294,7 @@ DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len) DUK_ASSERT_API_ENTRY(thr); ptr = duk_push_buffer_raw(thr, len, 0); + DUK_ASSERT(ptr != NULL); #if !defined(DUK_USE_ZERO_BUFFER_DATA) /* ES2015 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA * is not set. @@ -5952,7 +5954,7 @@ DUK_INTERNAL void duk_pack(duk_hthread *thr, duk_idx_t count) { * any refcount updates: net refcount changes are zero. */ tv_src = thr->valstack_top - count - 1; - duk_memcpy((void *) tv_dst, (const void *) tv_src, (size_t) count * sizeof(duk_tval)); + duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, (size_t) count * sizeof(duk_tval)); /* Overwrite result array to final value stack location and wipe * the rest; no refcount operations needed. @@ -6614,7 +6616,7 @@ DUK_INTERNAL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_ DUK_UNREF(thr); DUK_ASSERT(count * sizeof(duk_tval) >= count); /* no wrap */ - duk_memcpy((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval)); + duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval)); tv = tv_dst; while (count-- > 0) { diff --git a/src-input/duk_bi_buffer.c b/src-input/duk_bi_buffer.c index 1f1d490a..1ffd99d3 100644 --- a/src-input/duk_bi_buffer.c +++ b/src-input/duk_bi_buffer.c @@ -443,6 +443,7 @@ DUK_INTERNAL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_h DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) { duk_double_union du; + DUK_ASSERT(elem_size > 0); duk_memcpy((void *) du.uc, (const void *) p, (size_t) elem_size); switch (h_bufobj->elem_type) { @@ -524,6 +525,7 @@ DUK_INTERNAL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_b DUK_UNREACHABLE(); } + DUK_ASSERT(elem_size > 0); duk_memcpy((void *) p, (const void *) du.uc, (size_t) elem_size); } @@ -953,7 +955,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) { DUK_DDD(DUK_DDDPRINT("using memcpy: p_src=%p, p_dst=%p, byte_length=%ld", (void *) p_src, (void *) p_dst, (long) byte_length)); - duk_memcpy((void *) p_dst, (const void *) p_src, (size_t) byte_length); + duk_memcpy_unsafe((void *) p_dst, (const void *) p_src, (size_t) byte_length); break; } case 1: { @@ -1220,9 +1222,9 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_hthread *thr) { */ DUK_ASSERT(DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)); - duk_memcpy((void *) buf_slice, - (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), - (size_t) slice_length); + duk_memcpy_unsafe((void *) buf_slice, + (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), + (size_t) slice_length); /* Use the equivalent of: new TextEncoder().encode(this) to convert the * string. Result will be valid UTF-8; non-CESU-8 inputs are currently @@ -1384,7 +1386,7 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_hthread *thr) { /* Handle single character fills as memset() even when * the fill data comes from a one-char argument. */ - duk_memset((void *) p, (int) fill_str_ptr[0], (size_t) fill_length); + duk_memset_unsafe((void *) p, (int) fill_str_ptr[0], (size_t) fill_length); } else if (fill_str_len > 1) { duk_size_t i, n, t; @@ -1435,9 +1437,9 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_hthread *thr) { if (DUK_HBUFOBJ_VALID_SLICE(h_this)) { /* Cannot overlap. */ - duk_memcpy((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset), - (const void *) str_data, - (size_t) length); + duk_memcpy_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset), + (const void *) str_data, + (size_t) length); } else { DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore")); } @@ -1533,9 +1535,9 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_hthread *thr) { /* Must use memmove() because copy area may overlap (source and target * buffer may be the same, or from different slices. */ - duk_memmove((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart), - (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + source_ustart), - (size_t) copy_size); + duk_memmove_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart), + (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + source_ustart), + (size_t) copy_size); } else { DUK_DDD(DUK_DDDPRINT("buffer copy not covered by underlying buffer(s), ignoring")); } @@ -1721,7 +1723,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) { DUK_ASSERT(src_length == dst_length); DUK_DDD(DUK_DDDPRINT("fast path: able to use memmove() because views are compatible")); - duk_memmove((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length); + duk_memmove_unsafe((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length); return 0; } DUK_DDD(DUK_DDDPRINT("fast path: views are not compatible with a byte copy, copy by item")); @@ -1764,7 +1766,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) { DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source")); p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_length); DUK_ASSERT(p_src_copy != NULL); - duk_memcpy((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length); + duk_memcpy_unsafe((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length); p_src_base = p_src_copy; /* use p_src_base from now on */ } @@ -1891,9 +1893,9 @@ DUK_LOCAL void duk__arraybuffer_plain_slice(duk_hthread *thr, duk_hbuffer *h_val DUK_ASSERT(p_copy != NULL); copy_length = slice_length; - duk_memcpy((void *) p_copy, - (const void *) ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_val) + start_offset), - copy_length); + duk_memcpy_unsafe((void *) p_copy, + (const void *) ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_val) + start_offset), + copy_length); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ @@ -2015,9 +2017,9 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_hthread *thr) { * is left as zero. */ copy_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, slice_length); - duk_memcpy((void *) p_copy, - (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), - copy_length); + duk_memcpy_unsafe((void *) p_copy, + (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), + copy_length); h_val = duk_known_hbuffer(thr, -1); @@ -2209,9 +2211,9 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_hthread *thr) { if (h_bufobj->buf != NULL && DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { - duk_memcpy((void *) p, - (const void *) DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj), - copy_size); + duk_memcpy_unsafe((void *) p, + (const void *) DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj), + copy_size); } else { /* Just skip, leaving zeroes in the result. */ ; diff --git a/src-input/duk_bi_string.c b/src-input/duk_bi_string.c index 473357bf..b746da2d 100644 --- a/src-input/duk_bi_string.c +++ b/src-input/duk_bi_string.c @@ -1378,12 +1378,14 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_hthread *thr) { /* Temporary fixed buffer, later converted to string. */ buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, result_len); + DUK_ASSERT(buf != NULL); src = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); + DUK_ASSERT(src != NULL); #if defined(DUK_USE_PREFER_SIZE) p = buf; while (count-- > 0) { - duk_memcpy((void *) p, (const void *) src, input_blen); /* copy size may be zero */ + duk_memcpy((void *) p, (const void *) src, input_blen); /* copy size may be zero, but pointers are valid */ p += input_blen; } #else /* DUK_USE_PREFER_SIZE */ @@ -1400,7 +1402,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_hthread *thr) { (long) result_len)); if (remain <= copy_size) { /* If result_len is zero, this case is taken and does - * a zero size copy. + * a zero size copy (with valid pointers). */ duk_memcpy((void *) p, (const void *) src, remain); break; diff --git a/src-input/duk_bi_symbol.c b/src-input/duk_bi_symbol.c index 49c328d0..f9fcddb9 100644 --- a/src-input/duk_bi_symbol.c +++ b/src-input/duk_bi_symbol.c @@ -37,9 +37,10 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) { * +1 0xff after unique suffix for symbols with undefined description */ buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, 1 + len + 1 + 17 + 1); + DUK_ASSERT(buf != NULL); p = buf + 1; DUK_ASSERT(desc != NULL || len == 0); /* may be NULL if len is 0 */ - duk_memcpy((void *) p, (const void *) desc, len); + duk_memcpy_unsafe((void *) p, (const void *) desc, len); p += len; if (magic == 0) { /* Symbol(): create unique symbol. Use two 32-bit values diff --git a/src-input/duk_debug_fixedbuffer.c b/src-input/duk_debug_fixedbuffer.c index 70ef15af..96b0dabf 100644 --- a/src-input/duk_debug_fixedbuffer.c +++ b/src-input/duk_debug_fixedbuffer.c @@ -18,7 +18,7 @@ DUK_INTERNAL void duk_fb_put_bytes(duk_fixedbuffer *fb, const duk_uint8_t *buffe } else { copylen = length; } - duk_memcpy(fb->buffer + fb->offset, buffer, copylen); + duk_memcpy_unsafe(fb->buffer + fb->offset, buffer, copylen); fb->offset += copylen; } diff --git a/src-input/duk_debug_vsnprintf.c b/src-input/duk_debug_vsnprintf.c index efbd767e..24001dbe 100644 --- a/src-input/duk_debug_vsnprintf.c +++ b/src-input/duk_debug_vsnprintf.c @@ -1019,6 +1019,7 @@ DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_u duk_uint8_t *p = (duk_uint8_t *) buf; duk_uint8_t *p_end = (duk_uint8_t *) (buf + buf_size - 1); + DUK_ASSERT(buf != NULL); duk_memzero(buf, buf_size); for (i = 0; i < fptr_size; i++) { diff --git a/src-input/duk_debugger.c b/src-input/duk_debugger.c index 6873142f..0125f3f3 100644 --- a/src-input/duk_debugger.c +++ b/src-input/duk_debugger.c @@ -309,6 +309,7 @@ DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_ DUK_ASSERT(thr != NULL); heap = thr->heap; DUK_ASSERT(heap != NULL); + DUK_ASSERT(data != NULL); if (heap->dbg_read_cb == NULL) { DUK_D(DUK_DPRINT("attempt to read %ld bytes in detached state, return zero data", (long) length)); diff --git a/src-input/duk_error.h b/src-input/duk_error.h index 568301db..ea5d0133 100644 --- a/src-input/duk_error.h +++ b/src-input/duk_error.h @@ -423,7 +423,7 @@ #if defined(DUK_USE_ASSERTIONS) #define DUK_ASSERT_SET_GARBAGE(ptr,size) do { \ - duk_memset((void *) (ptr), 0x5a, size); \ + duk_memset_unsafe((void *) (ptr), 0x5a, size); \ } while (0) #else #define DUK_ASSERT_SET_GARBAGE(ptr,size) do {} while (0) diff --git a/src-input/duk_hbufobj.h b/src-input/duk_hbufobj.h index ee66af67..b5c85240 100644 --- a/src-input/duk_hbufobj.h +++ b/src-input/duk_hbufobj.h @@ -47,7 +47,8 @@ } while (0) /* Get the current data pointer (caller must ensure buf != NULL) as a - * duk_uint8_t ptr. + * duk_uint8_t ptr. Note that the result may be NULL if the underlying + * buffer has zero size and is not a fixed buffer. */ #define DUK_HBUFOBJ_GET_SLICE_BASE(heap,h) \ (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \ diff --git a/src-input/duk_heap_memory.c b/src-input/duk_heap_memory.c index 5a537b5a..efb7b393 100644 --- a/src-input/duk_heap_memory.c +++ b/src-input/duk_heap_memory.c @@ -127,7 +127,6 @@ DUK_INTERNAL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size) { res = DUK_ALLOC(heap, size); if (DUK_LIKELY(res != NULL)) { - /* assume memset with zero size is OK */ duk_memzero(res, size); } return res; diff --git a/src-input/duk_heap_stringtable.c b/src-input/duk_heap_stringtable.c index ee785f68..fba53307 100644 --- a/src-input/duk_heap_stringtable.c +++ b/src-input/duk_heap_stringtable.c @@ -690,7 +690,7 @@ DUK_LOCAL duk_hstring *duk__strtab_romstring_lookup(duk_heap *heap, const duk_ui while (curr != NULL) { if (strhash == DUK_HSTRING_GET_HASH(curr) && blen == DUK_HSTRING_GET_BYTELEN(curr) && - duk_memcmp((const void *) str, (const void *) DUK_HSTRING_GET_DATA(curr), blen) == 0) { + duk_memcmp_unsafe((const void *) str, (const void *) DUK_HSTRING_GET_DATA(curr), blen) == 0) { DUK_DDD(DUK_DDDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx", curr, (unsigned long) strhash, (unsigned long) DUK_HSTRING_GET_HASH(curr))); return curr; @@ -710,6 +710,7 @@ DUK_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uin /* Preliminaries. */ + /* XXX: maybe just require 'str != NULL' even for zero size? */ DUK_ASSERT(heap != NULL); DUK_ASSERT(blen == 0 || str != NULL); DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN); /* Caller is responsible for ensuring this. */ @@ -728,7 +729,7 @@ DUK_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uin while (h != NULL) { if (DUK_HSTRING_GET_HASH(h) == strhash && DUK_HSTRING_GET_BYTELEN(h) == blen && - duk_memcmp((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { + duk_memcmp_unsafe((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { /* Found existing entry. */ DUK_STATS_INC(heap, stats_strtab_intern_hit); return h; diff --git a/src-input/duk_hobject_props.c b/src-input/duk_hobject_props.c index fa274480..2883623a 100644 --- a/src-input/duk_hobject_props.c +++ b/src-input/duk_hobject_props.c @@ -811,9 +811,9 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, DUK_ASSERT(new_a != NULL || array_copy_size == 0U); DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL || array_copy_size == 0U); DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) > 0 || array_copy_size == 0U); - duk_memcpy((void *) new_a, - (const void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj), - array_copy_size); + duk_memcpy_unsafe((void *) new_a, + (const void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj), + array_copy_size); for (i = DUK_HOBJECT_GET_ASIZE(obj); i < new_a_size; i++) { duk_tval *tv = &new_a[i]; diff --git a/src-input/duk_js_ops.c b/src-input/duk_js_ops.c index 0f76768f..833b394e 100644 --- a/src-input/duk_js_ops.c +++ b/src-input/duk_js_ops.c @@ -727,11 +727,11 @@ DUK_INTERNAL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, const prefix_len = (len1 <= len2 ? len1 : len2); /* duk_memcmp() is guaranteed to return zero (equal) for zero length - * inputs so no zero length check is needed. + * inputs. */ - rc = duk_memcmp((const void *) buf1, - (const void *) buf2, - (size_t) prefix_len); + rc = duk_memcmp_unsafe((const void *) buf1, + (const void *) buf2, + (size_t) prefix_len); if (rc < 0) { return -1; diff --git a/src-input/duk_numconv.c b/src-input/duk_numconv.c index 9d631f3c..214872b1 100644 --- a/src-input/duk_numconv.c +++ b/src-input/duk_numconv.c @@ -674,6 +674,7 @@ DUK_LOCAL duk_size_t duk__dragon4_format_uint32(duk_uint8_t *buf, duk_uint32_t x duk_small_int_t dig; duk_uint32_t t; + DUK_ASSERT(buf != NULL); DUK_ASSERT(radix >= 2 && radix <= 36); /* A 32-bit unsigned integer formats to at most 32 digits (the diff --git a/src-input/duk_util.h b/src-input/duk_util.h index d76c5d9c..ad83cac0 100644 --- a/src-input/duk_util.h +++ b/src-input/duk_util.h @@ -315,12 +315,13 @@ struct duk_bufwriter_ctx { (bw_ctx)->p += duk__enc_len; \ } while (0) /* XXX: add temporary duk__p pointer here too; sharing */ +/* XXX: avoid unsafe variants */ #define DUK_BW_WRITE_RAW_BYTES(thr,bw_ctx,valptr,valsz) do { \ const void *duk__valptr; \ duk_size_t duk__valsz; \ duk__valptr = (const void *) (valptr); \ duk__valsz = (duk_size_t) (valsz); \ - duk_memcpy((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ (bw_ctx)->p += duk__valsz; \ } while (0) #define DUK_BW_WRITE_RAW_CSTRING(thr,bw_ctx,val) do { \ @@ -328,31 +329,31 @@ struct duk_bufwriter_ctx { duk_size_t duk__val_len; \ duk__val = (const duk_uint8_t *) (val); \ duk__val_len = DUK_STRLEN((const char *) duk__val); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HSTRING(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HBUFFER(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HBUFFER_FIXED(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HBUFFER_DYNAMIC(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) @@ -416,13 +417,14 @@ struct duk_bufwriter_ctx { DUK_BW_WRITE_RAW_CESU8((thr), (bw_ctx), (cp)); \ } while (0) /* XXX: add temporary duk__p pointer here too; sharing */ +/* XXX: avoid unsafe */ #define DUK_BW_WRITE_ENSURE_BYTES(thr,bw_ctx,valptr,valsz) do { \ const void *duk__valptr; \ duk_size_t duk__valsz; \ duk__valptr = (const void *) (valptr); \ duk__valsz = (duk_size_t) (valsz); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__valsz); \ - duk_memcpy((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ (bw_ctx)->p += duk__valsz; \ } while (0) #define DUK_BW_WRITE_ENSURE_CSTRING(thr,bw_ctx,val) do { \ @@ -431,35 +433,35 @@ struct duk_bufwriter_ctx { duk__val = (const duk_uint8_t *) (val); \ duk__val_len = DUK_STRLEN((const char *) duk__val); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HSTRING(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HBUFFER(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HBUFFER_FIXED(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HBUFFER_DYNAMIC(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - duk_memcpy((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) @@ -542,11 +544,126 @@ DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t val DUK_INTERNAL_DECL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len); #endif -DUK_INTERNAL_DECL void duk_memcpy(void *dst, const void *src, duk_size_t len); -DUK_INTERNAL_DECL void duk_memmove(void *dst, const void *src, duk_size_t len); +/* memcpy(), memmove() etc wrappers. The plain variants like duk_memcpy() + * assume C99+ and 'src' and 'dst' pointers must be non-NULL even when the + * operation size is zero. The unsafe variants like duk_memcpy_safe() deal + * with the zero size case explicitly, and allow NULL pointers in that case + * (which is undefined behavior in C99+). For the majority of actual targets + * a NULL pointer with a zero length is fine in practice. These wrappers are + * macros to force inlining; because there are hundreds of call sites, even a + * few extra bytes per call site adds up to ~1kB footprint. + */ +#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) +#define duk_memcpy(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memcpy_unsafe(dst,src,len) duk_memcpy((dst), (src), (len)) +#define duk_memmove(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memmove_unsafe(dst,src,len) duk_memmove((dst), (src), (len)) +#define duk_memset(dst,val,len) do { \ + void *duk__dst = (dst); \ + duk_small_int_t duk__val = (val); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ + } while (0) +#define duk_memset_unsafe(dst,val,len) duk_memset((dst), (val), (len)) +#define duk_memzero(dst,len) do { \ + void *duk__dst = (dst); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ + } while (0) +#define duk_memzero_unsafe(dst,len) duk_memzero((dst), (len)) +#else /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ +#define duk_memcpy(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memcpy_unsafe(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ + } \ + } while (0) +#define duk_memmove(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memmove_unsafe(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ + } \ + } while (0) +#define duk_memset(dst,val,len) do { \ + void *duk__dst = (dst); \ + duk_small_int_t duk__val = (val); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ + } while (0) +#define duk_memset_unsafe(dst,val,len) do { \ + void *duk__dst = (dst); \ + duk_small_int_t duk__val = (val); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ + } \ + } while (0) +#define duk_memzero(dst,len) do { \ + void *duk__dst = (dst); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ + } while (0) +#define duk_memzero_unsafe(dst,len) do { \ + void *duk__dst = (dst); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ + } \ + } while (0) +#endif /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ + DUK_INTERNAL_DECL duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len); -DUK_INTERNAL_DECL void duk_memset(void *s, duk_small_int_t c, duk_size_t len); -DUK_INTERNAL_DECL void duk_memzero(void *s, duk_size_t len); +DUK_INTERNAL_DECL duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len); DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival); DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival); diff --git a/src-input/duk_util_bufwriter.c b/src-input/duk_util_bufwriter.c index d7767009..b82b7eb1 100644 --- a/src-input/duk_util_bufwriter.c +++ b/src-input/duk_util_bufwriter.c @@ -4,6 +4,10 @@ #include "duk_internal.h" +/* XXX: Avoid duk_{memcmp,memmove}_unsafe() by imposing a minimum length of + * >0 for the underlying dynamic buffer. + */ + /* * Macro support functions (use only macros in calling code) */ @@ -15,6 +19,9 @@ DUK_LOCAL void duk__bw_update_ptrs(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, DUK_ASSERT(bw_ctx != NULL); DUK_UNREF(thr); + /* 'p' might be NULL when the underlying buffer is zero size. If so, + * the resulting pointers are not used unsafely. + */ p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, bw_ctx->buf); DUK_ASSERT(p != NULL || (DUK_HBUFFER_DYNAMIC_GET_SIZE(bw_ctx->buf) == 0 && curr_offset == 0 && new_length == 0)); bw_ctx->p = p + curr_offset; @@ -23,7 +30,6 @@ DUK_LOCAL void duk__bw_update_ptrs(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, } DUK_INTERNAL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf) { - DUK_ASSERT(thr != NULL); DUK_ASSERT(bw_ctx != NULL); DUK_ASSERT(h_buf != NULL); @@ -38,6 +44,7 @@ DUK_INTERNAL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ct (void) duk_push_dynamic_buffer(thr, buf_size); bw_ctx->buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1); + DUK_ASSERT(bw_ctx->buf != NULL); duk__bw_update_ptrs(thr, bw_ctx, 0, buf_size); } @@ -105,9 +112,9 @@ DUK_INTERNAL void duk_bw_write_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw DUK_UNREF(thr); p_base = bw->p_base; - duk_memcpy((void *) bw->p, - (const void *) (p_base + src_off), - (size_t) len); + duk_memcpy_unsafe((void *) bw->p, + (const void *) (p_base + src_off), + (size_t) len); bw->p += len; } @@ -137,12 +144,12 @@ DUK_INTERNAL void duk_bw_insert_raw_bytes(duk_hthread *thr, duk_bufwriter_ctx *b move_sz = buf_sz - dst_off; DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */ - duk_memmove((void *) (p_base + dst_off + len), - (const void *) (p_base + dst_off), - (size_t) move_sz); - duk_memcpy((void *) (p_base + dst_off), - (const void *) buf, - (size_t) len); + duk_memmove_unsafe((void *) (p_base + dst_off + len), + (const void *) (p_base + dst_off), + (size_t) move_sz); + duk_memcpy_unsafe((void *) (p_base + dst_off), + (const void *) buf, + (size_t) len); bw->p += len; } @@ -184,12 +191,12 @@ DUK_INTERNAL void duk_bw_insert_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *b move_sz = buf_sz - dst_off; DUK_ASSERT(p_base != NULL); /* buffer size is >= 1 */ - duk_memmove((void *) (p_base + dst_off + len), - (const void *) (p_base + dst_off), - (size_t) move_sz); - duk_memcpy((void *) (p_base + dst_off), - (const void *) (p_base + src_off), - (size_t) len); + duk_memmove_unsafe((void *) (p_base + dst_off + len), + (const void *) (p_base + dst_off), + (size_t) move_sz); + duk_memcpy_unsafe((void *) (p_base + dst_off), + (const void *) (p_base + src_off), + (size_t) len); bw->p += len; } @@ -222,7 +229,7 @@ DUK_INTERNAL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter move_sz = buf_sz - off; p_dst = p_base + off + len; p_src = p_base + off; - duk_memmove((void *) p_dst, (const void *) p_src, (size_t) move_sz); + duk_memmove_unsafe((void *) p_dst, (const void *) p_src, (size_t) move_sz); return p_src; /* point to start of 'reserved area' */ } @@ -253,9 +260,9 @@ DUK_INTERNAL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *b p_dst = p_base + off; p_src = p_dst + len; move_sz = (duk_size_t) (bw->p - p_src); - duk_memmove((void *) p_dst, - (const void *) p_src, - (size_t) move_sz); + duk_memmove_unsafe((void *) p_dst, + (const void *) p_src, + (size_t) move_sz); bw->p -= len; } diff --git a/src-input/duk_util_memory.c b/src-input/duk_util_memory.c index b4c74214..eb3c7042 100644 --- a/src-input/duk_util_memory.c +++ b/src-input/duk_util_memory.c @@ -1,67 +1,36 @@ /* - * Memcpy() etc. + * Memory utils. */ #include "duk_internal.h" -DUK_INTERNAL void duk_memcpy(void *dst, const void *src, duk_size_t len) { -#if !defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - /* For portability reasons both 'src' and 'dst' must be valid and - * non-NULL even if len is zero. Check for that explicitly to avoid - * depending on platform specific behavior. For the vast majority of - * actual targets a NULL pointer with a zero length is fine. - */ - if (DUK_UNLIKELY(len == 0U)) { - return; - } - DUK_ASSERT(src != NULL); - DUK_ASSERT(dst != NULL); -#else - DUK_ASSERT(src != NULL || len == 0U); - DUK_ASSERT(dst != NULL || len == 0U); -#endif - - (void) DUK_MEMCPY(dst, src, (size_t) len); +#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) +DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len) { + DUK_ASSERT(s1 != NULL || len == 0U); + DUK_ASSERT(s2 != NULL || len == 0U); + return DUK_MEMCMP(s1, s2, (size_t) len); } -DUK_INTERNAL void duk_memmove(void *dst, const void *src, duk_size_t len) { -#if !defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) - if (DUK_UNLIKELY(len == 0U)) { - return; - } - DUK_ASSERT(src != NULL); - DUK_ASSERT(dst != NULL); -#else - DUK_ASSERT(src != NULL || len == 0U); - DUK_ASSERT(dst != NULL || len == 0U); -#endif - - (void) DUK_MEMMOVE(dst, src, (size_t) len); +DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) { + DUK_ASSERT(s1 != NULL); + DUK_ASSERT(s2 != NULL); + return DUK_MEMCMP(s1, s2, (size_t) len); } - -DUK_INTERNAL duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) { - duk_small_int_t ret; - -#if !defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) +#else /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ +DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len) { + DUK_ASSERT(s1 != NULL || len == 0U); + DUK_ASSERT(s2 != NULL || len == 0U); if (DUK_UNLIKELY(len == 0U)) { return 0; } DUK_ASSERT(s1 != NULL); DUK_ASSERT(s2 != NULL); -#else - DUK_ASSERT(s1 != NULL || len == 0U); - DUK_ASSERT(s2 != NULL || len == 0U); -#endif - - ret = (duk_small_int_t) DUK_MEMCMP(s1, s2, (size_t) len); - DUK_ASSERT(ret == 0 || len > 0); /* If len == 0, must compare equal. */ - return ret; -} - -DUK_INTERNAL void duk_memset(void *s, duk_small_int_t c, duk_size_t len) { - (void) DUK_MEMSET(s, (int) c, (size_t) len); + return duk_memcmp(s1, s2, len); } -DUK_INTERNAL void duk_memzero(void *s, duk_size_t len) { - (void) DUK_MEMZERO(s, (size_t) len); +DUK_INTERNAL DUK_INLINE duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len) { + DUK_ASSERT(s1 != NULL); + DUK_ASSERT(s2 != NULL); + return DUK_MEMCMP(s1, s2, (size_t) len); } +#endif /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */