diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 287c40fb52..6826259a61 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -510,6 +510,7 @@ libtint_source_set("libtint_core_all_src") { "transform/wrap_arrays_in_structs.h", "transform/zero_init_workgroup_memory.cc", "transform/zero_init_workgroup_memory.h", + "utils/bitcast.h", "utils/block_allocator.h", "utils/crc32.h", "utils/debugger.cc", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 1b86aff4c5..9e606d2fa6 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -440,6 +440,7 @@ set(TINT_LIB_SRCS sem/vector.h sem/void.cc sem/void.h + utils/bitcast.h utils/block_allocator.h utils/crc32.h utils/enum_set.h @@ -806,6 +807,7 @@ if(TINT_BUILD_TESTS) text/unicode_test.cc traits_test.cc transform/transform_test.cc + utils/bitcast_test.cc utils/block_allocator_test.cc utils/crc32_test.cc utils/defer_test.cc diff --git a/src/tint/utils/bitcast.h b/src/tint/utils/bitcast.h new file mode 100644 index 0000000000..4450336fbd --- /dev/null +++ b/src/tint/utils/bitcast.h @@ -0,0 +1,39 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SRC_TINT_UTILS_BITCAST_H_ +#define SRC_TINT_UTILS_BITCAST_H_ + +#include + +namespace tint::utils { + +/// Bitcast performs a cast of `from` to the `TO` type using a memcpy. +/// This unsafe cast avoids triggering Clang's Control Flow Integrity checks. +/// See: crbug.com/dawn/1406 +/// See: https://clang.llvm.org/docs/ControlFlowIntegrity.html#bad-cast-checking +/// @param from the value to cast +/// @tparam TO the value to cast to +/// @returns the cast value +template +inline TO Bitcast(FROM&& from) { + static_assert(sizeof(FROM) == sizeof(TO)); + TO to; + memcpy(&to, &from, sizeof(TO)); + return to; +} + +} // namespace tint::utils + +#endif // SRC_TINT_UTILS_BITCAST_H_ diff --git a/src/tint/utils/bitcast_test.cc b/src/tint/utils/bitcast_test.cc new file mode 100644 index 0000000000..236489954b --- /dev/null +++ b/src/tint/utils/bitcast_test.cc @@ -0,0 +1,37 @@ +// Copyright 2022 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/tint/utils/bitcast.h" + +#include + +#include "gtest/gtest.h" + +namespace tint::utils { +namespace { + +TEST(Bitcast, Integer) { + uint32_t a = 123; + int32_t b = Bitcast(a); + EXPECT_EQ(a, static_cast(b)); +} + +TEST(Bitcast, Pointer) { + uint32_t a = 123; + void* b = Bitcast(&a); + EXPECT_EQ(&a, static_cast(b)); +} + +} // namespace +} // namespace tint::utils diff --git a/src/tint/utils/block_allocator.h b/src/tint/utils/block_allocator.h index 84d3bc3a3d..f413940985 100644 --- a/src/tint/utils/block_allocator.h +++ b/src/tint/utils/block_allocator.h @@ -19,6 +19,7 @@ #include #include +#include "src/tint/utils/bitcast.h" #include "src/tint/utils/math.h" namespace tint::utils { @@ -231,14 +232,7 @@ class BlockAllocator { } auto* base = &block_.current->data[0]; - auto* addr = static_cast(base + block_.current_offset); - // Use a memcpy to reinterpret 'void* addr' as 'TYPE* ptr'. - // This is done without using a static_cast, as Clang's Control Flow Integrity checks can - // trigger for this cast, as we're casting from uint8_t* to TYPE*. - // See: crbug.com/dawn/1406 - // See: https://clang.llvm.org/docs/ControlFlowIntegrity.html#bad-cast-checking - TYPE* ptr; - memcpy(&ptr, &addr, sizeof(addr)); + auto* ptr = utils::Bitcast(base + block_.current_offset); block_.current_offset += sizeof(TYPE); return ptr; } diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn index 3b9379f576..811127340b 100644 --- a/test/tint/BUILD.gn +++ b/test/tint/BUILD.gn @@ -357,6 +357,7 @@ tint_unittests_source_set("tint_unittests_transform_src") { tint_unittests_source_set("tint_unittests_utils_src") { sources = [ + "../../src/tint/utils/bitcast_test.cc", "../../src/tint/utils/crc32_test.cc", "../../src/tint/utils/defer_test.cc", "../../src/tint/utils/enum_set_test.cc",