diff options
-rw-r--r-- | .github/workflows/macos.yml | 2 | ||||
-rw-r--r-- | .github/workflows/openwrt-ci-master.yml | 53 | ||||
-rw-r--r-- | .github/workflows/openwrt-ci-pull-request.yml | 53 | ||||
-rw-r--r-- | CMakeLists.txt | 20 | ||||
-rw-r--r-- | lib/digest.c | 372 | ||||
-rw-r--r-- | tests/custom/99_bugs/50_missing_upvalue_resolving | 27 | ||||
-rw-r--r-- | types.c | 48 | ||||
-rw-r--r-- | vm.c | 2 |
8 files changed, 458 insertions, 119 deletions
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ad5f2ce..72f3454 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -15,7 +15,7 @@ jobs: - name: Setup run: | - brew install json-c + brew install json-c libmd - name: Build minimal version run: | diff --git a/.github/workflows/openwrt-ci-master.yml b/.github/workflows/openwrt-ci-master.yml index fda3f80..f95c924 100644 --- a/.github/workflows/openwrt-ci-master.yml +++ b/.github/workflows/openwrt-ci-master.yml @@ -32,56 +32,3 @@ jobs: path: | build/scan tests/cram/**/*.t.err - - sdk_build: - name: Build with OpenWrt ${{ matrix.arch }} SDK - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - include: - - arch: mips_24kc - target: ath79-generic - - - arch: arm_cortex-a9_neon - target: imx-cortexa9 - - - arch: mipsel_24kc - target: malta-le - - - arch: aarch64_cortex-a53 - target: mediatek-mt7622 - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Determine branch name - run: | - BRANCH="${GITHUB_BASE_REF#refs/heads/}" - echo "Building for $BRANCH" - echo "BRANCH=$BRANCH" >> $GITHUB_ENV - - - name: Build with OpenWrt ${{ matrix.arch }} SDK - uses: openwrt/gh-action-sdk@v5 - env: - ARCH: ${{ matrix.arch }} - FEEDNAME: ucode_ci - PACKAGES: ucode - - - name: Move created packages to project dir - run: cp bin/packages/${{ matrix.arch }}/ucode_ci/*.ipk . || true - - - name: Store packages - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.arch }}-packages - path: "*.ipk" - - - name: Store logs - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.arch }}-logs - path: logs/ diff --git a/.github/workflows/openwrt-ci-pull-request.yml b/.github/workflows/openwrt-ci-pull-request.yml index e8c29c1..8d6ad34 100644 --- a/.github/workflows/openwrt-ci-pull-request.yml +++ b/.github/workflows/openwrt-ci-pull-request.yml @@ -34,56 +34,3 @@ jobs: path: | build/scan tests/cram/**/*.t.err - - sdk_build: - name: Build with OpenWrt ${{ matrix.arch }} SDK - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - include: - - arch: mips_24kc - target: ath79-generic - - - arch: arm_cortex-a9_neon - target: imx-cortexa9 - - - arch: mipsel_24kc - target: malta-le - - - arch: aarch64_cortex-a53 - target: mediatek-mt7622 - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Determine branch name - run: | - BRANCH="${GITHUB_BASE_REF#refs/heads/}" - echo "Building for $BRANCH" - echo "BRANCH=$BRANCH" >> $GITHUB_ENV - - - name: Build with OpenWrt ${{ matrix.arch }} SDK - uses: openwrt/gh-action-sdk@v5 - env: - ARCH: ${{ matrix.arch }} - FEEDNAME: ucode_ci - PACKAGES: ucode - - - name: Move created packages to project dir - run: cp bin/packages/${{ matrix.arch }}/ucode_ci/*.ipk . || true - - - name: Store packages - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.arch }}-packages - path: "*.ipk" - - - name: Store logs - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.arch }}-logs - path: logs/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4564818..bdf0738 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ find_library(libubox NAMES ubox) find_library(libubus NAMES ubus) find_library(libblobmsg_json NAMES blobmsg_json) find_package(ZLIB) +find_library(libmd NAMES libmd.a md) if(LINUX) find_library(libnl_tiny NAMES nl-tiny) @@ -54,6 +55,10 @@ if(ZLIB_FOUND) set(DEFAULT_ZLIB_SUPPORT ON) endif() +if(libmd) + set(DEFAULT_DIGEST_SUPPORT ON) +endif() + option(DEBUG_SUPPORT "Debug plugin support" ON) option(FS_SUPPORT "Filesystem plugin support" ON) option(MATH_SUPPORT "Math plugin support" ON) @@ -67,6 +72,8 @@ option(ULOOP_SUPPORT "Uloop plugin support" ${DEFAULT_ULOOP_SUPPORT}) option(LOG_SUPPORT "Log plugin support" ON) option(SOCKET_SUPPORT "Socket plugin support" ON) option(ZLIB_SUPPORT "Zlib plugin support" ${DEFAULT_ZLIB_SUPPORT}) +option(DIGEST_SUPPORT "Digest plugin support" ${DEFAULT_DIGEST_SUPPORT}) +option(DIGEST_SUPPORT_EXTENDED "Enable additional hash algorithms" ${DEFAULT_DIGEST_SUPPORT}) set(LIB_SEARCH_PATH "${CMAKE_INSTALL_PREFIX}/lib/ucode/*.so:${CMAKE_INSTALL_PREFIX}/share/ucode/*.uc:./*.so:./*.uc" CACHE STRING "Default library search path") string(REPLACE ":" "\", \"" LIB_SEARCH_DEFINE "${LIB_SEARCH_PATH}") @@ -290,6 +297,19 @@ if(ZLIB_SUPPORT) target_link_libraries(zlib_lib ZLIB::ZLIB) endif() +if(DIGEST_SUPPORT) + pkg_check_modules(LIBMD REQUIRED libmd) + include_directories(${LIBMD_INCLUDE_DIRS}) + set(LIBRARIES ${LIBRARIES} digest_lib) + add_library(digest_lib MODULE lib/digest.c) + set_target_properties(digest_lib PROPERTIES OUTPUT_NAME digest PREFIX "") + if(DIGEST_SUPPORT_EXTENDED) + target_compile_definitions(digest_lib PUBLIC HAVE_DIGEST_EXTENDED) + endif() + target_link_options(digest_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS}) + target_link_libraries(digest_lib ${libmd}) +endif() + if(UNIT_TESTING) enable_testing() add_definitions(-DUNIT_TESTING) diff --git a/lib/digest.c b/lib/digest.c new file mode 100644 index 0000000..c1287ff --- /dev/null +++ b/lib/digest.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2024 Sebastian Ertz <sebastian.ertz@gmx.de> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * # Digest Functions + * + * The `digest` module bundles various digest functions. + * + * @module digest + */ + +#include <md5.h> +#include <sha1.h> +#include <sha2.h> + +#ifdef HAVE_DIGEST_EXTENDED +#include <md2.h> +#include <md4.h> +#endif + +#include "ucode/module.h" + + +static uc_value_t * +uc_digest_calc_data(uc_value_t *str, char *(*fn)(const uint8_t *,size_t,char *)) +{ + char buf[SHA512_DIGEST_STRING_LENGTH]; + + if( ucv_type(str) != UC_STRING ) + return NULL; + + if( fn((const uint8_t *)ucv_string_get(str), ucv_string_length(str), buf) ) + return ucv_string_new(buf); + + return NULL; +} + +static uc_value_t * +uc_digest_calc_file(uc_value_t *path, char *(fn)(const char *,char *)) +{ + char buf[SHA512_DIGEST_STRING_LENGTH]; + + if( ucv_type(path) != UC_STRING ) + return NULL; + + if( fn(ucv_string_get(path), buf) ) + return ucv_string_new(buf); + + return NULL; +} + +/** + * Calculates the MD5 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#md5 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * md5("This is a test"); // Returns "ce114e4501d2f4e2dcea3e17b546f339" + * md5(123); // Returns null + */ +static uc_value_t * +uc_digest_md5(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), MD5Data); +} + +/** + * Calculates the SHA1 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#sha1 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * sha1("This is a test"); // Returns "a54d88e06612d820bc3be72877c74f257b561b19" + * sha1(123); // Returns null + */ +static uc_value_t * +uc_digest_sha1(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), SHA1Data); +} + +/** + * Calculates the SHA256 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#sha256 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * sha256("This is a test"); // Returns "c7be1ed902fb8dd4d48997c6452f5d7e509fbcdbe2808b16bcf4edce4c07d14e" + * sha256(123); // Returns null + */ +static uc_value_t * +uc_digest_sha256(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), SHA256Data); +} + +#ifdef HAVE_DIGEST_EXTENDED +/** + * Calculates the MD2 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#md2 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * md2("This is a test"); // Returns "dc378580fd0722e56b82666a6994c718" + * md2(123); // Returns null + */ +static uc_value_t * +uc_digest_md2(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), MD2Data); +} + +/** + * Calculates the MD4 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#md4 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * md4("This is a test"); // Returns "3b487cf6856af7e330bc4b1b7d977ef8" + * md4(123); // Returns null + */ +static uc_value_t * +uc_digest_md4(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), MD4Data); +} + +/** + * Calculates the SHA384 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#sha384 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * sha384("This is a test"); // Returns "a27c7667e58200d4c0688ea136968404a0da366b1a9fc19bb38a0c7a609a1eef2bcc82837f4f4d92031a66051494b38c" + * sha384(123); // Returns null + */ +static uc_value_t * +uc_digest_sha384(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), SHA384Data); +} + +/** + * Calculates the SHA384 hash of string and returns that hash. + * + * Returns `null` if a non-string argument is given. + * + * @function module:digest#sha384 + * + * @param {string} str + * The string to hash. + * + * @returns {?string} + * + * @example + * sha512("This is a test"); // Returns "a028d4f74b602ba45eb0a93c9a4677240dcf281a1a9322f183bd32f0bed82ec72de9c3957b2f4c9a1ccf7ed14f85d73498df38017e703d47ebb9f0b3bf116f69" + * sha512(123); // Returns null + */ +static uc_value_t * +uc_digest_sha512(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_data(uc_fn_arg(0), SHA512Data); +} +#endif + +/** + * Calculates the MD5 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#md5_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_md5_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), MD5File); +} + +/** + * Calculates the SHA1 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#sha1_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_sha1_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), SHA1File); +} + +/** + * Calculates the SHA256 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#sha256_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_sha256_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), SHA256File); +} + +#ifdef HAVE_DIGEST_EXTENDED +/** + * Calculates the MD2 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#md2_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_md2_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), MD2File); +} + +/** + * Calculates the MD4 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#md4_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_md4_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), MD4File); +} + +/** + * Calculates the SHA384 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#sha384_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_sha384_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), SHA384File); +} + +/** + * Calculates the SHA512 hash of a given file and returns that hash. + * + * Returns `null` if an error occurred. + * + * @function module:digest#sha512_file + * + * @param {string} path + * The path to the file. + * + * @returns {?string} + */ +static uc_value_t * +uc_digest_sha512_file(uc_vm_t *vm, size_t nargs) +{ + return uc_digest_calc_file(uc_fn_arg(0), SHA512File); +} +#endif + + +static const uc_function_list_t global_fns[] = { + { "md5", uc_digest_md5 }, + { "sha1", uc_digest_sha1 }, + { "sha256", uc_digest_sha256 }, + { "md5_file", uc_digest_md5_file }, + { "sha1_file", uc_digest_sha1_file }, + { "sha256_file", uc_digest_sha256_file }, +#ifdef HAVE_DIGEST_EXTENDED + { "md2", uc_digest_md2 }, + { "md4", uc_digest_md4 }, + { "sha384", uc_digest_sha384 }, + { "sha512", uc_digest_sha512 }, + { "md2_file", uc_digest_md2_file }, + { "md4_file", uc_digest_md4_file }, + { "sha384_file", uc_digest_sha384_file }, + { "sha512_file", uc_digest_sha512_file }, +#endif +}; + +void uc_module_init(uc_vm_t *vm, uc_value_t *scope) +{ + uc_function_list_register(scope, global_fns); +} diff --git a/tests/custom/99_bugs/50_missing_upvalue_resolving b/tests/custom/99_bugs/50_missing_upvalue_resolving new file mode 100644 index 0000000..5e96634 --- /dev/null +++ b/tests/custom/99_bugs/50_missing_upvalue_resolving @@ -0,0 +1,27 @@ +Commit e5fe6b1 ("treewide: refactor vector usage code") accidentially dropped +the upvalue resolving logic from uc_vm_stack_push(), leading to unresolved +upvalues leaking into the script execution context. + +-- File test.uc -- +export let obj = { foo: true, bar: false }; +-- End -- + +-- Testcase -- +import * as test from "./files/test.uc"; + +printf("%.J\n", [ + type(test.obj), + test.obj.foo +]); +-- End -- + +-- Args -- +-R +-- End -- + +-- Expect stdout -- +[ + "object", + true +] +-- End -- @@ -2237,8 +2237,9 @@ uc_value_t * ucv_key_get(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key) { uc_value_t *o, *v = NULL; + bool found = false; + uc_upvalref_t *ref; int64_t idx; - bool found; char *k; if (ucv_type(scope) == UC_ARRAY) { @@ -2247,23 +2248,48 @@ ucv_key_get(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key) if (idx < 0 && idx > INT64_MIN && (uint64_t)llabs(idx) <= ucv_array_length(scope)) idx += ucv_array_length(scope); - if (idx >= 0 && (uint64_t)idx < ucv_array_length(scope)) - return ucv_get(ucv_array_get(scope, idx)); + if (idx >= 0 && (uint64_t)idx < ucv_array_length(scope)) { + v = ucv_array_get(scope, idx); + found = true; + } } - k = ucv_key_to_string(vm, key); + if (!found) { + k = ucv_key_to_string(vm, key); + + for (o = scope; o; o = ucv_prototype_get(o)) { + if (ucv_type(o) != UC_OBJECT) + continue; - for (o = scope; o; o = ucv_prototype_get(o)) { - if (ucv_type(o) != UC_OBJECT) - continue; + v = ucv_object_get(o, k ? k : ucv_string_get(key), &found); - v = ucv_object_get(o, k ? k : ucv_string_get(key), &found); + if (found) + break; + } - if (found) - break; + free(k); } - free(k); + /* Handle upvalue values in objects; under some specific circumstances + objects may contain upvalues, this primarily happens with wildcard module + import namespace dictionaries. */ +#ifdef __clang_analyzer__ + /* Clang static analyzer does not understand that ucv_type(NULL) can't + * possibly yield UC_UPVALUE. Nudge it. */ + if (v != NULL && ucv_type(v) == UC_UPVALUE) +#else + if (ucv_type(v) == UC_UPVALUE) +#endif + { + ref = (uc_upvalref_t *)v; + + if (ref->closed) + return ucv_get(ref->value); + else if (vm) + return ucv_get(vm->stack.entries[ref->slot]); + else + return NULL; + } return ucv_get(v); } @@ -431,7 +431,7 @@ uc_vm_resolve_upval(uc_vm_t *vm, uc_value_t *value) void uc_vm_stack_push(uc_vm_t *vm, uc_value_t *value) { - uc_vector_push(&vm->stack, value); + uc_vector_push(&vm->stack, uc_vm_resolve_upval(vm, value)); if (vm->trace) { fprintf(stderr, " [+%zd] %s\n", |