Skip to content

Commit 497efb9

Browse files
authored
server: handle non-EnvoyExceptions safely if thrown in constructor. (#4173)
This came up while addressing oss-fuzz issue https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=9335 in #4171. Without this PR, the server would shutdown non-gracefully, with TLS posts still possible to deleted worker thread dispatchers, resulting in heap-user-after-free. Protobuf was throwing a CHECK exception, which was not picked up as EnvoyException. Risk level: Low Testing: Unit tests added, corpus entry is in #4171. Signed-off-by: Harvey Tuch <[email protected]>
1 parent 6d6fafd commit 497efb9

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

source/server/server.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,14 @@ InstanceImpl::InstanceImpl(Options& options, Network::Address::InstanceConstShar
7676
} catch (const EnvoyException& e) {
7777
ENVOY_LOG(critical, "error initializing configuration '{}': {}", options.configPath(),
7878
e.what());
79-
79+
terminate();
80+
throw;
81+
} catch (const std::exception& e) {
82+
ENVOY_LOG(critical, "error initializing due to unexpected exception: {}", e.what());
83+
terminate();
84+
throw;
85+
} catch (...) {
86+
ENVOY_LOG(critical, "error initializing due to unknown exception");
8087
terminate();
8188
throw;
8289
}

test/server/server_test.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using testing::HasSubstr;
1616
using testing::InSequence;
1717
using testing::Invoke;
18+
using testing::InvokeWithoutArgs;
1819
using testing::Property;
1920
using testing::Ref;
2021
using testing::SaveArg;
@@ -299,5 +300,33 @@ TEST_P(ServerInstanceImplTest, NoOptionsPassed) {
299300
EnvoyException, "unable to read file: ");
300301
}
301302

303+
// Validate that when std::exception is unexpectedly thrown, we exit safely.
304+
// This is a regression test for when we used to crash.
305+
TEST_P(ServerInstanceImplTest, StdExceptionThrowInConstructor) {
306+
EXPECT_CALL(restart_, initialize(_, _)).WillOnce(InvokeWithoutArgs([] {
307+
throw(std::runtime_error("foobar"));
308+
}));
309+
EXPECT_THROW_WITH_MESSAGE(initialize("test/server/node_bootstrap.yaml"), std::runtime_error,
310+
"foobar");
311+
}
312+
313+
// Neither EnvoyException nor std::exception derived.
314+
class FakeException {
315+
public:
316+
FakeException(const std::string& what) : what_(what) {}
317+
const std::string& what() const { return what_; }
318+
319+
const std::string what_;
320+
};
321+
322+
// Validate that when a totally unknown exception is unexpectedly thrown, we
323+
// exit safely. This is a regression test for when we used to crash.
324+
TEST_P(ServerInstanceImplTest, UnknownExceptionThrowInConstructor) {
325+
EXPECT_CALL(restart_, initialize(_, _)).WillOnce(InvokeWithoutArgs([] {
326+
throw(FakeException("foobar"));
327+
}));
328+
EXPECT_THROW_WITH_MESSAGE(initialize("test/server/node_bootstrap.yaml"), FakeException, "foobar");
329+
}
330+
302331
} // namespace Server
303332
} // namespace Envoy

0 commit comments

Comments
 (0)