Skip to content

Commit f301244

Browse files
Sujay Patelmeta-codesync[bot]
authored andcommitted
proxygen: clear FilterChain tail_ in release()
Summary: `FilterChain::release()` hands the chain head to the caller but leaves `tail_` pointing at the last filter. Heap allocated filters delete themselves as soon as they read EOM or an error, and `FilterChain::setSource()` routes through `tail_` whenever a filter was inserted - so a released chain can dereference a freed filter. `HTTPSourceReader::read()` releases the chain up front and `HTTPClient::makeRequestReadResponse()` then calls `reader.setSource(nullptr)`, which makes any heap allocated filter added via `HTTPSourceReader::insertFilter()` a use-after-free at the end of every request. That is the ownership model every `FilterFactory` produces (`RequestContextFilterFactory`, `StatsFilterUtil`, `CompressionFilterFactory`, `ClientDecompressionFilterFactory` all `setHeapAllocated()`), so today `FilterFactory` filters cannot be used with an `HTTPSourceReader` at all. The existing reader filter users - the redirect handler and the logger - own their filters as members, which is why this has not fired. Clearing `tail_` makes a released chain empty, so a later `setSource()` applies to the head instead of a filter the chain no longer owns. In the normal path that turns the trailing `setSource(nullptr)` into a no-op, since every filter has already cleared its own source by then. Split out of D114432220, which needs this to install `FilterFactory` filters on an `HTTPSourceReader`. Reviewed By: hanidamlaj Differential Revision: D114950502 fbshipit-source-id: e334deb011355f5cae8cb9c38b61f15a3f659913
1 parent 094fa45 commit f301244

2 files changed

Lines changed: 23 additions & 0 deletions

File tree

proxygen/lib/http/coro/HTTPSourceFilterChain.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class FilterChain {
6666

6767
// release ownership of chain
6868
HTTPSource* release() {
69+
tail_ = nullptr;
6970
return head_.release();
7071
}
7172

proxygen/lib/http/coro/test/HTTPSourceTests.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "proxygen/lib/http/coro/HTTPSourceReader.h"
1515
#include "proxygen/lib/http/coro/HTTPStreamSource.h"
1616
#include "proxygen/lib/http/coro/HTTPStreamSourceHolder.h"
17+
#include "proxygen/lib/http/coro/filters/MutateFilter.h"
1718
#include "proxygen/lib/http/coro/test/Mocks.h"
1819
#include "proxygen/lib/http/coro/util/ExecutorSourceFilter.h"
1920
#include "proxygen/lib/http/coro/util/test/TestHelpers.h"
@@ -1269,6 +1270,27 @@ TEST(HTTPSourceReader, filter) {
12691270
EXPECT_EQ(events, 2);
12701271
}
12711272

1273+
// A heap allocated filter deletes itself when it reads EOM, so the reader must
1274+
// not touch it after the read - notably HTTPClient clears the reader's source
1275+
// once the read completes.
1276+
TEST(HTTPSourceReader, heapAllocatedFilter) {
1277+
folly::EventBase evb;
1278+
bool sawHeaders = false;
1279+
auto* filter = new MutateFilter(
1280+
/*source=*/nullptr, [&sawHeaders](HTTPHeaderEvent& headerEvent) {
1281+
sawHeaders = true;
1282+
EXPECT_EQ(headerEvent.headers->getStatusCode(), 200);
1283+
});
1284+
filter->setHeapAllocated();
1285+
1286+
HTTPSourceReader reader;
1287+
reader.insertFilter(filter);
1288+
reader.setSource(HTTPFixedSource::makeFixedResponse(200, makeBuf(100)));
1289+
folly::coro::blockingWait(reader.read(), &evb);
1290+
reader.setSource(nullptr);
1291+
EXPECT_TRUE(sawHeaders);
1292+
}
1293+
12721294
TEST(HTTPErrorTests, ErrorStrings) {
12731295
EXPECT_EQ(getErrorString(HTTPErrorCode::PROTOCOL_ERROR), "PROTOCOL_ERROR");
12741296
EXPECT_EQ(getErrorString(HTTPErrorCode::QPACK_DECOMPRESSION_FAILED),

0 commit comments

Comments
 (0)