diff --git a/.gitignore b/.gitignore index df2ce2a020..7ab31963b0 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ /es5-tests.zip /JS-Interpreter/ /runtests/node_modules/ +/runtests/package-lock.json /d067d2f0ca30.tar.bz2 /595a36b252ee97110724e6fa89fc92c9aa9a206a.zip /regfuzz-0.1.tar.gz @@ -90,3 +91,4 @@ /references/ECMA-262 5.1 edition June 2011.pdf /references/ECMA-262.pdf /tests/octane/octane +/.ccls-cache diff --git a/AUTHORS.rst b/AUTHORS.rst index 031c6a162c..7f4c08d200 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -66,6 +66,7 @@ and agreed to irrevocably license their contributions under the Duktape * Rick Sayre (https://github.com/whorfin) * Craig Leres (https://github.com/leres) * Maurici Abad (https://github.com/mauriciabad) +* Stojan Dimitrovski (https://github.com/hf) Other contributions =================== diff --git a/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml b/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml index 572f3c4eca..b638b0a392 100644 --- a/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml +++ b/config/config-options/DUK_USE_DATE_GET_LOCAL_TZOFFSET.yaml @@ -7,3 +7,6 @@ tags: description: > Mandatory macro for getting the local time offset for a given datetime, see datetime.rst. + + When used in Intel SGX enclaves it must be implemented by the enclave + developer, otherwise it will fail with a compilation error. diff --git a/config/config-options/DUK_USE_DATE_GET_NOW.yaml b/config/config-options/DUK_USE_DATE_GET_NOW.yaml index b89d9b089b..ea1caa04b2 100644 --- a/config/config-options/DUK_USE_DATE_GET_NOW.yaml +++ b/config/config-options/DUK_USE_DATE_GET_NOW.yaml @@ -13,3 +13,6 @@ description: > If the time provided experiences time jumps or doesn't advance in realtime (which is useful in some time virtualization scenarios), consider defining DUK_USE_GET_MONOTONIC_TIME. + + When used in Intel SGX enclaves it must be implemented by the enclave + developer, otherwise it will fail with a compilation error. diff --git a/config/config-options/DUK_USE_MEMBASED_POINTER_ENCODING.yaml b/config/config-options/DUK_USE_MEMBASED_POINTER_ENCODING.yaml new file mode 100644 index 0000000000..34d34d1dcb --- /dev/null +++ b/config/config-options/DUK_USE_MEMBASED_POINTER_ENCODING.yaml @@ -0,0 +1,13 @@ +define: DUK_USE_MEMBASED_POINTER_ENCODING +introduced: 3.0.0 +default: false +tags: + - ecmascript +description: > + All pointers that need to be encoded as string will be encoded in + lowercase hexadecimal encoding in the byte order they are laid out in + memory. Decoding will also work the same way. Using this will also + skip reliance on sscanf for platforms that don't support it. + + When turned off, pointer encoding to string is done via the %p format + specifier in the printf and scanf family of functions. diff --git a/config/config-options/DUK_USE_STANDARDIZED_POINTER_ENCODING.yaml b/config/config-options/DUK_USE_STANDARDIZED_POINTER_ENCODING.yaml new file mode 100644 index 0000000000..ae4a246940 --- /dev/null +++ b/config/config-options/DUK_USE_STANDARDIZED_POINTER_ENCODING.yaml @@ -0,0 +1,13 @@ +define: DUK_USE_MEMBASED_POINTER_ENCODING +introduced: 2.7.0 +default: false +tags: + - ecmascript +description: > + All pointers that need to be encoded as string will be encoded in + lowercase hexadecimal encoding in the byte order they are laid out in + memory. Decoding will also work the same way. Using this will also + skip reliance on sscanf for platforms that don't support it. + + When turned off, pointer encoding to string is done via the %p format + specifier in the printf and scanf family of functions. diff --git a/config/helper-snippets/DUK_F_INTELSGX.h.in b/config/helper-snippets/DUK_F_INTELSGX.h.in new file mode 100644 index 0000000000..0e095f946a --- /dev/null +++ b/config/helper-snippets/DUK_F_INTELSGX.h.in @@ -0,0 +1,9 @@ +/** + * Automatic Intel SGX detection is not possible at compile time. + * Therefore Intel SGX enclave developers wanting to use Duktape + * should either manually specify --platform=intelsgx to configure.py + * or manually define INTELSGX when compiling. + */ +#if defined(INTELSGX) +#define DUK_F_INTELSGX +#endif diff --git a/config/platforms.yaml b/config/platforms.yaml index 1028374897..7faf65371f 100644 --- a/config/platforms.yaml +++ b/config/platforms.yaml @@ -31,6 +31,10 @@ autodetect: name: Durango (XboxOne) check: DUK_F_DURANGO include: platform_durango.h.in + - + name: Intel SGX # Keep this above Linux or Windows. + check: DUK_F_INTELSGX + include: platform_intelsgx.h.in - name: Windows check: DUK_F_WINDOWS diff --git a/config/platforms/platform_intelsgx.h.in b/config/platforms/platform_intelsgx.h.in new file mode 100644 index 0000000000..9b7e2c00de --- /dev/null +++ b/config/platforms/platform_intelsgx.h.in @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +/* + * Intel SGX enclaves are completely isolated from the underlying + * kernel and therefore syscalls are not allowed by default. This + * means that getting the current time and locale information is + * impossible unless there is intervention by the enclave developers. + * + * Thus enclave developers must define DUK_USE_DATE_GET_NOW and + * DUK_USE_DATE_GET_LOCAL_TZOFFSET for their enclave either manually + * in duk_config.h or via other means (other config header, compiler + * flags). If these are not defined compilation will fail with a + * preprocessor error. + * + * DUK_USE_DATE_GET_NOW should return a duk_double_t value containing + * the current ECMASCript time. + * DUK_USE_DATE_GET_LOCAL_TZOFFSET should return a duk_int_t value + * containing the offset from UTC in seconds of the current time. + * See the datetime.rst file for more information, as well as the + * duk_bi_date.c, duk_bi_date_unix.c files for inspiration. + * + * DUK_USE_DATE_FORMAT_STRING and DUK_USE_DATE_PARSE_STRING macros are + * not mandatory and are therefore not defined here, but enclave + * developers are free to define them via duk_config.h or other means + * if they are implementing platform or locale scpecific date parsing + * and formatting. + * + * All related Duktape provided implementations regarding date and + * time are intentionally disabled, since date and time functions are + * not generally available in Intel SGX enclaves. + */ + +#undef DUK_USE_DATE_FMT_STRFTIME +#undef DUK_USE_DATE_NOW_GETTIMEOFDAY +#undef DUK_USE_DATE_NOW_TIME +#undef DUK_USE_DATE_NOW_WINDOWS +#undef DUK_USE_DATE_NOW_WINDOWS_SUBMS +#undef DUK_USE_DATE_PRS_GETDATE +#undef DUK_USE_DATE_PRS_STRPTIME +#undef DUK_USE_DATE_TZO_GMTIME +#undef DUK_USE_DATE_TZO_GMTIME_R +#undef DUK_USE_DATE_TZO_GMTIME_S +#undef DUK_USE_DATE_TZO_WINDOWS +#undef DUK_USE_DATE_TZO_WINDOWS_NO_DST + +/* + * Intel SGX enclaves don't have access to sscanf, so pointer parsing must + * be without using sscanf. + */ +#define DUK_USE_MEMBASED_POINTER_ENCODING + +#define DUK_SNPRINTF(a,b,c,...) _snprintf_s(a,b,b,c,__VA_ARGS__) +#define DUK_VSNPRINTF(a,b,c,...) _vsnprintf_s(a,b,b,c,__VA_ARGS__) + +#if defined(DUK_F_WINDOWS) +/* + * On Windows, assume we're little endian. Even Itanium which has a + * configurable endianness runs little endian in Windows. + */ +#if !defined(DUK_USE_BYTEORDER) +#define DUK_USE_BYTEORDER 1 +#endif +#endif + +#if defined(DUK_F_WINDOWS) +#define DUK_USE_OS_STRING "intelsgx-windows" +#elif defined(DUK_F_LINUX) +#define DUK_USE_OS_STRING "intelsgx-linux" +#else +#error Intel SGX is supported only on Linux or Windows. +#endif diff --git a/doc/json.rst b/doc/json.rst index 42c695212b..c8ad5284ba 100644 --- a/doc/json.rst +++ b/doc/json.rst @@ -151,7 +151,9 @@ detection: need to maintain yet another growing data structure for the stack, and don't need to do linear stack scans to detect loops. The downside is relatively large memory footprint and lots of additional string table operations. - However, these effects only come into play for very deep objects. + However, these effects only come into play for very deep objects. The option + ``DUK_USE_MEMBASED_POINTER_ENCODING`` can be used to remove the reliance + on ``%p``, however pointer values will still be platform-dependent. There's much room for improvement in the loop detection: @@ -546,6 +548,8 @@ specific form, using the format ``(%p)``, e.g.:: (0x1ff0e10) // 32-bit Linux (000FEFF8) // 32-bit Windows (000000000026A8A0) // 64-bit Windows + (0d0c0b0a00000000) // 0x0A0B0C0D in 64-bit little-endian x64 + // with DUK_USE_MEMBASED_POINTER_ENCODING A pointer value parses back correctly when serialized and parsed by the same program. Other than that there is no guarantee that a pointer value can be @@ -561,6 +565,14 @@ an error. (null) +By enabling the ``DUK_USE_MEMBASED_POINTER_ENCODING`` option you can make +the pointer encoding and decoding be "standard" across Duktape builds, but it +is still platform dependant. It would depend on the memory layout and size in +bytes of a pointer value, rather than ``%p``. Pointers can, therefore, be +parsed across different Duktape builds for the same platform. The pointers are +encoded in lowercase hex and there are 2 hex characters for each byte of the +pointer value, as it appears in memory, including heading / trailing 0s. + ASCII only output ----------------- @@ -674,6 +686,9 @@ specific form, using the format ``%p``, but wrapped in a marker table:: Note that compared to JX, the difference is that there are no surrounding parentheses outside the pointer value. +The option ``DUK_USE_MEMBASED_POINTER_ENCODING`` will also be used here as +well. + ASCII only output ----------------- diff --git a/src-input/duk_api_stack.c b/src-input/duk_api_stack.c index 1b895b053e..1ba9b50312 100644 --- a/src-input/duk_api_stack.c +++ b/src-input/duk_api_stack.c @@ -3345,6 +3345,12 @@ DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, d return duk_to_int_clamped_raw(thr, idx, minval, maxval, NULL); /* out_clamped==NULL -> RangeError if outside range */ } +DUK_INTERNAL void duk_pointer_to_string(duk_hthread *thr, void *ptr) { + char ptrstr[DUK_MAX_POINTER_ENCODING_SIZE]; + duk_size_t size = duk_encode_pointer_cstr(ptrstr, sizeof(ptrstr), (void *) ptr); + duk_push_lstring(thr, ptrstr, size); +} + DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; @@ -3408,7 +3414,7 @@ DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) { case DUK_TAG_POINTER: { void *ptr = DUK_TVAL_GET_POINTER(tv); if (ptr != NULL) { - duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) ptr); + duk_pointer_to_string(thr, ptr); } else { /* Represent a null pointer as 'null' to be consistent with * the JX format variant. Native '%p' format for a NULL diff --git a/src-input/duk_bi_date.c b/src-input/duk_bi_date.c index 4eaaf93557..3e655e3a57 100644 --- a/src-input/duk_bi_date.c +++ b/src-input/duk_bi_date.c @@ -957,8 +957,7 @@ DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_doub return 1; } -/* 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long. */ -DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags, duk_uint8_t *out_buf) { +DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags, duk_uint8_t *out_buf, duk_size_t bufsize) { char yearstr[8]; /* "-123456\0" */ char tzstr[8]; /* "+11:22\0" */ char sep = (flags & DUK_DATE_FLAG_SEP_T) ? DUK_ASC_UC_T : DUK_ASC_SPACE; @@ -1008,16 +1007,16 @@ DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, d * is portable. */ if ((flags & DUK_DATE_FLAG_TOSTRING_DATE) && (flags & DUK_DATE_FLAG_TOSTRING_TIME)) { - DUK_SPRINTF((char *) out_buf, "%s-%02d-%02d%c%02d:%02d:%02d.%03d%s", + DUK_SNPRINTF((char *) out_buf, bufsize, "%s-%02d-%02d%c%02d:%02d:%02d.%03d%s", (const char *) yearstr, (int) parts[DUK_DATE_IDX_MONTH], (int) parts[DUK_DATE_IDX_DAY], (int) sep, (int) parts[DUK_DATE_IDX_HOUR], (int) parts[DUK_DATE_IDX_MINUTE], (int) parts[DUK_DATE_IDX_SECOND], (int) parts[DUK_DATE_IDX_MILLISECOND], (const char *) tzstr); } else if (flags & DUK_DATE_FLAG_TOSTRING_DATE) { - DUK_SPRINTF((char *) out_buf, "%s-%02d-%02d", + DUK_SNPRINTF((char *) out_buf, bufsize, "%s-%02d-%02d", (const char *) yearstr, (int) parts[DUK_DATE_IDX_MONTH], (int) parts[DUK_DATE_IDX_DAY]); } else { DUK_ASSERT(flags & DUK_DATE_FLAG_TOSTRING_TIME); - DUK_SPRINTF((char *) out_buf, "%02d:%02d:%02d.%03d%s", + DUK_SNPRINTF((char *) out_buf, bufsize, "%02d:%02d:%02d.%03d%s", (int) parts[DUK_DATE_IDX_HOUR], (int) parts[DUK_DATE_IDX_MINUTE], (int) parts[DUK_DATE_IDX_SECOND], (int) parts[DUK_DATE_IDX_MILLISECOND], (const char *) tzstr); @@ -1074,7 +1073,7 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_hthread *thr, duk_small_uint_t fla /* Different calling convention than above used because the helper * is shared. */ - duk__format_parts_iso8601(parts, tzoffset, flags, buf); + duk__format_parts_iso8601(parts, tzoffset, flags, buf, sizeof(buf)); duk_push_string(thr, (const char *) buf); return 1; } diff --git a/src-input/duk_bi_json.c b/src-input/duk_bi_json.c index efe0ad01b7..de3abcf62d 100644 --- a/src-input/duk_bi_json.c +++ b/src-input/duk_bi_json.c @@ -538,12 +538,15 @@ DUK_LOCAL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx) { DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { duk_hthread *thr = js_ctx->thr; const duk_uint8_t *p; + char pcpy[DUK_MAX_POINTER_ENCODING_SIZE]; duk_small_int_t x; + duk_size_t encsz; void *voidptr; /* Caller has already eaten the first character ('(') which we don't need. */ p = js_ctx->p; + encsz = 0; for (;;) { x = *p; @@ -552,6 +555,7 @@ DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { * parenthesis. */ + encsz++; if (x == DUK_ASC_RPAREN) { break; } else if (x <= 0) { @@ -561,19 +565,16 @@ DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { p++; } - /* There is no need to NUL delimit the sscanf() call: trailing garbage is - * ignored and there is always a NUL terminator which will force an error - * if no error is encountered before it. It's possible that the scan - * would scan further than between [js_ctx->p,p[ though and we'd advance - * by less than the scanned value. - * - * Because pointers are platform specific, a failure to scan a pointer - * results in a null pointer which is a better placeholder than a missing - * value or an error. - */ - voidptr = NULL; - (void) DUK_SSCANF((const char *) js_ctx->p, DUK_STR_FMT_PTR, &voidptr); + + if (encsz > 1 && encsz <= sizeof(pcpy)) { + duk_memzero(pcpy, sizeof(pcpy)); + duk_memcpy(pcpy, js_ctx->p, encsz); + pcpy[encsz - 1] = 0; /* copied ')' change to NUL */ + + duk_decode_pointer_cstr(pcpy, encsz, &voidptr); + } + duk_push_pointer(thr, voidptr); js_ctx->p = p + 1; /* skip ')' */ @@ -1419,7 +1420,7 @@ DUK_LOCAL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) { * "long long" type exists. Could also rely on C99 directly but that * won't work for older MSVC. */ - DUK_SPRINTF((char *) buf, "%lld", (long long) v); + DUK_SNPRINTF((char *) buf, sizeof(buf), "%lld", (long long) v); DUK__EMIT_CSTR(js_ctx, (const char *) buf); } #endif @@ -1591,14 +1592,14 @@ DUK_LOCAL void duk__enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuff for (i = 0; i < n; i++) { duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth + 1); q = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, 32); - q += DUK_SPRINTF((char *) q, "\"%lu\": %u,", (unsigned long) i, (unsigned int) buf[i]); + q += DUK_SNPRINTF((char *) q, 32, "\"%lu\": %u,", (unsigned long) i, (unsigned int) buf[i]); DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); } } else { q = DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw); for (i = 0; i < n; i++) { q = DUK_BW_ENSURE_RAW(js_ctx->thr, &js_ctx->bw, 32, q); - q += DUK_SPRINTF((char *) q, "\"%lu\":%u,", (unsigned long) i, (unsigned int) buf[i]); + q += DUK_SNPRINTF((char *) q, 32, "\"%lu\":%u,", (unsigned long) i, (unsigned int) buf[i]); } DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); } @@ -1613,13 +1614,15 @@ DUK_LOCAL void duk__enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuff #if defined(DUK_USE_JX) || defined(DUK_USE_JC) DUK_LOCAL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { - char buf[64]; /* XXX: how to figure correct size? */ + char buf[DUK_MAX_POINTER_ENCODING_SIZE + 15]; /* length of {"_ptr":"null"} is 15 */ + char ptrbuf[DUK_MAX_POINTER_ENCODING_SIZE]; const char *fmt; DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); /* caller checks */ DUK_ASSERT(js_ctx->flag_ext_custom_or_compatible); duk_memzero(buf, sizeof(buf)); + duk_memzero(ptrbuf, sizeof(ptrbuf)); /* The #if defined() clutter here needs to handle the three * cases: (1) JX+JC, (2) JX only, (3) JC only. @@ -1629,7 +1632,7 @@ DUK_LOCAL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { #endif #if defined(DUK_USE_JX) { - fmt = ptr ? "(%p)" : "(null)"; + fmt = ptr ? "(%s)" : "(null)"; } #endif #if defined(DUK_USE_JX) && defined(DUK_USE_JC) @@ -1638,12 +1641,13 @@ DUK_LOCAL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { #if defined(DUK_USE_JC) { DUK_ASSERT(js_ctx->flag_ext_compatible); - fmt = ptr ? "{\"_ptr\":\"%p\"}" : "{\"_ptr\":\"null\"}"; + fmt = ptr ? "{\"_ptr\":\"%s\"}" : "{\"_ptr\":\"null\"}"; } #endif + duk_encode_pointer_cstr(ptrbuf, sizeof(ptrbuf), ptr); /* When ptr == NULL, the format argument is unused. */ - DUK_SNPRINTF(buf, sizeof(buf) - 1, fmt, ptr); /* must not truncate */ + DUK_SNPRINTF(buf, sizeof(buf) - 1, fmt, ptrbuf); /* must not truncate */ DUK__EMIT_CSTR(js_ctx, buf); } #endif /* DUK_USE_JX || DUK_USE_JC */ @@ -1736,6 +1740,7 @@ DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_ duk_hthread *thr = js_ctx->thr; duk_hobject *h_target; duk_uint_fast32_t i, n; + char ptrstr[DUK_MAX_POINTER_ENCODING_SIZE]; *entry_top = duk_get_top(thr); @@ -1761,7 +1766,8 @@ DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_ if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) { js_ctx->visiting[js_ctx->recursion_depth] = h_target; } else { - duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target); + duk_size_t len = duk_encode_pointer_cstr(ptrstr, sizeof(ptrstr), (void *) h_target); + duk_push_lstring(thr, ptrstr, len); duk_dup_top(thr); /* -> [ ... voidp voidp ] */ if (duk_has_prop(thr, js_ctx->idx_loop)) { DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT); @@ -1789,6 +1795,7 @@ DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_ DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { duk_hthread *thr = js_ctx->thr; duk_hobject *h_target; + char ptrstr[DUK_MAX_POINTER_ENCODING_SIZE]; /* C recursion check. */ @@ -1803,7 +1810,8 @@ DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_t if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) { /* Previous entry was inside visited[], nothing to do. */ } else { - duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target); + duk_size_t len = duk_encode_pointer_cstr(ptrstr, sizeof(ptrstr), (void *) h_target); + duk_push_lstring(thr, ptrstr, len); duk_del_prop(thr, js_ctx->idx_loop); /* -> [ ... ] */ } @@ -2749,7 +2757,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); d = DUK_TVAL_GET_DOUBLE(tv); - DUK_SPRINTF(buf, "%lg", d); + DUK_SNPRINTF(buf, sizeof(buf), "%lg", d); DUK__EMIT_CSTR(js_ctx, buf); #endif } diff --git a/src-input/duk_bi_symbol.c b/src-input/duk_bi_symbol.c index a6c49ac2a5..c5c52ef3cb 100644 --- a/src-input/duk_bi_symbol.c +++ b/src-input/duk_bi_symbol.c @@ -16,6 +16,7 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) { duk_uint8_t *buf; duk_uint8_t *p; duk_int_t magic; + duk_int_t flen; magic = duk_get_current_magic(thr); if (duk_is_undefined(thr, 0) && (magic == 0)) { @@ -35,6 +36,17 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) { * +1 0xff after description, before unique suffix * +17 autogenerated unique suffix: 'ffffffff-ffffffff' is longest * +1 0xff after unique suffix for symbols with undefined description + * + * The layout therefore looks like so: + * + * A len B f f f f f f f f - f f f f f f f f C + * -- --- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + * 00 len 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 + * + * snprintf does not always terminate with NUL, however if it does it will + * write the NUL in the C position, which can be overwritten with 0xff + * if desc is NULL. Nonetheless, snprintf when positive, returns the number + * of bytes that the format would have taken without counting NUL. */ buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, 1 + len + 1 + 17 + 1); DUK_ASSERT(buf != NULL); @@ -50,9 +62,18 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) { if (++thr->heap->sym_counter[0] == 0) { thr->heap->sym_counter[1]++; } - p += DUK_SPRINTF((char *) p, "\xFF" "%lx-%lx", - (unsigned long) thr->heap->sym_counter[1], - (unsigned long) thr->heap->sym_counter[0]); + + flen = DUK_SNPRINTF((char *) p, + 1 + 17 + 1, /* B || format || C for NUL */ + "\xFF" "%lx-%lx", + (unsigned long) thr->heap->sym_counter[1], + (unsigned long) thr->heap->sym_counter[0]); + + /* make sure that we had enough room to format the symbol */ + DUK_ASSERT(flen > 0 && flen <= 1 + 17 /* without + 1 for NUL */); + + p += flen; + if (desc == NULL) { /* Special case for 'undefined' description, trailing * 0xff distinguishes from empty string description, diff --git a/src-input/duk_numconv.c b/src-input/duk_numconv.c index cabc39192b..47a44f7882 100644 --- a/src-input/duk_numconv.c +++ b/src-input/duk_numconv.c @@ -91,16 +91,40 @@ DUK_LOCAL void duk__bi_print(const char *name, duk__bigint *x) { char buf[DUK__BI_MAX_PARTS * 9 + 64]; char *p = buf; duk_small_int_t i; + duk_int_t flen; /* No NUL term checks in this debug code. */ - p += DUK_SPRINTF(p, "%p n=%ld", (void *) x, (long) x->n); + flen = DUK_SNPRINTF(p, 64, "%p n=%ld", (void *) x, (long) x->n); + if (flen > 63) { + p += 63; + } else if (flen > 0) { + p += flen; + } + if (x->n == 0) { - p += DUK_SPRINTF(p, " 0"); + DUK_ASSERT(p - buf < sizeof(buf)); + + flen = DUK_SNPRINTF(p, 3, " 0"); + if (flen > 2) { + p += 2; + } else if (flen > 0) { + p += flen; + } } for (i = x->n - 1; i >= 0; i--) { - p += DUK_SPRINTF(p, " %08lx", (unsigned long) x->v[i]); + DUK_ASSERT(p - buf < sizeof(buf)); + + flen = DUK_SNPRINTF(p, 10, " %08lx", (unsigned long) x->v[i]); + if (flen > 9) { + p += 9; + } else if (flen > 0) { + p += flen; + } } + *p = 0; /* finally, NUL terminate buf regardless of snprintf */ + DUK_ASSERT(p - buf < sizeof(buf)); + DUK_DDD(DUK_DDDPRINT("%s: %s", (const char *) name, (const char *) buf)); } #endif diff --git a/src-input/duk_util.h b/src-input/duk_util.h index a63eba8292..9194a3d37d 100644 --- a/src-input/duk_util.h +++ b/src-input/duk_util.h @@ -728,4 +728,42 @@ DUK_INTERNAL_DECL duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y); #define DUK_IS_POWER_OF_TWO(x) \ ((x) != 0U && ((x) & ((x) - 1U)) == 0U) +#if defined(DUK_USE_MEMBASED_POINTER_ENCODING) +/* + * Memory based encoding is simple: 2 chars per byte. + */ +#define DUK_MAX_POINTER_ENCODING_SIZE (2 * sizeof(void *) + 1) +#else +/* + * It's impossible to know the actual size of the pointer encoded + * as string with %p (or decoded) at compile time. However, assuming + * that the worst thing printf/scanf can do is encode it as possibly + * signed base 8 integer, the encoding for (2^64-1) in base 8 is + * 22 characters long. We round at 32 (inc \0) to account for other + * bases or other weird encodings. + */ +#define DUK_MAX_POINTER_ENCODING_SIZE 32 +#endif + +/* + * Encodes a pointer value to a NUL terminated C string representing the ptr value, into buf. + * + * Returns: the number of the written characters without the null character at the end. + */ +DUK_INTERNAL_DECL duk_size_t duk_encode_pointer_cstr(char* buf, duk_size_t sz, void *ptr); + +#if defined(DUK_USE_JX) +/* + * Decodes a pointer value represented in a NUL terminated string containing *only ASCII*. + * + * Params: + * - buf the NUL termnated string + * - sz the size of the buffer, this is to ensure proper parsing + * - outputs the parsed pointer value or NULL + * + * Returns: 0 if parsing failed, 1 if parsing is successful. + */ +DUK_INTERNAL_DECL int duk_decode_pointer_cstr(const char* buf, duk_size_t sz, void **ptr); +#endif + #endif /* DUK_UTIL_H_INCLUDED */ diff --git a/src-input/duk_util_misc.c b/src-input/duk_util_misc.c index 8922d5e1bc..c3b8898c51 100644 --- a/src-input/duk_util_misc.c +++ b/src-input/duk_util_misc.c @@ -181,3 +181,103 @@ DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len) { } } #endif + +#if defined(DUK_USE_MEMBASED_POINTER_ENCODING) +union duk__ptr_access { + void *ptr; + unsigned char bytes[sizeof(void *)]; +}; +#endif + +DUK_INTERNAL duk_size_t duk_encode_pointer_cstr(char* buf, duk_size_t sz, void *ptr) { +#if defined(DUK_USE_MEMBASED_POINTER_ENCODING) + duk_size_t i; + union duk__ptr_access ptraccess; + + if (DUK_UNLIKELY(sz < 2 * sizeof(void *) + 1)) { + return 0; + } + + ptraccess.ptr = ptr; + + for (i = 0; i < sizeof(void *); i++) { + buf[2 * i + 0] = duk_lc_digits[(ptraccess.bytes[i] >> 4) & 0xF]; + buf[2 * i + 1] = duk_lc_digits[(ptraccess.bytes[i] >> 0) & 0xF]; + } + + return 2 * sizeof(void *); +#else + int compsize = DUK_SNPRINTF(buf, sz, DUK_STR_FMT_PTR, ptr); + + if (DUK_LIKELY(compsize > 0 && ((duk_size_t) compsize) < sz)) { + return (duk_size_t) compsize; + } + + duk_memzero(buf, sz); + + return 0; +#endif +} + +#if defined(DUK_USE_JX) +DUK_INTERNAL int duk_decode_pointer_cstr(const char* buf, duk_size_t sz, void **ptr) { +#if defined(DUK_USE_MEMBASED_POINTER_ENCODING) + duk_size_t i; + union duk_ptr_access ptraccess; + + *ptr = NULL; + + if (DUK_UNLIKELY(sz < 1 || sz < 2 * sizeof(void *) + 1 || 0 != buf[sz - 1])) { + return 0; /* syntax error */ + } + + for (i = 0; i < 2 * sizeof(void *); i++) { + if (DUK_LIKELY(buf[i] >= '0' && buf[i] <= '9')) { + continue; + } + + if (DUK_LIKELY(buf[i] >= 'a' && buf[i] <= 'f')) { + continue; + } + + return 0; /* syntax error */ + } + + for (i = 0; i < sizeof(void *); i++) { + ptraccess.bytes[i] = duk_hex_dectab_shift4[buf[2 * i + 0]] | duk_hex_dectab[buf[2 * i + 1]]; + } + + *ptr = ptraccess.ptr; + + return 1; /* OK */ +#else + int res; + duk_size_t i; + + *ptr = NULL; + + for (i = 0; i < sz; i++) { + if (0 == buf[i]) { + goto safe_sscanf; + } + } + + /* no NUL was found, therefore not safe to call sscanf */ + goto syntax_error; + +safe_sscanf: + res = DUK_SSCANF(buf, DUK_STR_FMT_PTR, ptr); + + if (res < 1) { + goto syntax_error; + } + + return 1; /* OK */ + +syntax_error: + *ptr = NULL; + + return 0; +#endif +#endif /* DUK_USE_JX */ +}