Skip to content

[DRAFT] Refcount stats across scopes#1

Closed
ambuc wants to merge 26 commits intomasterfrom
refcount-stats-across-scopes
Closed

[DRAFT] Refcount stats across scopes#1
ambuc wants to merge 26 commits intomasterfrom
refcount-stats-across-scopes

Conversation

@ambuc
Copy link
Owner

@ambuc ambuc commented Apr 17, 2018

Draft solution to envoyproxy#2453

ambuc added 6 commits April 11, 2018 18:44
Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
…cyTests

Signed-off-by: James Buckland <jbuckland@google.com>
HeapRawStatDataAllocator still does heap allocation without namespace resolution

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
"//source/common/common:assert_lib",
"//source/common/common:hash_lib",
"//source/common/common:perf_annotation_lib",
"//source/common/common:shared_memory_hash_set_lib",
Copy link

Choose a reason for hiding this comment

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

test comment

Copy link

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

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

Looks awesome - here are some comments to start!

}

RawStatData* BlockRawStatDataAllocator::alloc(const std::string& name) {
absl::string_view key = name;
Copy link

Choose a reason for hiding this comment

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

Why not just use name directly throughout?

Copy link
Owner Author

Choose a reason for hiding this comment

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

name comes in as a std::string& and we want to be able to call things like .remove_suffix() on it, which are absl::string_view features.

Copy link

Choose a reason for hiding this comment

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

ahhh, makes sense. I didn't realize you were substringing it.


RawStatData* alloc(const std::string& name);
void free(RawStatData& data);
std::unique_ptr<RawStatDataSet> stats_set_;
Copy link

Choose a reason for hiding this comment

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

Is there a reason we can't make this variable private?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I think we can! I thought it needed to be used in versionHelper, but I was able to rework that to need only the std::string version number instead of the whole RawStatDataSet itself.

// Stats::RawStatData must be naturally aligned for atomics to work properly.
RELEASE_ASSERT((reinterpret_cast<uintptr_t>(shmem->stats_set_data_) % alignof(RawStatDataSet)) ==
0);
RELEASE_ASSERT(
Copy link

Choose a reason for hiding this comment

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

What changed here?

Copy link
Owner Author

Choose a reason for hiding this comment

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

RawStatDataSet -> Stats::RawStatDataSet, which pushed it over some character limit and reflowed the formatting.

Copy link

Choose a reason for hiding this comment

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

Ah, feel free to disregard.

shmem_(SharedMemory::initialize(RawStatDataSet::numBytes(stats_set_options_), options)),
shmem_(
SharedMemory::initialize(Stats::RawStatDataSet::numBytes(stats_set_options_), options)),
allocator_(stats_set_options_, options.restartEpoch() == 0, shmem_.stats_set_data_),
Copy link

Choose a reason for hiding this comment

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

You will probably need to initialize this below inside the scope where lock is created, which is where the stats set used to be initialized. allocator_ will probably need to be a unique_ptr as a result. This is because the lock protects the initialization. If you move it above to the initializer list, the lock won't be held while it's being constructed. Side note: are you familiar with the idea of a scope lock (std::unique_lock<Thread::BasicLockable>)?

Copy link
Owner Author

Choose a reason for hiding this comment

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

This is a good catch. I reworked allocator_ to be a unique pointer and constructed it not-inline, and changed all references to allocator_.foo to allocator_->foo.

calloc(SharedMemoryHashSet<Stats::RawStatData>::numBytes(options), num_stats));
stats_allocator_ = std::make_unique<Stats::BlockRawStatDataAllocator>(options, true, ptr_);
}
~HotRestartNopImpl() { ::free(ptr_); }
Copy link

Choose a reason for hiding this comment

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

Be careful, here. I'd be sure that that stats_allocator_ doesn't attempt to write to this memory as it's destructing. This is an artifact of the C++ order of destruction where the destructor runs first, and then the member variables are destroyed. If this is a problem, we can modify the destructor to look like this:

~HotRestartNopImpl() { 
stats_allocator_ = nullptr;
::free(ptr_); 
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

I'll do this -- is this the kind of bug I can write a test for? Or is this just intuition that this could fail?

}

