Skip to content

Commit 863ab2f

Browse files
committed
fix(http): prevent use-after-free from synchronous abort/close() (AsyncTCP 3.5.0)
Ensure that AsyncWebRequest objects are held in scope until the end of their handler functions, avoiding any possible use-after-free cases when aborted or closed. This is managed by using `std::shared_ptr` to allow a scoped lifecycle "lock" to be taken without an OS mutex.
1 parent e43572c commit 863ab2f

3 files changed

Lines changed: 25 additions & 16 deletions

File tree

src/ESPAsyncWebServer.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,11 +508,22 @@ class AsyncWebServerRequest {
508508

509509
static bool _getEtag(File gzFile, char *eTag);
510510

511+
// Constructor is private to ensure factory is used to create shared_ptrs
512+
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
513+
511514
public:
512515
File _tempFile;
513516
void *_tempObject;
514517

515-
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
518+
// Factory function
519+
static std::shared_ptr<AsyncWebServerRequest> create(AsyncWebServer *server, AsyncClient *client) {
520+
AsyncWebServerRequest *req = new (std::nothrow) AsyncWebServerRequest(server, client);
521+
if (req) {
522+
req->_this = std::shared_ptr<AsyncWebServerRequest>(req); // store shared pointer to this request
523+
return req->_this;
524+
}
525+
return {}; // empty shared_ptr
526+
}
516527
~AsyncWebServerRequest();
517528

518529
AsyncClient *client() {
@@ -1763,7 +1774,6 @@ class AsyncWebServer : public AsyncMiddlewareChain {
17631774

17641775
void reset(); // remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
17651776

1766-
void _handleDisconnect(AsyncWebServerRequest *request);
17671777
void _attachHandler(AsyncWebServerRequest *request);
17681778
void _rewriteRequest(AsyncWebServerRequest *request);
17691779
};

src/WebRequest.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ static inline bool isParamChar(char c) {
1717
return ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='));
1818
}
1919

20-
static void doNotDelete(AsyncWebServerRequest *) {}
21-
2220
using namespace asyncsrv;
2321

2422
enum {
@@ -108,8 +106,6 @@ AsyncWebServerRequest::~AsyncWebServerRequest() {
108106
_response = nullptr;
109107
}
110108

111-
_this.reset();
112-
113109
if (_tempObject != NULL) {
114110
free(_tempObject);
115111
}
@@ -124,6 +120,8 @@ AsyncWebServerRequest::~AsyncWebServerRequest() {
124120
}
125121

126122
void AsyncWebServerRequest::_onData(void *buf, size_t len) {
123+
std::shared_ptr<AsyncWebServerRequest> self = _this; // Ensure we stay in scope over the function
124+
127125
// SSL/TLS handshake detection
128126
#ifndef ASYNC_TCP_SSL_ENABLED
129127
if (_parseState == PARSE_REQ_START && len && ((uint8_t *)buf)[0] == 0x16) { // 0x16 indicates a Handshake message (SSL/TLS).
@@ -240,6 +238,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) {
240238
}
241239

242240
void AsyncWebServerRequest::_onPoll() {
241+
std::shared_ptr<AsyncWebServerRequest> self = _this; // Ensure we stay in scope over the function
243242
// os_printf("p\n");
244243
if (_response && _client && _client->canSend()) {
245244
_response->_ack(this, 0, 0);
@@ -252,6 +251,8 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) {
252251
return;
253252
}
254253

254+
std::shared_ptr<AsyncWebServerRequest> self = _this; // Ensure we stay in scope over the function
255+
255256
if (!_response->_finished()) {
256257
_response->_ack(this, len, time);
257258
// recheck if response has just completed, close connection
@@ -271,6 +272,7 @@ void AsyncWebServerRequest::_onError(int8_t error) {
271272
void AsyncWebServerRequest::_onTimeout(uint32_t time) {
272273
(void)time;
273274
// os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString());
275+
// We do not need to lock the shared pointer here as we do no work after closing the client
274276
_client->close();
275277
}
276278

@@ -283,7 +285,7 @@ void AsyncWebServerRequest::_onDisconnect() {
283285
if (_onDisconnectfn) {
284286
_onDisconnectfn();
285287
}
286-
_server->_handleDisconnect(this);
288+
this->_this.reset(); // release shared pointer to this request, allowing for object destruction
287289
}
288290

289291
void AsyncWebServerRequest::_addGetParams(const String &params) {
@@ -1054,9 +1056,6 @@ AsyncWebServerRequestPtr AsyncWebServerRequest::pause() {
10541056
return _this;
10551057
}
10561058
client()->setRxTimeout(0);
1057-
// this shared ptr will hold the request pointer until it gets destroyed following a disconnect.
1058-
// this is just used as a holder providing weak observers, so the deleter is a no-op.
1059-
_this = std::shared_ptr<AsyncWebServerRequest>(this, doNotDelete);
10601059
_paused = true;
10611060
return _this;
10621061
}
@@ -1065,7 +1064,6 @@ void AsyncWebServerRequest::abort() {
10651064
if (!_sent) {
10661065
_sent = true;
10671066
_paused = false;
1068-
_this.reset();
10691067
async_ws_log_v("Abort request: %s", _url.c_str());
10701068
_client->abort();
10711069
}
@@ -1327,7 +1325,9 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch
13271325
r->addHeader(T_WWW_AUTH, header.c_str());
13281326
} else {
13291327
async_ws_log_e("Failed to allocate");
1328+
delete r;
13301329
abort();
1330+
return;
13311331
}
13321332

13331333
break;
@@ -1351,7 +1351,9 @@ void AsyncWebServerRequest::requestAuthentication(AsyncAuthType method, const ch
13511351
r->addHeader(T_WWW_AUTH, header.c_str());
13521352
} else {
13531353
async_ws_log_e("Failed to allocate");
1354+
delete r;
13541355
abort();
1356+
return;
13551357
}
13561358
}
13571359
break;

src/WebServer.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "ESPAsyncWebServer.h"
55
#include "WebHandlerImpl.h"
66

7+
#include <memory>
78
#include <string>
89
#include <utility>
910

@@ -47,7 +48,7 @@ AsyncWebServer::AsyncWebServer(uint16_t port) : _server(port) {
4748
return;
4849
}
4950
c->setRxTimeout(3);
50-
AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer *)s, c);
51+
std::shared_ptr<AsyncWebServerRequest> r = AsyncWebServerRequest::create(static_cast<AsyncWebServer *>(s), c);
5152
if (r == NULL) {
5253
c->abort();
5354
delete c;
@@ -127,10 +128,6 @@ void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *
127128
}
128129
#endif
129130

130-
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request) {
131-
delete request;
132-
}
133-
134131
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request) {
135132
// the last rewrite that matches the request will be used
136133
// we do not break the loop to allow for multiple rewrites to be applied and only the last one to be used (allows overriding)

0 commit comments

Comments
 (0)