Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bazel/cc_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def main():
# See https://github.com/envoyproxy/envoy/issues/1341
argv.append("-fno-limit-debug-info")
argv.append("-Wthread-safety")
argv.append("-Wgnu-conditional-omitted-operand")
elif "gcc" in compiler or "g++" in compiler:
# -Wmaybe-initialized is warning about many uses of absl::optional. Disable
# to prevent build breakage. This option does not exist in clang, so setting
Expand Down
1 change: 1 addition & 0 deletions include/envoy/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ envoy_basic_cc_library(
name = "base_includes",
hdrs = [
"exception.h",
"platform.h",
"pure.h",
],
include_prefix = "envoy/common",
Expand Down
19 changes: 19 additions & 0 deletions include/envoy/common/platform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

// NOLINT(namespace-envoy)
#if !defined(_MSC_VER)
#define STACK_ALLOC_ARRAY(var, type, num) type var[num]

#define PACKED_STRUCT(definition, ...) definition, ##__VA_ARGS__ __attribute__((packed))

#else
#include <malloc.h>

#define STACK_ALLOC_ARRAY(var, type, num) \
type* var = static_cast<type*>(::_alloca(sizeof(type) * num))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed this...does this actually work? What if 'type' has a constructor; it doesn't look like it would be called and you'd get uninitialized memory.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could loop over the array and used placed new, FWIW. You'd also need to take care of destructors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmarantz that's a good point -- the constructors/destructors definitely won't get called.

It works for now since the macro is only used with:

  • Buffer::RawSlice
  • struct iovec
  • uint64_t
  • unsigned char

and none of these types have constructors/destructors that do anything interesting. In the Buffer::RawSlice + struct iovec case, it looks like we always manually initialize the array (either through getRawSlices or looping over the array).

The best way to fix this probably depends on:

  • how important it is that these arrays are on the stack
  • does the solution need to be fully general or could we restrict it to the types above

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch @jmarantz. @sesmith177 we definitely need to fix this somehow as it will produce a bunch of annoying and hard to find bugs. One option that comes to mind is having some wrapper RAII template object that does placement new and then would automatically do placement delete on scope exit?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an aside, could we just say that users have to compile Envoy with clang on windows? I'm wondering if that would also be a way to "fix" these problems?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative here is to just edit the code-sites to just not use stack allocation. Is there a perf win from stack in this case? My thinking in general has been that if you want to have a lot of threads, then you don't want to allocate huge amounts of stack space to them, so you might not want to allocate potentially large things on the stack.

If that sounds right, I'd seal the deal by throwing a compile switch in clang to make that invalid, if such a switch exists (IIRC g++ has such a switch).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A third option might be to use something like:

static_assert(std::is_trivially_destructible<type>::value, #type " must be trivially destructible");

in the macro/wrapper class to ensure that type has a trivial destructor and doesn't need to be deleted.

We're happy to go in any of the above directions.

It would be a significant effort to require clang on Windows. The tricky part would be ensuring that all third-party dependencies can be build with clang on Windows as well (i.e. do not require MSVC). When initially starting on this project, we made an attempt to compile everything with gcc/mingw, unfortunately there were dependencies that would not build on Windows with that toolchain.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattklein123 @jmarantz We have a branch that replaces variable length arrays with std::vector (and adds -Wvla to ensure variable length arrays are invalid) here: https://github.com/greenhouse-org/envoy/commit/47c48a48f4ea71c00eb714674094bbd6d3d2a4c1

If it's decided that these arrays must be allocated on the stack, we will work on a wrapper template object and PR that instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not in favor of switching to std::vector as I don't think we should take a perf hit due to a platform issue. If it's not too bad, I think I would go with the wrapper template. I don't think it will be that hard so can we try it first?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, we'll get that PR'd


#define PACKED_STRUCT(definition, ...) \
__pragma(pack(push, 1)) definition, ##__VA_ARGS__; \
__pragma(pack(pop))

#endif
19 changes: 12 additions & 7 deletions source/common/buffer/buffer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <cstdint>
#include <string>

#include "envoy/common/platform.h"

#include "common/api/os_sys_calls_impl.h"
#include "common/common/assert.h"

Expand Down Expand Up @@ -34,9 +36,10 @@ void OwnedImpl::add(const std::string& data) {

void OwnedImpl::add(const Instance& data) {
uint64_t num_slices = data.getRawSlices(nullptr, 0);
RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, RawSlice, num_slices);
data.getRawSlices(slices, num_slices);
for (RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
RawSlice& slice = slices[i];
add(slice.mem_, slice.len_);
}
}
Expand Down Expand Up @@ -113,7 +116,7 @@ Api::SysCallIntResult OwnedImpl::read(int fd, uint64_t max_length) {
constexpr uint64_t MaxSlices = 2;
RawSlice slices[MaxSlices];
const uint64_t num_slices = reserve(max_length, slices, MaxSlices);
struct iovec iov[num_slices];
STACK_ALLOC_ARRAY(iov, iovec, num_slices);
uint64_t num_slices_to_read = 0;
uint64_t num_bytes_to_read = 0;
for (; num_slices_to_read < num_slices && num_bytes_to_read < max_length; num_slices_to_read++) {
Expand Down Expand Up @@ -168,7 +171,7 @@ Api::SysCallIntResult OwnedImpl::write(int fd) {
constexpr uint64_t MaxSlices = 16;
RawSlice slices[MaxSlices];
const uint64_t num_slices = std::min(getRawSlices(slices, MaxSlices), MaxSlices);
struct iovec iov[num_slices];
STACK_ALLOC_ARRAY(iov, iovec, num_slices);
uint64_t num_slices_to_write = 0;
for (uint64_t i = 0; i < num_slices; i++) {
if (slices[i].mem_ != nullptr && slices[i].len_ != 0) {
Expand Down Expand Up @@ -198,15 +201,17 @@ OwnedImpl::OwnedImpl(const void* data, uint64_t size) : OwnedImpl() { add(data,

std::string OwnedImpl::toString() const {
uint64_t num_slices = getRawSlices(nullptr, 0);
RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, RawSlice, num_slices);
getRawSlices(slices, num_slices);
size_t len = 0;
for (RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
RawSlice& slice = slices[i];
len += slice.len_;
}
std::string output;
output.reserve(len);
for (RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
RawSlice& slice = slices[i];
output.append(static_cast<const char*>(slice.mem_), slice.len_);
}

Expand Down
7 changes: 5 additions & 2 deletions source/common/common/base64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <cstdint>
#include <string>

#include "envoy/common/platform.h"

#include "common/common/empty_string.h"

namespace Envoy {
Expand Down Expand Up @@ -178,12 +180,13 @@ std::string Base64::encode(const Buffer::Instance& buffer, uint64_t length) {
ret.reserve(output_length);

uint64_t num_slices = buffer.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
buffer.getRawSlices(slices, num_slices);

uint64_t j = 0;
uint8_t next_c = 0;
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& slice = slices[i];
const uint8_t* slice_mem = static_cast<const uint8_t*>(slice.mem_);

for (uint64_t i = 0; i < slice.len_ && j < length; ++i, ++j) {
Expand Down
6 changes: 4 additions & 2 deletions source/common/compressor/zlib_compressor_impl.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "common/compressor/zlib_compressor_impl.h"

#include "envoy/common/exception.h"
#include "envoy/common/platform.h"

#include "common/common/assert.h"

Expand Down Expand Up @@ -35,10 +36,11 @@ uint64_t ZlibCompressorImpl::checksum() { return zstream_ptr_->adler; }

void ZlibCompressorImpl::compress(Buffer::Instance& buffer, State state) {
const uint64_t num_slices = buffer.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
buffer.getRawSlices(slices, num_slices);

for (const Buffer::RawSlice& input_slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
const Buffer::RawSlice& input_slice = slices[i];
zstream_ptr_->avail_in = input_slice.len_;
zstream_ptr_->next_in = static_cast<Bytef*>(input_slice.mem_);
// Z_NO_FLUSH tells the compressor to take the data in and compresses it as much as possible
Expand Down
6 changes: 4 additions & 2 deletions source/common/decompressor/zlib_decompressor_impl.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "common/decompressor/zlib_decompressor_impl.h"

#include "envoy/common/exception.h"
#include "envoy/common/platform.h"

#include "common/common/assert.h"

Expand Down Expand Up @@ -34,10 +35,11 @@ uint64_t ZlibDecompressorImpl::checksum() { return zstream_ptr_->adler; }
void ZlibDecompressorImpl::decompress(const Buffer::Instance& input_buffer,
Buffer::Instance& output_buffer) {
const uint64_t num_slices = input_buffer.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
input_buffer.getRawSlices(slices, num_slices);

for (const Buffer::RawSlice& input_slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& input_slice = slices[i];
zstream_ptr_->avail_in = input_slice.len_;
zstream_ptr_->next_in = static_cast<Bytef*>(input_slice.mem_);
while (inflateNext()) {
Expand Down
6 changes: 4 additions & 2 deletions source/common/filesystem/filesystem_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <string>

#include "envoy/common/exception.h"
#include "envoy/common/platform.h"
#include "envoy/common/time.h"
#include "envoy/event/dispatcher.h"

Expand Down Expand Up @@ -141,7 +142,7 @@ FileImpl::~FileImpl() {

void FileImpl::doWrite(Buffer::Instance& buffer) {
uint64_t num_slices = buffer.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
buffer.getRawSlices(slices, num_slices);

// We must do the actual writes to disk under lock, so that we don't intermix chunks from
Expand All @@ -154,7 +155,8 @@ void FileImpl::doWrite(Buffer::Instance& buffer) {
// process lock or had multiple locks.
{
Thread::LockGuard lock(file_lock_);
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& slice = slices[i];
const Api::SysCallSizeResult result = os_sys_calls_.write(fd_, slice.mem_, slice.len_);
ASSERT(result.rc_ == static_cast<ssize_t>(slice.len_));
stats_.write_completed_.inc();
Expand Down
7 changes: 5 additions & 2 deletions source/common/grpc/codec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <cstdint>
#include <vector>

#include "envoy/common/platform.h"

#include "common/buffer/buffer_impl.h"

namespace Envoy {
Expand All @@ -23,9 +25,10 @@ Decoder::Decoder() : state_(State::FH_FLAG) {}

bool Decoder::decode(Buffer::Instance& input, std::vector<Frame>& output) {
uint64_t count = input.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[count];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, count);
input.getRawSlices(slices, count);
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < count; i++) {
Buffer::RawSlice& slice = slices[i];
uint8_t* mem = reinterpret_cast<uint8_t*>(slice.mem_);
for (uint64_t j = 0; j < slice.len_;) {
uint8_t c = *mem;
Expand Down
6 changes: 3 additions & 3 deletions source/common/grpc/google_grpc_creds_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ std::shared_ptr<grpc::ChannelCredentials> CredsUtility::getChannelCredentials(
case envoy::api::v2::core::GrpcService::GoogleGrpc::ChannelCredentials::kSslCredentials: {
const auto& ssl_credentials = google_grpc.channel_credentials().ssl_credentials();
const grpc::SslCredentialsOptions ssl_credentials_options = {
.pem_root_certs = Config::DataSource::read(ssl_credentials.root_certs(), true),
.pem_private_key = Config::DataSource::read(ssl_credentials.private_key(), true),
.pem_cert_chain = Config::DataSource::read(ssl_credentials.cert_chain(), true),
Config::DataSource::read(ssl_credentials.root_certs(), true),
Config::DataSource::read(ssl_credentials.private_key(), true),
Config::DataSource::read(ssl_credentials.cert_chain(), true),
};
return grpc::SslCredentials(ssl_credentials_options);
}
Expand Down
11 changes: 7 additions & 4 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string>

#include "envoy/buffer/buffer.h"
#include "envoy/common/platform.h"
#include "envoy/http/header_map.h"
#include "envoy/network/connection.h"

Expand Down Expand Up @@ -331,9 +332,10 @@ bool ConnectionImpl::maybeDirectDispatch(Buffer::Instance& data) {

ssize_t total_parsed = 0;
uint64_t num_slices = data.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
data.getRawSlices(slices, num_slices);
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& slice = slices[i];
total_parsed += slice.len_;
onBody(static_cast<const char*>(slice.mem_), slice.len_);
}
Expand All @@ -355,9 +357,10 @@ void ConnectionImpl::dispatch(Buffer::Instance& data) {
ssize_t total_parsed = 0;
if (data.length() > 0) {
uint64_t num_slices = data.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
data.getRawSlices(slices, num_slices);
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& slice = slices[i];
total_parsed += dispatchSlice(static_cast<const char*>(slice.mem_), slice.len_);
}
} else {
Expand Down
6 changes: 4 additions & 2 deletions source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <memory>
#include <vector>

#include "envoy/common/platform.h"
#include "envoy/event/dispatcher.h"
#include "envoy/http/codes.h"
#include "envoy/http/header_map.h"
Expand Down Expand Up @@ -288,9 +289,10 @@ ConnectionImpl::~ConnectionImpl() { nghttp2_session_del(session_); }
void ConnectionImpl::dispatch(Buffer::Instance& data) {
ENVOY_CONN_LOG(trace, "dispatching {} bytes", connection_, data.length());
uint64_t num_slices = data.getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
data.getRawSlices(slices, num_slices);
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& slice = slices[i];
dispatching_ = true;
ssize_t rc =
nghttp2_session_mem_recv(session_, static_cast<const uint8_t*>(slice.mem_), slice.len_);
Expand Down
7 changes: 5 additions & 2 deletions source/common/http/message_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
#include <cstdint>
#include <string>

#include "envoy/common/platform.h"

namespace Envoy {
namespace Http {

std::string MessageImpl::bodyAsString() const {
std::string ret;
if (body_) {
uint64_t num_slices = body_->getRawSlices(nullptr, 0);
Buffer::RawSlice slices[num_slices];
STACK_ALLOC_ARRAY(slices, Buffer::RawSlice, num_slices);
body_->getRawSlices(slices, num_slices);
for (Buffer::RawSlice& slice : slices) {
for (uint64_t i = 0; i < num_slices; i++) {
Buffer::RawSlice& slice = slices[i];
ret.append(reinterpret_cast<const char*>(slice.mem_), slice.len_);
}
}
Expand Down
13 changes: 5 additions & 8 deletions source/common/network/address_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,12 @@ Address::InstanceConstSharedPtr addressFromSockAddr(const sockaddr_storage& ss,
const struct sockaddr_in6* sin6 = reinterpret_cast<const struct sockaddr_in6*>(&ss);
ASSERT(AF_INET6 == sin6->sin6_family);
if (!v6only && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
#ifndef s6_addr32
#ifdef __APPLE__
#define s6_addr32 __u6_addr.__u6_addr32
#endif
#if defined(__APPLE__)
struct sockaddr_in sin = {
{}, AF_INET, sin6->sin6_port, {sin6->sin6_addr.__u6_addr.__u6_addr32[3]}, {}};
#else
struct sockaddr_in sin = {AF_INET, sin6->sin6_port, {sin6->sin6_addr.s6_addr32[3]}, {}};
#endif
struct sockaddr_in sin = {.sin_family = AF_INET,
.sin_port = sin6->sin6_port,
.sin_addr = {.s_addr = sin6->sin6_addr.s6_addr32[3]},
.sin_zero = {}};
return std::make_shared<Address::Ipv4Instance>(&sin);
} else {
return std::make_shared<Address::Ipv6Instance>(*sin6, v6only);
Expand Down
3 changes: 1 addition & 2 deletions source/common/stats/symbol_table_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ Symbol SymbolTableImpl::toSymbol(absl::string_view sv) {
auto decode_insert = decode_map_.insert({next_symbol_, std::move(str)});
ASSERT(decode_insert.second);

auto encode_insert = encode_map_.insert(
{decode_insert.first->second, {.symbol_ = next_symbol_, .ref_count_ = 1}});
auto encode_insert = encode_map_.insert({decode_insert.first->second, {next_symbol_, 1}});
ASSERT(encode_insert.second);

result = next_symbol_;
Expand Down
2 changes: 1 addition & 1 deletion source/common/stats/tag_producer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ TagProducerImpl::TagProducerImpl(const envoy::config::metrics::v2::StatsConfig&
}
} else if (tag_specifier.tag_value_case() ==
envoy::config::metrics::v2::TagSpecifier::kFixedValue) {
default_tags_.emplace_back(Stats::Tag{.name_ = name, .value_ = tag_specifier.fixed_value()});
default_tags_.emplace_back(Stats::Tag{name, tag_specifier.fixed_value()});
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions source/common/upstream/load_balancer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string>
#include <vector>

#include "envoy/common/platform.h"
#include "envoy/runtime/runtime.h"
#include "envoy/upstream/upstream.h"

Expand Down Expand Up @@ -193,9 +194,9 @@ void ZoneAwareLoadBalancerBase::regenerateLocalityRoutingStructures() {
//
// Basically, fariness across localities within a priority is guaranteed. Fairness across
// localities across priorities is not.
uint64_t local_percentage[num_localities];
STACK_ALLOC_ARRAY(local_percentage, uint64_t, num_localities);
calculateLocalityPercentage(localHostSet().healthyHostsPerLocality(), local_percentage);
uint64_t upstream_percentage[num_localities];
STACK_ALLOC_ARRAY(upstream_percentage, uint64_t, num_localities);
calculateLocalityPercentage(host_set.healthyHostsPerLocality(), upstream_percentage);

// If we have lower percent of hosts in the local cluster in the same locality,
Expand Down
6 changes: 3 additions & 3 deletions source/extensions/filters/http/buffer/buffer_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ void BufferFilter::initConfig() {
const std::string& name = HttpFilterNames::get().Buffer;
const auto* entry = callbacks_->route()->routeEntry();

const BufferFilterSettings* tmp = entry->perFilterConfigTyped<BufferFilterSettings>(name);
const BufferFilterSettings* route_local =
entry->perFilterConfigTyped<BufferFilterSettings>(name)
?: entry->virtualHost().perFilterConfigTyped<BufferFilterSettings>(name);
tmp ? tmp : entry->virtualHost().perFilterConfigTyped<BufferFilterSettings>(name);

settings_ = route_local ?: settings_;
settings_ = route_local ? route_local : settings_;
}

Http::FilterHeadersStatus BufferFilter::decodeHeaders(Http::HeaderMap&, bool end_stream) {
Expand Down
Loading