Skip to content

Commit d3e445e

Browse files
authored
lua filter: ensure active_request_ is verified before deletion Fixes envoyproxy#38017 (envoyproxy#38863)
Lua filter was removing ":status" and then setting a response body, which was causing envoy to terminate with a SIGSEGV. The issue occurred because "onEncodeComplete()" attempted to delete "activerequest" via "deferredDelete()", but "activerequest" had already been deleted earlier, leading to a segmentation fault. This fix ensures that "activerequest" is only deleted once, preventing double deletion. This is done by verifying that "activerequest" is not null before the existing check of "activerequest->remotecomplete". Risk Level: Low Testing: I added a new integration test in "lua_integration_test.cc" file specifically for this bug. Without the fix, the test fails; with it applied, the test passes successfully. Fixes envoyproxy#38017 --------- Signed-off-by: Leonor Figueira <[email protected]>
1 parent f72ce98 commit d3e445e

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

source/common/http/filter_manager.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ void ActiveStreamFilterBase::commonContinue() {
8484
}
8585
}
8686

87+
if (!canContinue()) {
88+
ENVOY_STREAM_LOG(trace, "cannot continue filter chain: filter={}", *this,
89+
static_cast<const void*>(this));
90+
return;
91+
}
92+
8793
// Make sure that we handle the zero byte data frame case. We make no effort to optimize this
8894
// case in terms of merging it into a header only request/response. This could be done in the
8995
// future.
@@ -92,8 +98,20 @@ void ActiveStreamFilterBase::commonContinue() {
9298
doHeaders(observedEndStream() && !bufferedData() && !hasTrailers());
9399
}
94100

101+
if (!canContinue()) {
102+
ENVOY_STREAM_LOG(trace, "cannot continue filter chain: filter={}", *this,
103+
static_cast<const void*>(this));
104+
return;
105+
}
106+
95107
doMetadata();
96108

109+
if (!canContinue()) {
110+
ENVOY_STREAM_LOG(trace, "cannot continue filter chain: filter={}", *this,
111+
static_cast<const void*>(this));
112+
return;
113+
}
114+
97115
// It is possible for trailers to be added during doData(). doData() itself handles continuation
98116
// of trailers for the non-continuation case. Thus, we must keep track of whether we had
99117
// trailers prior to calling doData(). If we do, then we continue them here, otherwise we rely
@@ -103,6 +121,12 @@ void ActiveStreamFilterBase::commonContinue() {
103121
doData(observedEndStream() && !had_trailers_before_data);
104122
}
105123

124+
if (!canContinue()) {
125+
ENVOY_STREAM_LOG(trace, "cannot continue filter chain: filter={}", *this,
126+
static_cast<const void*>(this));
127+
return;
128+
}
129+
106130
if (had_trailers_before_data) {
107131
doTrailers();
108132
}

test/extensions/filters/http/lua/lua_integration_test.cc

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,5 +2026,66 @@ name: lua
20262026
cleanup();
20272027
}
20282028

2029+
#ifdef NDEBUG
2030+
// This test is only run in release mode because in debug mode,
2031+
// the code reaches ENVOY_BUG() which triggers a forced abort
2032+
// that stops execution.
2033+
TEST_P(LuaIntegrationTest, ModifyResponseBodyAndRemoveStatusHeader) {
2034+
if (downstream_protocol_ != Http::CodecType::HTTP1) {
2035+
GTEST_SKIP() << "This is a test that only supports http1";
2036+
}
2037+
if (!testing_downstream_filter_) {
2038+
GTEST_SKIP() << "This is a local reply test that does not go upstream";
2039+
}
2040+
const std::string filter_config =
2041+
R"EOF(
2042+
name: lua
2043+
typed_config:
2044+
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
2045+
default_source_code:
2046+
inline_string: |
2047+
function envoy_on_response(response_handle)
2048+
local response_headers = response_handle:headers()
2049+
response_headers:remove(":status")
2050+
response_handle:body(true):setBytes("hello world")
2051+
end
2052+
)EOF";
2053+
2054+
const std::string route_config =
2055+
R"EOF(
2056+
name: basic_lua_routes
2057+
virtual_hosts:
2058+
- name: rds_vhost_1
2059+
domains: ["lua.per.route"]
2060+
routes:
2061+
- match:
2062+
prefix: "/lua"
2063+
direct_response:
2064+
status: 200
2065+
body:
2066+
inline_string: "hello"
2067+
)EOF";
2068+
2069+
initializeWithYaml(filter_config, route_config);
2070+
codec_client_ = makeHttpConnection(lookupPort("http"));
2071+
2072+
Http::TestRequestHeaderMapImpl default_headers{{":method", "GET"},
2073+
{":path", "/lua"},
2074+
{":scheme", "http"},
2075+
{":authority", "lua.per.route"},
2076+
{"x-forwarded-for", "10.0.0.1"}};
2077+
2078+
auto encoder_decoder = codec_client_->startRequest(default_headers);
2079+
auto response = std::move(encoder_decoder.second);
2080+
2081+
ASSERT_TRUE(response->waitForEndStream());
2082+
ASSERT_TRUE(response->complete());
2083+
EXPECT_EQ("502", response->headers().getStatusValue());
2084+
EXPECT_THAT(response->body(), testing::HasSubstr("missing required header: :status"));
2085+
2086+
cleanup();
2087+
}
2088+
#endif
2089+
20292090
} // namespace
20302091
} // namespace Envoy

0 commit comments

Comments
 (0)