diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml index e945887b2b..0068206aae 100644 --- a/.github/workflows/compilation_on_android_ubuntu.yml +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -369,6 +369,48 @@ jobs: ctest --output-on-failure working-directory: tests/unit + build_array_bounds_warning_check: + needs: [build_llvm_libraries_on_ubuntu_2204] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + with: + submodules: recursive + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build shared-utils with array-bounds warnings as errors + run: | + mkdir build && cd build + cmake .. \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_FLAGS="-Werror=array-bounds" \ + -DCMAKE_CXX_FLAGS="-Werror=array-bounds" + cmake --build . --target shared_utils_test --parallel 4 + working-directory: tests/unit + build_regression_tests: needs: [build_llvm_libraries_on_ubuntu_2204] runs-on: ${{ matrix.os }} diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 687c75e142..1ea7ed713a 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -432,7 +432,7 @@ typedef struct AOTFrame { * currently local's ref flags are stored in AOTModule, * here we only reserve the padding bytes */ - uint32 lp[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint32, lp); } AOTFrame; #if WASM_ENABLE_STATIC_PGO != 0 diff --git a/core/iwasm/common/gc/gc_object.h b/core/iwasm/common/gc/gc_object.h index 75fdbef5d8..e0be3b198b 100644 --- a/core/iwasm/common/gc/gc_object.h +++ b/core/iwasm/common/gc/gc_object.h @@ -77,7 +77,7 @@ typedef uintptr_t WASMI31ObjectRef; typedef struct WASMStructObject { /* Must be pointer of WASMRttObject of struct type */ WASMObjectHeader header; - uint8 field_data[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint8, field_data); } WASMStructObject, *WASMStructObjectRef; /* Representation of WASM array objects */ @@ -89,7 +89,7 @@ typedef struct WASMArrayObject { * elem_size = 2 ^ (length & 0x3) */ uint32 length; - uint8 elem_data[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint8, elem_data); } WASMArrayObject, *WASMArrayObjectRef; #define WASM_ARRAY_LENGTH_SHIFT 2 diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index f1ecccfb37..9a545cda2b 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -133,7 +133,7 @@ typedef struct AOTTableInitData { /* Function index count */ uint32 value_count; /* Function index array */ - InitializerExpression init_values[1]; + BH_FLEXIBLE_ARRAY_MEMBER(InitializerExpression, init_values); } AOTTableInitData; /** diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index c60349d10f..732a4a980c 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -270,13 +270,13 @@ typedef union WASMValue { typedef struct WASMStructNewInitValues { uint32 type_idx; uint32 count; - WASMValue fields[1]; + BH_FLEXIBLE_ARRAY_MEMBER(WASMValue, fields); } WASMStructNewInitValues; typedef struct WASMArrayNewInitValues { uint32 type_idx; uint32 length; - WASMValue elem_data[1]; + BH_FLEXIBLE_ARRAY_MEMBER(WASMValue, elem_data); } WASMArrayNewInitValues; typedef struct InitializerExpression { @@ -444,7 +444,7 @@ typedef struct WASMFuncType { /* types of params and results, only store the first byte * of the type, if it cannot be described with one byte, * then the full type info is stored in ref_type_maps */ - uint8 types[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint8, types); } WASMFuncType; #if WASM_ENABLE_GC != 0 @@ -486,7 +486,7 @@ typedef struct WASMStructType { * the first byte of the field type, if it cannot be described * with one byte, then the full field type info is stored in * ref_type_maps */ - WASMStructFieldType fields[1]; + BH_FLEXIBLE_ARRAY_MEMBER(WASMStructFieldType, fields); } WASMStructType; typedef struct WASMArrayType { @@ -862,7 +862,7 @@ typedef struct BrTableCache { /* Address of br_table opcode */ uint8 *br_table_op_addr; uint32 br_count; - uint32 br_depths[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint32, br_depths); } BrTableCache; #if WASM_ENABLE_DEBUG_INTERP != 0 diff --git a/core/iwasm/interpreter/wasm_interp.h b/core/iwasm/interpreter/wasm_interp.h index 1416405460..eda83f6338 100644 --- a/core/iwasm/interpreter/wasm_interp.h +++ b/core/iwasm/interpreter/wasm_interp.h @@ -50,7 +50,7 @@ typedef struct WASMInterpFrame { #if WASM_ENABLE_GC != 0 uint8 *frame_ref; #endif - uint32 operand[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint32, operand); #else /* else of WASM_ENABLE_FAST_INTERP != 0 */ /* Operand stack top pointer of the current frame. The bottom of the stack is the next cell after the last local variable. */ @@ -71,7 +71,7 @@ typedef struct WASMInterpFrame { * whether each cell in local and stack area is a GC obj * jit spill cache: only available for fast jit */ - uint32 lp[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint32, lp); #endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ } WASMInterpFrame; diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index a2c67bea2c..a5eb57104d 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -1328,8 +1328,10 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, goto fail; } + /* Use offsetof for flexible array members: + sizeof excludes elem_data when it is []. */ size = - sizeof(WASMArrayNewInitValues) + offsetof(WASMArrayNewInitValues, elem_data) + sizeof(WASMValue) * (uint64)len_val.i32; if (!(array_init_values = loader_malloc( size, error_buf, error_buf_size))) { diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 98913e9fee..8271940f52 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -188,7 +188,7 @@ struct WASMTableInstance { /* Maximum size */ uint32 max_size; /* Table elements */ - table_elem_type_t elems[1]; + BH_FLEXIBLE_ARRAY_MEMBER(table_elem_type_t, elems); }; struct WASMGlobalInstance { diff --git a/core/shared/platform/include/platform_common.h b/core/shared/platform/include/platform_common.h index 28001af746..64eb20b533 100644 --- a/core/shared/platform/include/platform_common.h +++ b/core/shared/platform/include/platform_common.h @@ -99,6 +99,16 @@ BH_VPRINTF(const char *format, va_list ap); #endif #endif +/* Prefer C99 flexible array members for FORTIFY/ASAN compatibility */ +#ifndef BH_FLEXIBLE_ARRAY_MEMBER +#if !defined(__cplusplus) && defined(__STDC_VERSION__) \ + && __STDC_VERSION__ >= 199901L +#define BH_FLEXIBLE_ARRAY_MEMBER(type, name) type name[] +#else +#define BH_FLEXIBLE_ARRAY_MEMBER(type, name) type name[1] +#endif +#endif + typedef uint8_t uint8; typedef int8_t int8; typedef uint16_t uint16; diff --git a/core/shared/utils/bh_bitmap.h b/core/shared/utils/bh_bitmap.h index c0e56cb995..c37ec614d6 100644 --- a/core/shared/utils/bh_bitmap.h +++ b/core/shared/utils/bh_bitmap.h @@ -23,7 +23,7 @@ typedef struct bh_bitmap { uintptr_t end_index; /* The bitmap. */ - uint8 map[1]; + BH_FLEXIBLE_ARRAY_MEMBER(uint8, map); } bh_bitmap; /** diff --git a/core/shared/utils/bh_hashmap.c b/core/shared/utils/bh_hashmap.c index 794b7a746e..bffa3aecf0 100644 --- a/core/shared/utils/bh_hashmap.c +++ b/core/shared/utils/bh_hashmap.c @@ -22,7 +22,7 @@ struct HashMap { KeyEqualFunc key_equal_func; KeyDestroyFunc key_destroy_func; ValueDestroyFunc value_destroy_func; - HashMapElem *elements[1]; + BH_FLEXIBLE_ARRAY_MEMBER(HashMapElem *, elements); }; HashMap * diff --git a/tests/unit/shared-utils/bh_hashmap_test.cc b/tests/unit/shared-utils/bh_hashmap_test.cc index 496170226f..8ca2068abb 100644 --- a/tests/unit/shared-utils/bh_hashmap_test.cc +++ b/tests/unit/shared-utils/bh_hashmap_test.cc @@ -29,7 +29,7 @@ struct HashMap { KeyEqualFunc key_equal_func; KeyDestroyFunc key_destroy_func; ValueDestroyFunc value_destroy_func; - HashMapElem *elements[1]; + BH_FLEXIBLE_ARRAY_MEMBER(HashMapElem *, elements); }; int DESTROY_NUM = 0; @@ -37,6 +37,19 @@ char TRAVERSE_KEY[] = "key_1"; char TRAVERSE_VAL[] = "val_1"; int TRAVERSE_COMP_RES = 0; +uint32 +hash_to_last_bucket(const void *key) +{ + (void)key; + return 31; +} + +bool +ptr_equal_test(void *key1, void *key2) +{ + return key1 == key2; +} + class bh_hashmap_test_suite : public testing::Test { protected: @@ -108,6 +121,19 @@ TEST_F(bh_hashmap_test_suite, bh_hash_map_insert) (void *)"val_1")); } +TEST_F(bh_hashmap_test_suite, bh_hash_map_insert_trailing_bucket) +{ + HashMap *test_hash_map = bh_hash_map_create( + 32, false, hash_to_last_bucket, ptr_equal_test, nullptr, nullptr); + int key = 0; + int value = 0; + + ASSERT_NE((HashMap *)nullptr, test_hash_map); + EXPECT_EQ(true, bh_hash_map_insert(test_hash_map, &key, &value)); + EXPECT_EQ(&value, bh_hash_map_find(test_hash_map, &key)); + EXPECT_EQ(true, bh_hash_map_destroy(test_hash_map)); +} + TEST_F(bh_hashmap_test_suite, bh_hash_map_find) { HashMap *test_hash_map = bh_hash_map_create( @@ -359,4 +385,4 @@ TEST_F(bh_hashmap_test_suite, bh_hashmap_thread_safety) EXPECT_EQ(200, COUNT_ELEM); EXPECT_EQ(true, bh_hash_map_destroy(test_hash_map)); -} \ No newline at end of file +} diff --git a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh index 9ea733c3ff..f30f29b091 100755 --- a/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh +++ b/tests/wamr-test-suites/wasi-test-script/run_wasi_tests.sh @@ -71,6 +71,7 @@ run_aot_tests () { test_aot="${test_wasm%.wasm}.aot" test_json="${test_wasm%.wasm}.json" + # This pre-read is redundant: expected is reset below before checking if [ -f ${test_wasm} ]; then expected=$(jq .exit_code ${test_json}) fi @@ -141,7 +142,9 @@ if [[ $MODE != "aot" ]];then deactivate else target_option="" - if [[ $TARGET == "X86_32" ]];then + if [[ $TARGET == "X86_64" ]]; then + target_option="--target=x86_64 --cpu=x86-64" + elif [[ $TARGET == "X86_32" ]];then target_option="--target=i386" fi