Skip to content

Commit 28c9247

Browse files
authored
Revert "Revert "Fix ESP crash when fetching large jwt public keys (#745)" (#746)" (#748)
This reverts commit ab6e180.
1 parent 7131d4b commit 28c9247

File tree

2 files changed

+64
-43
lines changed

2 files changed

+64
-43
lines changed

src/nginx/http.cc

+50-42
Original file line numberDiff line numberDiff line change
@@ -493,30 +493,21 @@ void wakeup_event_handler(ngx_event_t *ev) {
493493
"wakeup_event_handler called: %V%V",
494494
&http_connection->host_header, &http_connection->url_path);
495495

496-
// Move all pointers to the local variables because once we destroy the
497-
// pools we can no longer access the memory.
498-
499496
// Request and a connection pools.
500-
ngx_pool_t *rp = http_connection->request->pool;
501-
ngx_pool_t *cp = http_connection->connection.pool;
497+
ngx_pool_t *ep = http_connection->esp_pool;
498+
ngx_pool_t *cp = http_connection->connection_pool_reset.pool;
499+
ngx_pool_t *rp = http_connection->request_pool_reset.pool;
502500

503-
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
504-
"esp: destroying pools c=%p, r=%p", cp, rp);
505-
506-
// If the connection is reset by peer, somehow rp or cp is 0.
507-
// Not sure if any of pools have been freed. But trying to free
508-
// a nullptr pool definitely will cause crash.
509-
// Here, choose the least of evil, memory leak over crash.
510-
if (rp == nullptr || cp == nullptr) {
511-
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
512-
"esp memory pools may not be freed: pools c=%p, r=%p", cp,
513-
rp);
514-
return;
515-
}
501+
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ev->log, 0,
502+
"esp: destroying pools ep=%p, cp=%p, rp=%p", ep, cp, rp);
516503

517-
// Destroy the pools.
518-
ngx_destroy_pool(rp);
519-
ngx_destroy_pool(cp);
504+
if (cp != nullptr) {
505+
ngx_destroy_pool(cp);
506+
}
507+
if (rp != nullptr) {
508+
ngx_destroy_pool(rp);
509+
}
510+
ngx_destroy_pool(ep);
520511
}
521512

522513
// A finalize handler. Called by NGINX when request is complete (success)
@@ -612,6 +603,11 @@ void ngx_esp_upstream_finalize_request(ngx_http_request_t *r, ngx_int_t rc) {
612603

613604
// Schedule the wakeup call with NGINX.
614605
ngx_post_event(&http_connection->wakeup_event, &ngx_posted_events);
606+
607+
// upstream will call output filter chain. Its postpone filter checks
608+
// content_length with subrequest_output_buffer_size. By clearing it
609+
// to make sure it passes the postone filter.
610+
ngx_http_clear_content_length(r);
615611
}
616612

617613
// An input filter initialization handler.
@@ -754,37 +750,39 @@ class ngx_pool_t_deleter {
754750
// over them (the subrequest code has less control over pools than we have
755751
// here), a single pool could work as well.
756752
Status allocate_pools(
757-
ngx_log_t *log,
758-
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> *out_connection_pool,
759-
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> *out_request_pool) {
760-
// Only these structures will be allocated from the pool. No need to allocate
761-
// pool bigger than necessary.
762-
const size_t kConnectionPoolSize = sizeof(ngx_pool_t) +
763-
sizeof(ngx_pool_cleanup_t) +
764-
sizeof(ngx_esp_http_connection);
765-
766-
// Create the connection pool.
767-
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> connection_pool(
768-
ngx_create_pool(kConnectionPoolSize, log));
753+
ngx_log_t *log, std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> &esp_pool,
754+
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> &connection_pool,
755+
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> &request_pool) {
756+
// Only esp_http_connection will be allocated from the pool. No need to
757+
// allocate pool bigger than necessary.
758+
const size_t kEspPoolSize = sizeof(ngx_pool_t) + sizeof(ngx_pool_cleanup_t) +
759+
sizeof(ngx_esp_http_connection);
760+
esp_pool.reset(ngx_create_pool(kEspPoolSize, log));
761+
if (esp_pool == nullptr) {
762+
return Status(NGX_ERROR, "Out of memory");
763+
}
764+
765+
// Only a clean up is allocated from list pool
766+
const size_t kConnectionPoolSize =
767+
sizeof(ngx_pool_t) + sizeof(ngx_pool_cleanup_t);
768+
connection_pool.reset(ngx_create_pool(kConnectionPoolSize, log));
769769
if (connection_pool == nullptr) {
770770
return Status(NGX_ERROR, "Out of memory");
771771
}
772772

773773
// Allocate the request pool.
774-
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> request_pool(
775-
ngx_create_pool(kRequestPoolSize, log));
774+
request_pool.reset(ngx_create_pool(kRequestPoolSize, log));
776775
if (request_pool == nullptr) {
777776
return Status(NGX_ERROR, "Out of memory");
778777
}
779778

779+
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "created HTTP ESP pool: %p",
780+
esp_pool.get());
780781
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "created HTTP connection pool: %p",
781782
connection_pool.get());
782783
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "created HTTP request pool: %p",
783784
request_pool.get());
784785

