0

Add limitations to bit_cast

Using bit_cast on pointers does not accomplish anything useful, and
obscures what might be incorrect behavior. Do not allow base::bit_cast
to be misused in that way.

Bug: 1506769
Change-Id: Ib9f3acf954c5537c7520f72827854de81afda382
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5094596
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Reviewed-by: Ilya Sherman <isherman@chromium.org>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Dominic Battre <battre@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#1236404}
This commit is contained in:
Avi Drissman
2023-12-12 17:44:37 +00:00
committed by Chromium LUCI CQ
parent 3e6bea0754
commit 70cb7f749d
13 changed files with 97 additions and 72 deletions

@ -1189,6 +1189,15 @@ _BANNED_CPP_FUNCTIONS : Sequence[BanRule] = (
True,
[_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
),
BanRule(
r'/\bstd::bit_cast\b',
(
'std::bit_cast is banned; use base::bit_cast instead for values and '
'standard C++ casting when pointers are involved.',
),
True,
[_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders.
),
BanRule(
r'/\bstd::(c8rtomb|mbrtoc8)\b',
(

@ -7,40 +7,37 @@
#include <type_traits>
#include "base/compiler_specific.h"
#if !HAS_BUILTIN(__builtin_bit_cast)
#include <string.h> // memcpy
#endif
namespace base {
// This is C++20's std::bit_cast<>(). It morally does what
// `*reinterpret_cast<Dest*>(&source)` does, but the cast/deref pair is
// undefined behavior, while bit_cast<>() isn't.
template <class Dest, class Source>
#if HAS_BUILTIN(__builtin_bit_cast)
constexpr
#else
inline
#endif
Dest
bit_cast(const Source& source) {
#if HAS_BUILTIN(__builtin_bit_cast)
// TODO(thakis): Keep only this codepath once nacl is gone or updated.
return __builtin_bit_cast(Dest, source);
#else
static_assert(sizeof(Dest) == sizeof(Source),
"bit_cast requires source and destination to be the same size");
static_assert(std::is_trivially_copyable_v<Dest>,
"bit_cast requires the destination type to be copyable");
static_assert(std::is_trivially_copyable_v<Source>,
"bit_cast requires the source type to be copyable");
// This is an equivalent to C++20's std::bit_cast<>(), but with additional
// warnings. It morally does what `*reinterpret_cast<Dest*>(&source)` does, but
// the cast/deref pair is undefined behavior, while bit_cast<>() isn't.
//
// This is not a magic "get out of UB free" card. This must only be used on
// values, not on references or pointers. For pointers, use
// reinterpret_cast<>(), and then look at https://eel.is/c++draft/basic.lval#11
// as that's probably UB also.
Dest dest;
memcpy(&dest, &source, sizeof(dest));
return dest;
#endif
template <class Dest, class Source>
constexpr Dest bit_cast(const Source& source) {
static_assert(!std::is_pointer_v<Source>,
"bit_cast must not be used on pointer types");
static_assert(!std::is_pointer_v<Dest>,
"bit_cast must not be used on pointer types");
static_assert(!std::is_reference_v<Source>,
"bit_cast must not be used on reference types");
static_assert(!std::is_reference_v<Dest>,
"bit_cast must not be used on reference types");
static_assert(
sizeof(Dest) == sizeof(Source),
"bit_cast requires source and destination types to be the same size");
static_assert(std::is_trivially_copyable_v<Source>,
"bit_cast requires the source type to be trivially copyable");
static_assert(
std::is_trivially_copyable_v<Dest>,
"bit_cast requires the destination type to be trivially copyable");
return __builtin_bit_cast(Dest, source);
}
} // namespace base

@ -6,7 +6,6 @@
#include "net/filter/brotli_source_stream.h"
#include "base/bit_cast.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
@ -108,9 +107,9 @@ class BrotliSourceStream : public FilterSourceStream {
if (decoding_status_ != DecodingStatus::DECODING_IN_PROGRESS)
return base::unexpected(ERR_CONTENT_DECODING_FAILED);
const uint8_t* next_in = base::bit_cast<uint8_t*>(input_buffer->data());
const uint8_t* next_in = reinterpret_cast<uint8_t*>(input_buffer->data());
size_t available_in = input_buffer_size;
uint8_t* next_out = base::bit_cast<uint8_t*>(output_buffer->data());
uint8_t* next_out = reinterpret_cast<uint8_t*>(output_buffer->data());
size_t available_out = output_buffer_size;
BrotliDecoderResult result =

@ -6,7 +6,6 @@
#include <cstring>
#include "base/bit_cast.h"
#include "base/check_op.h"
#include "third_party/zlib/zlib.h"
@ -52,9 +51,9 @@ void CompressGzip(const char* source,
dest_left -= sizeof(gzip_header);
}
zlib_stream.next_in = base::bit_cast<Bytef*>(source);
zlib_stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(source));
zlib_stream.avail_in = source_len;
zlib_stream.next_out = base::bit_cast<Bytef*>(dest);
zlib_stream.next_out = reinterpret_cast<Bytef*>(dest);
zlib_stream.avail_out = dest_left;
code = deflate(&zlib_stream, Z_FINISH);

@ -8,7 +8,6 @@
#include <memory>
#include <utility>
#include "base/bit_cast.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
@ -133,10 +132,10 @@ base::expected<size_t, Error> GzipSourceStream::FilterData(
case STATE_SNIFFING_DEFLATE_HEADER: {
DCHECK_EQ(TYPE_DEFLATE, type());
zlib_stream_.get()->next_in = base::bit_cast<Bytef*>(input_data);
zlib_stream_.get()->next_in = reinterpret_cast<Bytef*>(input_data);
zlib_stream_.get()->avail_in = input_data_size;
zlib_stream_.get()->next_out =
base::bit_cast<Bytef*>(output_buffer->data());
reinterpret_cast<Bytef*>(output_buffer->data());
zlib_stream_.get()->avail_out = output_buffer_size;
int ret = inflate(zlib_stream_.get(), Z_NO_FLUSH);
@ -211,10 +210,10 @@ base::expected<size_t, Error> GzipSourceStream::FilterData(
DCHECK(!state_compressed_entered);
state_compressed_entered = true;
zlib_stream_.get()->next_in = base::bit_cast<Bytef*>(input_data);
zlib_stream_.get()->next_in = reinterpret_cast<Bytef*>(input_data);
zlib_stream_.get()->avail_in = input_data_size;
zlib_stream_.get()->next_out =
base::bit_cast<Bytef*>(output_buffer->data());
reinterpret_cast<Bytef*>(output_buffer->data());
zlib_stream_.get()->avail_out = output_buffer_size;
int ret = inflate(zlib_stream_.get(), Z_NO_FLUSH);
@ -256,9 +255,9 @@ bool GzipSourceStream::InsertZlibHeader() {
char dummy_output[4];
inflateReset(zlib_stream_.get());
zlib_stream_.get()->next_in = base::bit_cast<Bytef*>(&dummy_header[0]);
zlib_stream_.get()->next_in = reinterpret_cast<Bytef*>(&dummy_header[0]);
zlib_stream_.get()->avail_in = sizeof(dummy_header);
zlib_stream_.get()->next_out = base::bit_cast<Bytef*>(&dummy_output[0]);
zlib_stream_.get()->next_out = reinterpret_cast<Bytef*>(&dummy_output[0]);
zlib_stream_.get()->avail_out = sizeof(dummy_output);
int ret = inflate(zlib_stream_.get(), Z_NO_FLUSH);

@ -12,8 +12,6 @@
#include <memory>
#include "base/bit_cast.h"
namespace {
#pragma pack(push, 1)
@ -233,7 +231,7 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
intercepted_code.service_id = full_local_thunk->original.service_id;
intercepted_code.mov_edx = kMovEdx;
intercepted_code.mov_edx_param =
base::bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
reinterpret_cast<ULONG>(&full_remote_thunk->internal_thunk);
intercepted_code.call_edx = kJmpEdx;
bytes_to_write = kMinServiceSize;
@ -291,8 +289,8 @@ bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
ULONG relative = function_code.service_id;
// First, fix our copy of their patch.
relative +=
base::bit_cast<ULONG>(target_) - base::bit_cast<ULONG>(remote_thunk);
relative += reinterpret_cast<ULONG>(target_) -
reinterpret_cast<ULONG>(remote_thunk);
function_code.service_id = relative;
@ -302,8 +300,8 @@ bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
const ULONG kJmp32Size = 5;
relative_jump_ = base::bit_cast<ULONG>(&full_thunk->internal_thunk) -
base::bit_cast<ULONG>(target_) - kJmp32Size;
relative_jump_ = reinterpret_cast<ULONG>(&full_thunk->internal_thunk) -
reinterpret_cast<ULONG>(target_) - kJmp32Size;
}
// Save the verified code
@ -320,8 +318,8 @@ bool ServiceResolverThunk::VerifyJumpTargetForTesting(
return false;
}
ULONG source_addr = base::bit_cast<ULONG>(target_);
ULONG target_addr = base::bit_cast<ULONG>(thunk_storage);
ULONG source_addr = reinterpret_cast<ULONG>(target_);
ULONG target_addr = reinterpret_cast<ULONG>(thunk_storage);
return target_addr + kMaxServiceSize - kJmp32Size - source_addr ==
patched->service_id;
}

@ -4,7 +4,6 @@
#include "services/data_decoder/gzipper.h"
#include "base/bit_cast.h"
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "mojo/public/cpp/base/big_buffer.h"
@ -29,7 +28,7 @@ void Gzipper::Deflate(mojo_base::BigBuffer data, DeflateCallback callback) {
std::vector<uint8_t> compressed_data(compressed_data_size);
if (zlib_internal::CompressHelper(
zlib_internal::ZRAW, compressed_data.data(), &compressed_data_size,
base::bit_cast<const Bytef*>(data.data()), data.size(),
reinterpret_cast<const Bytef*>(data.data()), data.size(),
Z_DEFAULT_COMPRESSION,
/*malloc_fn=*/nullptr, /*free_fn=*/nullptr) != Z_OK) {
std::move(callback).Run(absl::nullopt);

@ -8,7 +8,6 @@
#include <limits>
#include <string_view>
#include "base/bit_cast.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/field_trial_params.h"
@ -137,7 +136,7 @@ bool CheckCrossOriginReadBlocking(const ResourceRequest& resource_request,
const size_t size =
std::min(static_cast<size_t>(net::kMaxBytesToSniff), content.size());
decision = analyzer->Sniff(
std::string_view(base::bit_cast<const char*>(content.front()), size));
std::string_view(reinterpret_cast<const char*>(content.front()), size));
if (decision == corb::ResponseAnalyzer::Decision::kSniffMore)
decision = analyzer->HandleEndOfSniffableResponseBody();
DCHECK_NE(decision, corb::ResponseAnalyzer::Decision::kSniffMore);

@ -4,7 +4,6 @@
#include "services/network/network_service_memory_cache_url_loader.h"
#include "base/bit_cast.h"
#include "base/task/sequenced_task_runner.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
@ -194,8 +193,8 @@ void NetworkServiceMemoryCacheURLLoader::WriteMore() {
if (net_log_.IsCapturing()) {
net_log_.AddByteTransferEvent(
net::NetLogEventType::IN_MEMORY_CACHE_BYTES_READ, total_write_size,
base::bit_cast<const char*>(content_->data().data() +
original_write_position));
reinterpret_cast<const char*>(content_->data().data() +
original_write_position));
}
if (write_completed) {

@ -2196,6 +2196,32 @@ to `absl::bind_front`.
Overlaps with `base::Bind`.
***
### std::bit_cast <sup>[banned]</sup>
```c++
float quake_rsqrt(float number) {
long i = std::bit_cast<long>(number);
i = 0x5f3759df - (i >> 1); // wtf?
float y = std::bit_cast<float>(i);
return y * (1.5f - (0.5f * number * y * y));
}
```
**Description:** Returns an value constructed with the same bits as an value of
a different type.
**Documentation:**
[`std::bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast)
**Notes:**
*** promo
The `std::` version of `bit_cast` allows casting of pointer and reference types,
which is both useless in that it doesn't avoid UB, and dangerous in that it
allows arbitrary casting away of modifiers like `const`. Instead of using
`bit_cast` on pointers, use standard C++ casts. For use on values, use
`base::bit_cast` which does not allow this unwanted usage.
***
### std::{c8rtomb,mbrtoc8} <sup>[banned]</sup>
```c++

@ -127,7 +127,7 @@ bool FloatEqualForHash(T a, T b) {
template <typename T>
unsigned HashPointer(T* key) {
return HashInt(base::bit_cast<internal::IntHashBits<T*>>(key));
return HashInt(reinterpret_cast<internal::IntHashBits<T*>>(key));
}
// Useful compounding hash functions.

@ -21,6 +21,7 @@
#include <limits>
#include "base/bit_cast.h"
#include "base/compiler_specific.h"
#include "build/build_config.h"
#include "third_party/fdlibm/overflowing-math.h"

@ -4,7 +4,6 @@
#include "third_party/zlib/google/compression_utils.h"
#include "base/bit_cast.h"
#include "base/check_op.h"
#include "base/process/memory.h"
#include "base/sys_byteorder.h"
@ -24,8 +23,8 @@ bool GzipCompress(base::span<const char> input,
// uLongf can be larger than size_t.
uLongf compressed_size_long = static_cast<uLongf>(output_buffer_size);
if (zlib_internal::GzipCompressHelper(
base::bit_cast<Bytef*>(output_buffer), &compressed_size_long,
base::bit_cast<const Bytef*>(input.data()),
reinterpret_cast<Bytef*>(output_buffer), &compressed_size_long,
reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size()), malloc_fn, free_fn) != Z_OK) {
return false;
}
@ -55,7 +54,7 @@ bool GzipCompress(base::span<const uint8_t> input, std::string* output) {
if (zlib_internal::GzipCompressHelper(
compressed_data, &compressed_data_size,
base::bit_cast<const Bytef*>(input.data()), input_size, nullptr,
reinterpret_cast<const Bytef*>(input.data()), input_size, nullptr,
nullptr) != Z_OK) {
free(compressed_data);
return false;
@ -82,8 +81,8 @@ bool GzipUncompress(const std::string& input, std::string* output) {
uncompressed_output.resize(uncompressed_size);
if (zlib_internal::GzipUncompressHelper(
base::bit_cast<Bytef*>(uncompressed_output.data()),
&uncompressed_size, base::bit_cast<const Bytef*>(input.data()),
reinterpret_cast<Bytef*>(uncompressed_output.data()),
&uncompressed_size, reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.length())) == Z_OK) {
output->swap(uncompressed_output);
return true;
@ -102,8 +101,8 @@ bool GzipUncompress(base::span<const uint8_t> input,
if (uncompressed_size > output.size())
return false;
return zlib_internal::GzipUncompressHelper(
base::bit_cast<Bytef*>(output.data()), &uncompressed_size,
base::bit_cast<const Bytef*>(input.data()),
reinterpret_cast<Bytef*>(const_cast<uint8_t*>(output.data())),
&uncompressed_size, reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
@ -117,8 +116,8 @@ bool GzipUncompress(base::span<const uint8_t> input, std::string* output) {
uLongf uncompressed_size = GetUncompressedSize(input);
output->resize(uncompressed_size);
return zlib_internal::GzipUncompressHelper(
base::bit_cast<Bytef*>(output->data()), &uncompressed_size,
base::bit_cast<const Bytef*>(input.data()),
reinterpret_cast<Bytef*>(output->data()), &uncompressed_size,
reinterpret_cast<const Bytef*>(input.data()),
static_cast<uLongf>(input.size())) == Z_OK;
}
@ -128,7 +127,8 @@ uint32_t GetUncompressedSize(base::span<const char> compressed_data) {
uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data) {
return zlib_internal::GetGzipUncompressedSize(
base::bit_cast<Bytef*>(compressed_data.data()), compressed_data.size());
reinterpret_cast<const Bytef*>(compressed_data.data()),
compressed_data.size());
}
} // namespace compression