From 8c802aace4076bba2223666fff27373e24519eaf Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 19 Jun 2017 15:56:07 -0700 Subject: [PATCH] Add ReinterpretBytes which does a checked, variable size bit cast This renames and improves the VariableSizeBitCast helper from util/linux/auxiliary_vector.* and moves it to misc. Change-Id: I4bf46f4cfc0e60c900ff9bde467a21ad43c684cd Reviewed-on: https://chromium-review.googlesource.com/534174 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- util/linux/auxiliary_vector.cc | 42 ----------- util/linux/auxiliary_vector.h | 6 +- util/misc/reinterpret_bytes.cc | 70 +++++++++++++++++++ util/misc/reinterpret_bytes.h | 48 +++++++++++++ util/misc/reinterpret_bytes_test.cc | 104 ++++++++++++++++++++++++++++ util/util.gyp | 2 + util/util_test.gyp | 1 + 7 files changed, 227 insertions(+), 46 deletions(-) create mode 100644 util/misc/reinterpret_bytes.cc create mode 100644 util/misc/reinterpret_bytes.h create mode 100644 util/misc/reinterpret_bytes_test.cc diff --git a/util/linux/auxiliary_vector.cc b/util/linux/auxiliary_vector.cc index 8852e614..143ab6e3 100644 --- a/util/linux/auxiliary_vector.cc +++ b/util/linux/auxiliary_vector.cc @@ -16,9 +16,6 @@ #include #include -#include - -#include #include "base/files/file_path.h" #include "base/logging.h" @@ -62,43 +59,4 @@ bool AuxiliaryVector::Read(pid_t pid) { return false; } -// static -bool AuxiliaryVector::VariableSizeBitCast(uint64_t data, - char* dest, - size_t dest_size) { - auto data_p = reinterpret_cast(&data); - constexpr size_t data_size = sizeof(uint64_t); - - // Verify that any unused bytes from data are zero. - // The unused bytes are at the start of the data buffer for big-endian and the - // end of the buffer for little-endian. - if (dest_size < data_size) { - uint64_t zero = 0; - auto extra_bytes = data_p; -#if defined(ARCH_CPU_LITTLE_ENDIAN) - extra_bytes += dest_size; -#endif // ARCH_CPU_LITTLE_ENDIAN - if (memcmp(extra_bytes, &zero, data_size - dest_size) != 0) { - LOG(ERROR) << "information loss"; - return false; - } - } - - // Zero out the destination, in case it is larger than data. - memset(dest, 0, dest_size); - -#if defined(ARCH_CPU_LITTLE_ENDIAN) - // Copy a prefix of data to a prefix of dest for little-endian - memcpy(dest, data_p, std::min(dest_size, data_size)); -#else - // or the suffix of data to the suffix of dest for big-endian - if (data_size >= dest_size) { - memcpy(dest, data_p + data_size - dest_size, dest_size); - } else { - memcpy(dest + dest_size - data_size, data_p, data_size); - } -#endif // ARCH_CPU_LITTLE_ENDIAN - return true; -} - } // namespace crashpad diff --git a/util/linux/auxiliary_vector.h b/util/linux/auxiliary_vector.h index 904eada4..65d0e3cf 100644 --- a/util/linux/auxiliary_vector.h +++ b/util/linux/auxiliary_vector.h @@ -21,6 +21,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "util/misc/reinterpret_bytes.h" namespace crashpad { @@ -55,8 +56,7 @@ class AuxiliaryVector { LOG(ERROR) << "value not found"; return false; } - return VariableSizeBitCast( - iter->second, reinterpret_cast(value), sizeof(V)); + return ReinterpretBytes(iter->second, value); } protected: @@ -66,8 +66,6 @@ class AuxiliaryVector { template bool Read(pid_t pid); - static bool VariableSizeBitCast(uint64_t data, char* dest, size_t dest_size); - DISALLOW_COPY_AND_ASSIGN(AuxiliaryVector); }; diff --git a/util/misc/reinterpret_bytes.cc b/util/misc/reinterpret_bytes.cc new file mode 100644 index 00000000..65ec33f3 --- /dev/null +++ b/util/misc/reinterpret_bytes.cc @@ -0,0 +1,70 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "util/misc/reinterpret_bytes.h" + +#include + +#include + +#include "base/logging.h" + +namespace crashpad { +namespace internal { + +bool ReinterpretBytesImpl(const char* data, + size_t data_size, + char* dest, + size_t dest_size) { + // Verify that any unused bytes from data are zero. + // The unused bytes are at the start of the data buffer for big-endian and the + // end of the buffer for little-endian. + if (dest_size < data_size) { + auto extra_bytes = data; +#if defined(ARCH_CPU_LITTLE_ENDIAN) + extra_bytes += dest_size; +#endif // ARCH_CPU_LITTLE_ENDIAN + + uint64_t zero = 0; + size_t extra_bytes_size = data_size - dest_size; + while (extra_bytes_size > 0) { + size_t to_check = std::min(extra_bytes_size, sizeof(zero)); + if (memcmp(extra_bytes, &zero, to_check) != 0) { + LOG(ERROR) << "information loss"; + return false; + } + extra_bytes += to_check; + extra_bytes_size -= to_check; + } + } + + // Zero out the destination, in case it is larger than data. + memset(dest, 0, dest_size); + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + // Copy a prefix of data to a prefix of dest for little-endian + memcpy(dest, data, std::min(dest_size, data_size)); +#else + // or the suffix of data to the suffix of dest for big-endian + if (data_size >= dest_size) { + memcpy(dest, data + data_size - dest_size, dest_size); + } else { + memcpy(dest + dest_size - data_size, data, data_size); + } +#endif // ARCH_CPU_LITTLE_ENDIAN + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/util/misc/reinterpret_bytes.h b/util/misc/reinterpret_bytes.h new file mode 100644 index 00000000..561180da --- /dev/null +++ b/util/misc/reinterpret_bytes.h @@ -0,0 +1,48 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_ +#define CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_ + +#include + +namespace crashpad { + +namespace internal { + +bool ReinterpretBytesImpl(const char* from, + size_t from_size, + char* to, + size_t to_size); + +} // namespace internal + +//! \brief Copies the bytes of \a from to \to. +//! +//! This function is similar to `bit_cast`, except that it can operate on +//! differently sized types. +//! +//! \return `true` if the copy is possible without information loss, otherwise +//! `false` with a message logged. +template +bool ReinterpretBytes(const From& from, To* to) { + return internal::ReinterpretBytesImpl(reinterpret_cast(&from), + sizeof(From), + reinterpret_cast(to), + sizeof(To)); +} + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_ diff --git a/util/misc/reinterpret_bytes_test.cc b/util/misc/reinterpret_bytes_test.cc new file mode 100644 index 00000000..47badb97 --- /dev/null +++ b/util/misc/reinterpret_bytes_test.cc @@ -0,0 +1,104 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "util/misc/reinterpret_bytes.h" + +#include + +#include + +#include "base/bit_cast.h" +#include "gtest/gtest.h" +#include "util/numeric/int128.h" + +namespace crashpad { +namespace test { +namespace { + +template +void ExpectReinterpret(From from, To* to, To expected) { + ASSERT_TRUE(ReinterpretBytes(from, to)); + EXPECT_EQ(*to, expected); +} + +template +void ExpectUnsignedEqual(From from, To* to) { + To expected = static_cast(from); + ExpectReinterpret(from, to, expected); +} + +TEST(ReinterpretBytes, ToUnsigned) { + uint64_t from64, to64; + uint32_t from32, to32; + + from32 = 0; + ExpectUnsignedEqual(from32, &to32); + ExpectUnsignedEqual(from32, &to64); + + from32 = std::numeric_limits::max(); + ExpectUnsignedEqual(from32, &to32); + ExpectUnsignedEqual(from32, &to64); + + from64 = 0; + ExpectUnsignedEqual(from64, &to32); + ExpectUnsignedEqual(from64, &to64); + + from64 = std::numeric_limits::max(); + ExpectUnsignedEqual(from64, &to64); + EXPECT_FALSE(ReinterpretBytes(from64, &to32)); + + uint8_t to8 = std::numeric_limits::max(); + uint128_struct from128; + from128.lo = to8; + from128.hi = 0; + ExpectReinterpret(from128, &to8, to8); +} + +TEST(ReinterpretBytes, ToSigned) { + uint64_t from64; + int64_t to64; + int32_t to32; + + from64 = 0; + ExpectReinterpret(from64, &to32, static_cast(0)); + ExpectReinterpret(from64, &to64, static_cast(0)); + + to32 = -1; + from64 = bit_cast(to32); + ExpectReinterpret(from64, &to32, to32); + + to32 = std::numeric_limits::max(); + from64 = bit_cast(to32); + ExpectReinterpret(from64, &to32, to32); + + to32 = std::numeric_limits::min(); + from64 = bit_cast(to32); + ExpectReinterpret(from64, &to32, to32); + + to64 = -1; + from64 = bit_cast(to64); + ExpectReinterpret(from64, &to64, to64); + + to64 = std::numeric_limits::max(); + from64 = bit_cast(to64); + ExpectReinterpret(from64, &to64, to64); + + to64 = std::numeric_limits::min(); + from64 = bit_cast(to64); + ExpectReinterpret(from64, &to64, to64); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/util.gyp b/util/util.gyp index 8e5c7cfe..60e41957 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -120,6 +120,8 @@ 'misc/pdb_structures.h', 'misc/random_string.cc', 'misc/random_string.h', + 'misc/reinterpret_bytes.cc', + 'misc/reinterpret_bytes.h', 'misc/scoped_forbid_return.cc', 'misc/scoped_forbid_return.h', 'misc/symbolic_constants_common.h', diff --git a/util/util_test.gyp b/util/util_test.gyp index ef5a681f..1b4cff95 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -71,6 +71,7 @@ 'misc/paths_test.cc', 'misc/scoped_forbid_return_test.cc', 'misc/random_string_test.cc', + 'misc/reinterpret_bytes_test.cc', 'misc/uuid_test.cc', 'net/http_body_gzip_test.cc', 'net/http_body_test.cc',