785-
*out_connection_pool = std::move(connection_pool);
786-
*out_request_pool = std::move(request_pool);
787-
788786
return Status::OK;
789787
}
790788

@@ -1104,20 +1102,20 @@ Status ngx_esp_create_http_request(
11041102
ngx_log_t *log, HTTPRequest *request,
11051103
ngx_esp_http_connection **out_http_connection) {
11061104
// Create the connection pool and request pools.
1105+
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> esp_pool;
11071106
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> connection_pool;
11081107
std::unique_ptr<ngx_pool_t, ngx_pool_t_deleter> request_pool;
11091108

1110-
Status status = allocate_pools(log, &connection_pool, &request_pool);
1109+
Status status = allocate_pools(log, esp_pool, connection_pool, request_pool);
11111110
if (!status.ok()) {
11121111
return status;
11131112
}
11141113

11151114
// Allocate the HTTP connection state in one go.
11161115
// Because we custom-fit the pool size to match this one allocation, the
11171116
// alloction will not fail.
1118-
ngx_esp_http_connection *http_connection =
1119-
RegisterPoolCleanup(connection_pool.get(), new (connection_pool.get())
1120-
ngx_esp_http_connection());
1117+
ngx_esp_http_connection *http_connection = RegisterPoolCleanup(
1118+
esp_pool.get(), new (esp_pool.get()) ngx_esp_http_connection());
11211119
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
11221120
"esp: allocated http_connection %p", http_connection);
11231121
if (http_connection == nullptr) {
@@ -1160,6 +1158,15 @@ Status ngx_esp_create_http_request(
11601158
return status;
11611159
}
11621160

1161+
// connection_pool and request_pool can be freed by Nginx.
1162+
// If the pool is freed, the cleanup function will reset the pointer.
1163+
http_connection->esp_pool = esp_pool.get();
1164+
http_connection->connection_pool_reset.pool = connection_pool.get();
1165+
http_connection->request_pool_reset.pool = request_pool.get();
1166+
RegisterPoolCleanup(connection_pool.get(),
1167+
&http_connection->connection_pool_reset);
1168+
RegisterPoolCleanup(request_pool.get(), &http_connection->request_pool_reset);
1169+
11631170
// Success. Release the pool smart pointers.
11641171
//
11651172
// The pools are now owned by the connection and request objects
@@ -1170,6 +1177,7 @@ Status ngx_esp_create_http_request(
11701177
// connection pool: http_connection->connection.pool
11711178
//
11721179
// They will be destroyed in wakeup_event_handler.
1180+
esp_pool.release();
11731181
connection_pool.release();
11741182
request_pool.release();
11751183

src/nginx/http.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ namespace google {
4141
namespace api_manager {
4242
namespace nginx {
4343

44+
// A class to reset the pool pointer at destructor.
45+
struct NgxPoolReset {
46+
~NgxPoolReset() { pool = nullptr; }
47+
ngx_pool_t* pool{};
48+
};
49+
4450
// ngx_esp_http_connection contains all of the connection data necessary
4551
// to issue an HTTP request using NGINX's upstream module.
4652
//
@@ -50,6 +56,13 @@ namespace nginx {
5056
// is calculated exactly to fit any NGINX pool structures, this struct,
5157
// and pool cleanup callback (which will call the destructor).
5258
struct ngx_esp_http_connection {
59+
// The pool owns this data structure.
60+
ngx_pool_t* esp_pool;
61+
// The pool for the fake connection.
62+
NgxPoolReset connection_pool_reset;
63+
// The pool for the request
64+
NgxPoolReset request_pool_reset;
65+
5366
// A 'fake' client NGINX connection object.
5467
//
5568
// Because NGINX is primarily a proxy, it usually has two connections:
@@ -78,7 +91,7 @@ struct ngx_esp_http_connection {
7891
// Request information.
7992

8093
// The nginx_http_request_t used to handle the HTTP request.
81-
ngx_http_request_t *request;
94+
ngx_http_request_t* request;
8295

8396
// Target URL path and host. Host will be used to send 'Host' header.
8497
ngx_str_t url_path;

0 commit comments

Comments
 (0)