TEST(SomeTest, HotAllocConsistencyTest) {
int max_stats_ = 16384; // ENVOY_DEFAULT_MAX_STATS
Copy link

Choose a reason for hiding this comment

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

If you want this to match that number, you should probably use the macro directly.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I'll include server/options_impl.h and get access to it.

SharedMemoryHashSetOptions options_ = Server::sharedMemHashOptions(max_stats_);
uint8_t* ptr = static_cast<uint8_t*>(
calloc(SharedMemoryHashSet<Stats::RawStatData>::numBytes(options_), max_stats_));
std::unique_ptr<RawStatDataSet> stats_set_ =
Copy link

Choose a reason for hiding this comment

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

Since you have your fancy new block allocator, why not use it here? :) . Otherwise the test is really testing a reimplementation of your allocator rather than the allocator itself.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Good catch. Rewrote to look like

TEST(SomeTest, HotAllocConsistencyTest) { 
  int max_stats_ = 16384; // ENVOY_DEFAULT_MAX_STATS

  SharedMemoryHashSetOptions options_ = Server::sharedMemHashOptions(max_stats_);
  uint8_t* ptr = static_cast<uint8_t*>(
      calloc(SharedMemoryHashSet<Stats::RawStatData>::numBytes(options_), max_stats_));

  Stats::BlockRawStatDataAllocator allocator = 
    Stats::BlockRawStatDataAllocator(options_, true, ptr);                                                                                                                                                                      

  auto data_1 = allocator.alloc("foo");
  auto data_2 = allocator.alloc("foo");

  EXPECT_EQ(data_1, data_2);

  ::free(ptr);
}

ambuc added 4 commits April 17, 2018 13:54
Signed-off-by: James Buckland <jbuckland@google.com>
and rework versionHelper to expect a string of the RawStatDataSet version instead of the whole set itself

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
…nter, so that it can be initialized after the locks are held

Signed-off-by: James Buckland <jbuckland@google.com>
"No regex specified for tag specifier and no default regex for name: 'test_extractor'");
}

TEST(SomeTest, HotAllocConsistencyTest) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 I think I don't have a great way to test this twice, once in the context of injecting shared memory, and once in the context of injecting non-shared memory. Is there a test we can write to prove this works in the original worry case in envoyproxy#2453 ?

ambuc added 7 commits April 18, 2018 09:59
Signed-off-by: James Buckland <jbuckland@google.com>
since it is no longer used in shared memory only

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
able to be passed in by reference or left null for allocators which don't need it

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
…matting fixes

Signed-off-by: James Buckland <jbuckland@google.com>
ptr_ = static_cast<uint8_t*>(
calloc(MemoryHashSet<Stats::RawStatData>::numBytes(options), num_stats));
stats_allocator_ =
std::make_unique<Stats::BlockRawStatDataAllocator>(options, true, ptr_, &stat_lock_);
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 when you see new statLock / stat_lock_ objects in HotRestart, HotRestartImpl, HotRestartNopImpl, and HotRestart mock objects, it's because of this line. I wanted to create and use a lock mechanism even when the restart isn't hot.

Copy link

Choose a reason for hiding this comment

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

But why does it need to have its own accessor method?

#include "tclap/CmdLine.h"

// Can be overridden at compile time
#ifndef ENVOY_DEFAULT_MAX_STATS
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 I moved this to options_impl.h for visibility.

)

envoy_cc_library(
name = "shared_memory_hash_set_lib",
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 This is one of a few commits which rename SharedMemoryHashSet to MemoryHashSet, or shared_memory_hash_set to memory_hash_set, or sharedMemOptions to memOptions. I wanted to indicate that the MemoryHashSet doesn't need to be run on shared memory.

return false;
}

RawStatData* BlockRawStatDataAllocator::alloc(const std::string& name) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

I moved a lot of these tests from hot_restart_impl_test.cc since they are really unit tests on the allocator itself. A few stayed in hot_restart_impl_test.cc as they tested behavior across hot restart.

