Skip to content

Commit 2070e49

Browse files
committed
make edge case handing in AsyncCallbackJsonWebHandler more robust
1 parent 56470cb commit 2070e49

File tree

4 files changed

+41
-13
lines changed

4 files changed

+41
-13
lines changed

src/AsyncJson.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,20 +117,35 @@ void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request)
117117
JsonVariant json;
118118
_onRequest(request, json);
119119
return;
120-
} else if (request->_tempObject != NULL) {
120+
}
121+
// this is not a GET
122+
// check if body is too large, if it is, don't parse
123+
if (request->contentLength() > _maxContentLength)
124+
{
125+
request->send(413);
126+
return;
127+
}
121128

129+
// try to parse body as JSON
130+
if (request->_tempObject != NULL)
131+
{
132+
size_t dataSize = min(request->contentLength(), request->_tempSize); // smaller value of contentLength or the size of the buffer. normally those should match.
122133
#if ARDUINOJSON_VERSION_MAJOR == 5
123134
DynamicJsonBuffer jsonBuffer;
124-
JsonVariant json = jsonBuffer.parse((uint8_t *)(request->_tempObject));
135+
uint8_t * p = (uint8_t *)(request->_tempObject);
136+
p[dataSize] = '\0'; // null terminate, assume we allocated one extra char
137+
// parse can only get null terminated strings as parameters
138+
JsonVariant json = jsonBuffer.parse(p);
125139
if (json.success()) {
126140
#elif ARDUINOJSON_VERSION_MAJOR == 6
127141
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
128-
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject), request->contentLength());
142+
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject), dataSize);
129143
if (!error) {
130144
JsonVariant json = jsonBuffer.as<JsonVariant>();
131145
#else
132146
JsonDocument jsonBuffer;
133-
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject), request->contentLength());
147+
// deserializeJson expects a null terminated string or a pointer plus length
148+
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t *)(request->_tempObject), dataSize);
134149
if (!error) {
135150
JsonVariant json = jsonBuffer.as<JsonVariant>();
136151
#endif
@@ -139,27 +154,40 @@ void AsyncCallbackJsonWebHandler::handleRequest(AsyncWebServerRequest *request)
139154
return;
140155
}
141156
}
142-
request->send(_contentLength > _maxContentLength ? 413 : 400);
143-
} else {
157+
// there is no body, no buffer or we had an error parsing the body
158+
request->send(400);
159+
} else { // if no _onRequest
144160
request->send(500);
145161
}
146162
}
147163

148164
void AsyncCallbackJsonWebHandler::handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
149165
if (_onRequest) {
150-
_contentLength = total;
151-
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
152-
request->_tempObject = malloc(total);
153-
if (request->_tempObject == NULL) {
166+
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) { // if request content length is valid size and we have no content buffer yet
167+
request->_tempObject = malloc(total + 1); // allocate one additional byte so we can null terminate this buffer (needed for ArduinoJson 5)
168+
if (request->_tempObject == NULL) { // if allocation failed
154169
#ifdef ESP32
155170
log_e("Failed to allocate");
156171
#endif
157172
request->abort();
158173
return;
159174
}
175+
request->_tempSize = total; // store the size of allocation we made into _tempSize
160176
}
161177
if (request->_tempObject != NULL) {
162-
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
178+
// check if the buffer is the right size so we don't write out of bounds
179+
if (request->_tempSize >= total)
180+
{
181+
memcpy((uint8_t *)(request->_tempObject) + index, data, len);
182+
}
183+
else
184+
{
185+
#ifdef ESP32
186+
log_e("Bad size of temp buffer");
187+
#endif
188+
request->abort();
189+
return;
190+
}
163191
}
164192
}
165193
}

src/AsyncJson.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
7979
String _uri;
8080
WebRequestMethodComposite _method;
8181
ArJsonRequestHandlerFunction _onRequest;
82-
size_t _contentLength;
8382
#if ARDUINOJSON_VERSION_MAJOR == 6
8483
size_t maxJsonBufferSize;
8584
#endif

src/ESPAsyncWebServer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ class AsyncWebServerRequest {
261261
public:
262262
File _tempFile;
263263
void *_tempObject;
264+
size_t _tempSize;
264265

265266
AsyncWebServerRequest(AsyncWebServer *, AsyncClient *);
266267
~AsyncWebServerRequest();

src/WebRequest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer *s, AsyncClient *c)
2525
: _client(c), _server(s), _handler(NULL), _response(NULL), _temp(), _parseState(PARSE_REQ_START), _version(0), _method(HTTP_ANY), _url(), _host(),
2626
_contentType(), _boundary(), _authorization(), _reqconntype(RCT_HTTP), _authMethod(AsyncAuthType::AUTH_NONE), _isMultipart(false), _isPlainPost(false),
2727
_expectingContinue(false), _contentLength(0), _parsedLength(0), _multiParseState(0), _boundaryPosition(0), _itemStartIndex(0), _itemSize(0), _itemName(),
28-
_itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL) {
28+
_itemFilename(), _itemType(), _itemValue(), _itemBuffer(0), _itemBufferIndex(0), _itemIsFile(false), _tempObject(NULL), _tempSize(0) {
2929
c->onError(
3030
[](void *r, AsyncClient *c, int8_t error) {
3131
(void)c;

0 commit comments

Comments
 (0)