Skip to content

Commit 28cb0f7

Browse files
filters: support on_downstream_data in set_filter_state (#43760)
<!-- !!!ATTENTION!!! If you are fixing *any* crash or *any* potential security issue, *do not* open a pull request in this repo. Please report the issue via emailing envoy-security@googlegroups.com where the issue will be triaged appropriately. Thank you in advance for helping to keep Envoy secure. !!!ATTENTION!!! For an explanation of how to fill out the fields, please see the relevant section in [PULL_REQUESTS.md](https://github.com/envoyproxy/envoy/blob/main/PULL_REQUESTS.md) !!!ATTENTION!!! Please check the [use of generative AI policy](https://github.com/envoyproxy/envoy/blob/main/CONTRIBUTING.md?plain=1#L41). You may use generative AI only if you fully understand the code. You need to disclose this usage in the PR description to ensure transparency. --> Commit Message: filters: support on_downstream_data in set_filter_state Additional Description: Similar to ON_DOWNSTREAM_TLS_HANDSHAKE support in set_filter_state, this PR adds the ability to set_filter_state when the filter first receives data. This allows to set filter state based on dynamic metadata added by other filters when they first receive data. Risk Level: Low Testing: Integration tests added. Docs Changes: Described added feature Release Notes: filters: Added support for set_filter_state at on_downstream_data time Platform Specific Features: Fixes #43640 --------- Signed-off-by: Guilherme Silva <26658340+GuilhermeJSilva@users.noreply.github.com>
1 parent 7f56aad commit 28cb0f7

6 files changed

Lines changed: 73 additions & 13 deletions

File tree

api/envoy/extensions/filters/network/set_filter_state/v3/set_filter_state.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ message Config {
3131
// For non-TLS downstream connections (where there is no TLS handshake), this
3232
// list is applied when a new connection is received.
3333
repeated common.set_filter_state.v3.FilterStateValue on_downstream_tls_handshake = 2;
34+
35+
// A sequence of the filter state values to apply in the specified order
36+
// when data is first received from the downstream connection.
37+
repeated common.set_filter_state.v3.FilterStateValue on_downstream_data = 3;
3438
}

changelogs/current.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,5 +687,18 @@ new_features:
687687
Added :ref:`set() <config_http_filters_lua_stream_info_filter_state_wrapper>` to the Lua filter
688688
state API, allowing Lua scripts to create and store filter state objects dynamically using
689689
registered object factories.
690+
- area: http
691+
change: |
692+
Fixed an issue where filter chain execution could continue on HTTP streams that had been reset but not yet
693+
destroyed. This could cause use-after-free conditions when filter callbacks were invoked on filters that
694+
had already received ``onDestroy()``. The fix ensures that ``decodeHeaders()``, ``decodeData()``,
695+
``decodeTrailers()``, and ``decodeMetadata()`` are blocked after a downstream reset.
696+
- area: network_filter
697+
change: |
698+
Added support for
699+
``on_downstream_data`` (see
700+
:ref:`envoy_v3_api_field_extensions.filters.network.set_filter_state.v3.Config.on_downstream_data`)
701+
to the :ref:`set_filter_state network filter <config_network_filters_set_filter_state>`, allowing
702+
connection filter state to be populated after first receiving data from the downstream connection.
690703
691704
deprecated:

docs/root/configuration/listeners/network_filters/set_filter_state.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The filter can apply values at different points in the connection lifecycle:
1717
* ``on_downstream_tls_handshake``: applied when the downstream TLS handshake is complete. For
1818
non-TLS downstream connections (where there is no TLS handshake), this list is applied when the
1919
new connection is accepted.
20+
* ``on_downstream_data``: applied when data is first received from the downstream connection.
2021

2122
.. warning::
2223
This filter allows overriding the behavior of other extensions and
@@ -86,6 +87,20 @@ TLS handshake completes (e.g., downstream peer certificate SANs), use
8687
text_format_source:
8788
inline_string: "%DOWNSTREAM_PEER_URI_SAN%"
8889

90+
When you need to populate filter state using information that is only available after receiving
91+
data from downstream (e.g., dynamic metadata set by another filter when data is received), use
92+
``on_downstream_data``:
93+
94+
.. validated-code-block:: yaml
95+
:type-name: envoy.extensions.filters.network.set_filter_state.v3.Config
96+
97+
on_downstream_data:
98+
- object_key: my.custom.filter_state
99+
factory_key: envoy.string
100+
format_string:
101+
text_format_source:
102+
inline_string: "%DYNAMIC_METADATA(envoy.filters.network.ext_authz:key)%"
103+
89104

90105
Statistics
91106
----------

source/extensions/filters/network/set_filter_state/config.cc

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ Network::FilterStatus SetFilterState::onNewConnection() {
2626
return Network::FilterStatus::Continue;
2727
}
2828

29+
Network::FilterStatus SetFilterState::onData(Buffer::Instance&, bool) {
30+
if (on_downstream_data_ != nullptr && waiting_for_downstream_data_) {
31+
waiting_for_downstream_data_ = false;
32+
on_downstream_data_->updateFilterState({}, read_callbacks_->connection().streamInfo());
33+
}
34+
return Network::FilterStatus::Continue;
35+
}
36+
2937
void SetFilterState::onEvent(Network::ConnectionEvent event) {
3038
// For SSL connections the Connected event is raised after the downstream TLS handshake completes.
3139
// Mirror tcp_proxy's behavior: only run the TLS hook for downstream TLS connections and only
@@ -79,10 +87,17 @@ class SetFilterStateConfigFactory
7987
StreamInfo::FilterState::LifeSpan::Connection, context);
8088
}
8189

82-
return [on_new_connection_config,
83-
on_downstream_tls_handshake_config](Network::FilterManager& filter_manager) -> void {
90+
Filters::Common::SetFilterState::ConfigSharedPtr on_downstream_data_config;
91+
if (!proto_config.on_downstream_data().empty()) {
92+
on_downstream_data_config = std::make_shared<Filters::Common::SetFilterState::Config>(
93+
proto_config.on_downstream_data(), StreamInfo::FilterState::LifeSpan::Connection,
94+
context);
95+
}
96+
97+
return [on_new_connection_config, on_downstream_tls_handshake_config,
98+
on_downstream_data_config](Network::FilterManager& filter_manager) -> void {
8499
filter_manager.addReadFilter(std::make_shared<SetFilterState>(
85-
on_new_connection_config, on_downstream_tls_handshake_config));
100+
on_new_connection_config, on_downstream_tls_handshake_config, on_downstream_data_config));
86101
};
87102
}
88103

source/extensions/filters/network/set_filter_state/config.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ class SetFilterState : public Network::ReadFilter,
1515
Logger::Loggable<Logger::Id::filter> {
1616
public:
1717
SetFilterState(Filters::Common::SetFilterState::ConfigSharedPtr on_new_connection,
18-
Filters::Common::SetFilterState::ConfigSharedPtr on_downstream_tls_handshake)
18+
Filters::Common::SetFilterState::ConfigSharedPtr on_downstream_tls_handshake,
19+
Filters::Common::SetFilterState::ConfigSharedPtr on_downstream_data)
1920
: on_new_connection_(std::move(on_new_connection)),
20-
on_downstream_tls_handshake_(std::move(on_downstream_tls_handshake)) {}
21+
on_downstream_tls_handshake_(std::move(on_downstream_tls_handshake)),
22+
on_downstream_data_(std::move(on_downstream_data)) {}
2123

2224
// Network::ReadFilter
23-
Network::FilterStatus onData(Buffer::Instance&, bool) override {
24-
return Network::FilterStatus::Continue;
25-
}
25+
Network::FilterStatus onData(Buffer::Instance&, bool) override;
2626
Network::FilterStatus onNewConnection() override;
2727
void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override {
2828
read_callbacks_ = &callbacks;
@@ -36,6 +36,10 @@ class SetFilterState : public Network::ReadFilter,
3636
apply_downstream_tls_handshake_on_new_connection_ = true;
3737
}
3838
}
39+
40+
if (on_downstream_data_ != nullptr) {
41+
waiting_for_downstream_data_ = true;
42+
}
3943
}
4044

4145
// Network::ConnectionCallbacks
@@ -48,10 +52,12 @@ class SetFilterState : public Network::ReadFilter,
4852

4953
const Filters::Common::SetFilterState::ConfigSharedPtr on_new_connection_;
5054
const Filters::Common::SetFilterState::ConfigSharedPtr on_downstream_tls_handshake_;
55+
const Filters::Common::SetFilterState::ConfigSharedPtr on_downstream_data_;
5156
Network::ReadFilterCallbacks* read_callbacks_{};
52-
bool waiting_for_downstream_tls_handshake_{false};
53-
bool apply_downstream_tls_handshake_on_new_connection_{false};
54-
bool downstream_tls_handshake_{false};
57+
bool waiting_for_downstream_tls_handshake_ : 1 {false};
58+
bool apply_downstream_tls_handshake_on_new_connection_ : 1 {false};
59+
bool downstream_tls_handshake_ : 1 {false};
60+
bool waiting_for_downstream_data_ : 1 {false};
5561
};
5662

5763
} // namespace SetFilterState

test/extensions/filters/network/set_filter_state/integration_test.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,20 @@ class SetFilterStateIntegrationTest : public testing::TestWithParam<Network::Add
4949
format_string:
5050
text_format_source:
5151
inline_string: "baz"
52+
on_downstream_data:
53+
- object_key: on_data
54+
factory_key: foo
55+
format_string:
56+
text_format_source:
57+
inline_string: "on_data_set"
5258
- name: envoy.filters.network.echo
5359
typed_config:
5460
"@type": type.googleapis.com/envoy.extensions.filters.network.echo.v3.Echo
5561
)EOF");
5662
}
5763

5864
void SetUp() override {
59-
useListenerAccessLog("%FILTER_STATE(early)%|%FILTER_STATE(late)%");
65+
useListenerAccessLog("%FILTER_STATE(early)%|%FILTER_STATE(late)%|%FILTER_STATE(on_data)%");
6066
BaseIntegrationTest::initialize();
6167
}
6268
};
@@ -70,7 +76,8 @@ TEST_P(SetFilterStateIntegrationTest, PlaintextConnectionAppliesBothLifecycleLis
7076
ASSERT_TRUE(tcp_client->write("hello"));
7177
ASSERT_TRUE(tcp_client->connected());
7278
tcp_client->close();
73-
EXPECT_THAT(waitForAccessLog(listener_access_log_name_), testing::HasSubstr("\"bar\"|\"baz\""));
79+
EXPECT_THAT(waitForAccessLog(listener_access_log_name_),
80+
testing::HasSubstr("\"bar\"|\"baz\"|\"on_data_set\""));
7481
}
7582

7683
class SetFilterStateDownstreamTlsIntegrationTest

0 commit comments

Comments
 (0)