Copy link

Choose a reason for hiding this comment

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

What tests are you referring to? This is an implementation file...?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Wow, I don't know how this ended up here. I think I meant to say I moved these ::alloc and ::free functions from hot_restart_impl.cc.

EXPECT_EQ(stat5, stat5_prime);
}

TEST_F(HotRestartImplTest, truncateKey) {
Copy link
Owner Author

Choose a reason for hiding this comment

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

I moved a lot of these to stats_impl.cc. See the other comment here: https://github.com/ambuc/envoy/pull/1/files#r182548087


Stats::RawStatData* HotRestartImpl::alloc(const std::string& name) {
// Try to find the existing slot in shared memory, otherwise allocate a new one.
std::unique_lock<Thread::BasicLockable> lock(stat_lock_);
Copy link
Owner Author

Choose a reason for hiding this comment

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

The whole functionality of HotRestartImpl::alloc, including the lock, got moved up into BlockRawStatDataAllocator::alloc. Same for ::free().

void HotRestartImpl::shutdown() { socket_event_.reset(); }

std::string HotRestartImpl::version() {
return versionHelper(shmem_.maxStats(), Stats::RawStatData::maxNameLength(), *stats_set_);
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 I wanted BlockRawStatDataAllocator's stats_set_ member variable to be private, so I ended up writing a function here [1] to just fetch the version as a string.

[1] https://github.com/ambuc/envoy/pull/1/files#diff-fb83459430ccfd2f0d934dcfacdbd9d1R399

Copy link

Choose a reason for hiding this comment

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

Seems reasonable to me.

Stats::RawStatDataSet stats_set(options, true /* init */, mem_buffer_for_dry_run_.get());
auto stats_set_version = stats_set.version();

return versionHelper(max_num_stats, max_stat_name_len, stats_set);
Copy link
Owner Author

Choose a reason for hiding this comment

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

See the comment here https://github.com/ambuc/envoy/pull/1/files#r182549180, we can now pass the string version instead of the whole set by reference.

Copy link

Choose a reason for hiding this comment

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

SGTM.

RpcBase* receiveRpc(bool block);
void sendMessage(sockaddr_un& address, RpcBase& rpc);
static std::string versionHelper(uint64_t max_num_stats, uint64_t max_stat_name_len,
RawStatDataSet& stats_set);
Copy link
Owner Author

Choose a reason for hiding this comment

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

public:
HotRestartNopImpl() {}
HotRestartNopImpl() {
int num_stats = 100;
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 TODO(jbuckland) find a less arbitrary number for this. I seem to recall setting this to ENVOY_DEFAULT_MAX_STATS caused testing to hang. What should we do here?

Copy link

Choose a reason for hiding this comment

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

This is not a test, this is the real application, so this must be pulled from the supplied command line options/default just like the hot restart implementation does. I would suggest supplying it via the constructor just like HotRestartImpl:

HotRestartImpl(Options& options);

#include "tclap/CmdLine.h"

// Can be overridden at compile time
#ifndef ENVOY_DEFAULT_MAX_STATS
Copy link
Owner Author

Choose a reason for hiding this comment

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

Moved to options_impl.h for visibility.

Copy link

Choose a reason for hiding this comment

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

Makes sense.

Copy link

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

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

Looks good so far - left a few comments.

* @return BlockRawStatDataAllocator newly constructed BlockRawStatDataAllocator.
*/
BlockRawStatDataAllocator(const MemoryHashSetOptions& options, bool init, uint8_t* memory,
Thread::BasicLockable* stat_lock)
Copy link

Choose a reason for hiding this comment

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

Just a note: if we decide that we want this in all cases, we might consider making this a reference. Or if we want this class to own it, we could pass in a unique_ptr and check that it's not nullptr.

std::unique_lock<Thread::BasicLockable> lock(stat_lock_);
stats_set_.reset(new RawStatDataSet(stats_set_options_, options.restartEpoch() == 0,
shmem_.stats_set_data_));
allocator_ = std::make_unique<Stats::BlockRawStatDataAllocator>(
Copy link

Choose a reason for hiding this comment

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

Why did you get rid of the locking here?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I moved the locking functionality inside the BlockRawStatDataAllocator itself, which is why it gets initialized with a reference to a lock.

ASSERT(key_removed);
memset(&data, 0, Stats::RawStatData::size());
}
void HotRestartImpl::free(Stats::RawStatData& data) { allocator_->free(data); }
Copy link

Choose a reason for hiding this comment

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

Why are we still forwarding these calls?

public:
HotRestartNopImpl() {}
HotRestartNopImpl() {
int num_stats = 100;
Copy link

Choose a reason for hiding this comment

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

This is not a test, this is the real application, so this must be pulled from the supplied command line options/default just like the hot restart implementation does. I would suggest supplying it via the constructor just like HotRestartImpl:

HotRestartImpl(Options& options);

ptr_ = static_cast<uint8_t*>(
calloc(MemoryHashSet<Stats::RawStatData>::numBytes(options), num_stats));
stats_allocator_ =
std::make_unique<Stats::BlockRawStatDataAllocator>(options, true, ptr_, &stat_lock_);
Copy link

Choose a reason for hiding this comment

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

But why does it need to have its own accessor method?

void setup(int num_stats) {
Stats::RawStatData::configureForTestsOnly(options_);
MemoryHashSetOptions memory_hash_set_options_ = Server::memHashOptions(num_stats);
ptr_ = static_cast<uint8_t*>(
Copy link

Choose a reason for hiding this comment

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

We should probably move to the way Josh allocates the memory in his tests - see here. Something like:

uint32_t num_bytes = MemoryHashSet<Stats::RawStatData>::numBytes(memory_hash_set_options_);
ptr_ = new uint8_t[num_bytes];

Side note: why are you multiplying the number of bytes times the number of stats? That may be the source of your issues - you're squaring the capacity.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Great catch! I've updated this.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Just kidding, I updated this for HotRestartNopImpl. I'll update it here too.

Stats::RawStatDataSet stats_set(options, true /* init */, mem_buffer_for_dry_run_.get());
auto stats_set_version = stats_set.version();

return versionHelper(max_num_stats, max_stat_name_len, stats_set);
Copy link

Choose a reason for hiding this comment

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

SGTM.

ambuc added 3 commits April 19, 2018 13:19
…tics

Signed-off-by: James Buckland <jbuckland@google.com>
…ly them with memory, have unit tests, and are not child classes of RawStatDataAllocator

instead, they are called (in a few places) as restart_obj_.statsAllocator().alloc() or restart_obj.statsAllocator().free()

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
#include "common/common/thread.h"
#include "common/stats/stats_impl.h"

#include "server/hot_restart_impl.h"
Copy link

Choose a reason for hiding this comment

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

Why does this include hot_restart_impl.h?

Copy link
Owner Author

Choose a reason for hiding this comment

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

it needs blockMemHashOptions from there.

#include "tclap/CmdLine.h"

// Can be overridden at compile time
#ifndef ENVOY_DEFAULT_MAX_STATS
Copy link

Choose a reason for hiding this comment

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

Makes sense.

stat2 = nullptr;
stat4 = nullptr;

EXPECT_CALL(options_, restartEpoch()).WillRepeatedly(Return(1));
Copy link

Choose a reason for hiding this comment

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

Why is this here and why would the nopimpl call this method?

Copy link
Owner Author

Choose a reason for hiding this comment

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

You're right, this is leftover from the sameAlloc function when it was being called on a more tightly coupled instance of HotRestart.

return false;
}

RawStatData* BlockRawStatDataAllocator::alloc(const std::string& name) {
Copy link

Choose a reason for hiding this comment

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

What tests are you referring to? This is an implementation file...?

void HotRestartImpl::shutdown() { socket_event_.reset(); }

std::string HotRestartImpl::version() {
return versionHelper(shmem_.maxStats(), Stats::RawStatData::maxNameLength(), *stats_set_);
Copy link

Choose a reason for hiding this comment

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

Seems reasonable to me.

ambuc added 6 commits April 20, 2018 14:18
Signed-off-by: James Buckland <jbuckland@google.com>
…instead of taking pointer to it

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
… for proper scope testing

Signed-off-by: James Buckland <jbuckland@google.com>
Signed-off-by: James Buckland <jbuckland@google.com>
* This is a heap test allocator that works similar to how the shared memory allocator works in
* terms of reference counting, etc.
*/
class TestAllocator : public RawStatDataAllocator {
Copy link
Owner Author

Choose a reason for hiding this comment

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

@mrice32 I was able to completely swap in this TestAllocator for a real, functional BlockRawStatDataAllocator, which ends up running the below suite of namespace resolution tests.

@ambuc ambuc closed this Apr 23, 2018
@ambuc
Copy link
Owner Author

ambuc commented Apr 23, 2018

Closed in favor of envoyproxy#3163.

htuch pushed a commit to envoyproxy/envoy that referenced this pull request May 4, 2018
…ot restart (#3249)

Simpler solution to issue #2453 than pull #3163, continuing draft work in ambuc#1 and ambuc#2. Summary of changes:

adds an unordered_map named stats_set_ as a member variable of HeapRawStatDataAllocator, and implements reference counting / dedup on allocated stats.
Risk Level: Low.

Testing: Add a test to stats_impl_test. Passes bazel test test/....

Docs Changes: N/A

Release Notes: This is user-facing in that non-hot restart stat allocation now resolves namespace properly, but no effect on user configs.

Fixes: #2453

Signed-off-by: James Buckland <jbuckland@google.com>
ambuc pushed a commit that referenced this pull request Nov 2, 2018
…ardown. (envoyproxy#4940)

server_fuzz_test indicated the below crash, where the DispatcherImpl teardown
releases some upstream client SSL related objects that then needs
SecretManagerImpl to unregister. Previously, this was already destructed by
time we were in ~DispatcherImpl(), this PR reorders.

    #0 0xc1e826 in size /usr/local/include/c++/v1/__hash_table:809:55
    #1 0xc1e826 in bucket_count /usr/local/include/c++/v1/__hash_table:1197
    #2 0xc1e826 in std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, void*>*> std::__1::__hash_table<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> > > >::find<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) /usr/local/include/c++/v1/__hash_table:2334
    #3 0xc1e278 in unsigned long std::__1::__hash_table<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::weak_ptr<Envoy::Secret::TlsCertificateSdsApi> > > >::__erase_unique<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) /usr/local/include/c++/v1/__hash_table:2510:20
    #4 0xc1def6 in erase /usr/local/include/c++/v1/unordered_map:1156:59
    #5 0xc1def6 in Envoy::Secret::SecretManagerImpl::DynamicSecretProviders<Envoy::Secret::TlsCertificateSdsApi>::removeDynamicSecretProvider(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) /bazel-out/k8-fastbuild/bin/source/common/secret/_virtual_includes/secret_manager_impl_lib/common/secret/secret_manager_impl.h:75
    #6 0x724aa9 in Envoy::Cleanup::~Cleanup() /bazel-out/k8-fastbuild/bin/source/common/common/_virtual_includes/cleanup_lib/common/common/cleanup.h:11:16
    #7 0xc1ff33 in Envoy::Secret::SdsApi::~SdsApi() /bazel-out/k8-fastbuild/bin/source/common/secret/_virtual_includes/sds_api_lib/common/secret/sds_api.h:29:7
    #8 0xc188a0 in __release_shared /usr/local/include/c++/v1/memory:3530:9
    #9 0xc188a0 in __release_shared /usr/local/include/c++/v1/memory:3572
    #10 0xc188a0 in std::__1::shared_ptr<Envoy::Secret::SecretProvider<Envoy::Ssl::TlsCertificateConfig> >::~shared_ptr() /usr/local/include/c++/v1/memory:4508
    #11 0x149c922 in Envoy::Ssl::ContextConfigImpl::~ContextConfigImpl() /source/common/ssl/context_config_impl.cc:117:1
    #12 0x14a0d8f in Envoy::Ssl::ClientContextConfigImpl::~ClientContextConfigImpl() /bazel-out/k8-fastbuild/bin/source/common/ssl/_virtual_includes/context_config_lib/common/ssl/context_config_impl.h:91:7
    #13 0x14a0dc8 in Envoy::Ssl::ClientContextConfigImpl::~ClientContextConfigImpl() /bazel-out/k8-fastbuild/bin/source/common/ssl/_virtual_includes/context_config_lib/common/ssl/context_config_impl.h:91:7
    #14 0x149815b in operator() /usr/local/include/c++/v1/memory:2325:5
    #15 0x149815b in reset /usr/local/include/c++/v1/memory:2638
    #16 0x149815b in ~unique_ptr /usr/local/include/c++/v1/memory:2592
    #17 0x149815b in Envoy::Ssl::ClientSslSocketFactory::~ClientSslSocketFactory() /bazel-out/k8-fastbuild/bin/source/common/ssl/_virtual_includes/ssl_socket_lib/common/ssl/ssl_socket.h:83
    #18 0x14981c8 in Envoy::Ssl::ClientSslSocketFactory::~ClientSslSocketFactory() /bazel-out/k8-fastbuild/bin/source/common/ssl/_virtual_includes/ssl_socket_lib/common/ssl/ssl_socket.h:83:7
    #19 0x1362caf in operator() /usr/local/include/c++/v1/memory:2325:5
    #20 0x1362caf in reset /usr/local/include/c++/v1/memory:2638
    #21 0x1362caf in ~unique_ptr /usr/local/include/c++/v1/memory:2592
    #22 0x1362caf in Envoy::Upstream::ClusterInfoImpl::~ClusterInfoImpl() /bazel-out/k8-fastbuild/bin/source/common/upstream/_virtual_includes/upstream_includes/common/upstream/upstream_impl.h:362
    #23 0x1362d28 in Envoy::Upstream::ClusterInfoImpl::~ClusterInfoImpl() /bazel-out/k8-fastbuild/bin/source/common/upstream/_virtual_includes/upstream_includes/common/upstream/upstream_impl.h:362:7
    #24 0x66e560 in __release_shared /usr/local/include/c++/v1/memory:3530:9
    #25 0x66e560 in __release_shared /usr/local/include/c++/v1/memory:3572
    #26 0x66e560 in std::__1::shared_ptr<Envoy::Upstream::ClusterInfo const>::~shared_ptr() /usr/local/include/c++/v1/memory:4508
    #27 0x13621bf in Envoy::Upstream::HostImpl::~HostImpl() /bazel-out/k8-fastbuild/bin/source/common/upstream/_virtual_includes/upstream_includes/common/upstream/upstream_impl.h:156:7
    #28 0x13621f8 in Envoy::Upstream::HostImpl::~HostImpl() /bazel-out/k8-fastbuild/bin/source/common/upstream/_virtual_includes/upstream_includes/common/upstream/upstream_impl.h:156:7
    #29 0x66e650 in __release_shared /usr/local/include/c++/v1/memory:3530:9
    #30 0x66e650 in __release_shared /usr/local/include/c++/v1/memory:3572
    #31 0x66e650 in std::__1::shared_ptr<Envoy::Upstream::HostDescription const>::~shared_ptr() /usr/local/include/c++/v1/memory:4508
    #32 0x13b20c3 in Envoy::Http::CodecClient::~CodecClient() /source/common/http/codec_client.cc:38:30
    #33 0x13b2258 in Envoy::Http::CodecClientProd::~CodecClientProd() /bazel-out/k8-fastbuild/bin/source/common/http/_virtual_includes/codec_client_lib/common/http/codec_client.h:229:7
    #34 0x751de6 in operator() /usr/local/include/c++/v1/memory:2325:5
    #35 0x751de6 in reset /usr/local/include/c++/v1/memory:2638
    #36 0x751de6 in ~unique_ptr /usr/local/include/c++/v1/memory:2592
    #37 0x751de6 in destroy /usr/local/include/c++/v1/memory:1867
    #38 0x751de6 in __destroy<std::__1::unique_ptr<Envoy::Event::DeferredDeletable, std::__1::default_delete<Envoy::Event::DeferredDeletable> > > /usr/local/include/c++/v1/memory:1729
    #39 0x751de6 in destroy<std::__1::unique_ptr<Envoy::Event::DeferredDeletable, std::__1::default_delete<Envoy::Event::DeferredDeletable> > > /usr/local/include/c++/v1/memory:1597
    #40 0x751de6 in __destruct_at_end /usr/local/include/c++/v1/vector:422
    #41 0x751de6 in clear /usr/local/include/c++/v1/vector:365
    #42 0x751de6 in std::__1::__vector_base<std::__1::unique_ptr<Envoy::Event::DeferredDeletable, std::__1::default_delete<Envoy::Event::DeferredDeletable> >, std::__1::allocator<std::__1::unique_ptr<Envoy::Event::DeferredDeletable, std::__1::default_delete<Envoy::Event::DeferredDeletable> > > >::~__vector_base() /usr/local/include/c++/v1/vector:459
    #43 0x74d1aa in ~vector /usr/local/include/c++/v1/vector:551:5
    #44 0x74d1aa in Envoy::Event::DispatcherImpl::~DispatcherImpl() /source/common/event/dispatcher_impl.cc:41
    #45 0x74d658 in Envoy::Event::DispatcherImpl::~DispatcherImpl() /source/common/event/dispatcher_impl.cc:41:35
    #46 0x697b76 in operator() /usr/local/include/c++/v1/memory:2325:5
    #47 0x697b76 in reset /usr/local/include/c++/v1/memory:2638
    #48 0x697b76 in ~unique_ptr /usr/local/include/c++/v1/memory:2592
    #49 0x697b76 in Envoy::Server::InstanceImpl::InstanceImpl(Envoy::Server::Options&, Envoy::Event::TimeSystem&, std::__1::shared_ptr<Envoy::Network::Address::Instance const>, Envoy::TestHooks&, Envoy::Server::HotRestart&, Envoy::Stats::StoreRoot&, Envoy::Thread::BasicLockable&, Envoy::Server::ComponentFactory&, std::__1::unique_ptr<Envoy::Runtime::RandomGenerator, std::__1::default_delete<Envoy::Runtime::RandomGenerator> >&&, Envoy::ThreadLocal::Instance&) /source/server/server.cc:92
    #50 0x586026 in make_unique<Envoy::Server::InstanceImpl, testing::NiceMock<Envoy::Server::MockOptions> &, Envoy::Event::TestTimeSystem &, std::__1::shared_ptr<Envoy::Network::Address::Ipv4Instance>, Envoy::DefaultTestHooks &, testing::NiceMock<Envoy::Server::MockHotRestart> &, Envoy::Stats::TestIsolatedStoreImpl &, Envoy::Thread::MutexBasicLockable &, Envoy::Server::TestComponentFactory &, std::__1::unique_ptr<Envoy::Runtime::RandomGeneratorImpl, std::__1::default_delete<Envoy::Runtime::RandomGeneratorImpl> >, Envoy::ThreadLocal::InstanceImpl &> /usr/local/include/c++/v1/memory:3118:32
    #51 0x586026 in Envoy::Server::TestOneProtoInput(envoy::config::bootstrap::v2::Bootstrap const&) /test/server/server_fuzz_test.cc:78

Fixes oss-fuzz issue https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11231

Risk Level: Low
Testing: Corpus entry added. It's pretty hard to build regressions for this
  kind of destruction ordering, so relying on server_fuzz_test + corpus.

Signed-off-by: Harvey Tuch <htuch@google.com>
ambuc pushed a commit that referenced this pull request Jun 3, 2019
Provide the HTTP path normalization per RFC 3986 (sans case normalization). This addresses CVE-2019-9901.

The config HttpConnectionManager.normalize_path needs to be set for each HCM configuration to enable (default is off). There is also a runtime optione http_connection_manager.normalize_path
to change this default when not set in HCM.

Risk level: Low
Testing: New unit and integration tests added.

Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Harvey Tuch <htuch@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants