Skip to content

Commit 18af259

Browse files
allanrboFelipe Zimmerle
authored andcommitted
IIS, buffer request body before taking lock
IIS, buffer request body before taking lock
1 parent 8dd4070 commit 18af259

File tree

1 file changed

+94
-27
lines changed

1 file changed

+94
-27
lines changed

iis/mymodule.cpp

Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@
3232

3333
#include "winsock2.h"
3434

35+
// Used to hold each chunk of request body that gets read before the full ModSec engine is invoked
36+
typedef struct preAllocBodyChunk {
37+
preAllocBodyChunk* next;
38+
size_t length;
39+
void* data;
40+
} preAllocBodyChunk;
41+
3542
class REQUEST_STORED_CONTEXT : public IHttpStoredContext
3643
{
3744
public:
@@ -82,8 +89,11 @@ class REQUEST_STORED_CONTEXT : public IHttpStoredContext
8289
char *m_pResponseBuffer;
8390
ULONGLONG m_pResponseLength;
8491
ULONGLONG m_pResponsePosition;
92+
93+
preAllocBodyChunk* requestBodyBufferHead;
8594
};
8695

96+
8797
//----------------------------------------------------------------------------
8898

8999
char *GetIpAddr(apr_pool_t *pool, PSOCKADDR pAddr)
@@ -286,6 +296,44 @@ REQUEST_STORED_CONTEXT *RetrieveIISContext(request_rec *r)
286296
return NULL;
287297
}
288298

299+
HRESULT GetRequestBodyFromIIS(IHttpRequest* pRequest, preAllocBodyChunk** head)
300+
{
301+
HRESULT hr = S_OK;
302+
HTTP_REQUEST * pRawRequest = pRequest->GetRawHttpRequest();
303+
preAllocBodyChunk** cur = head;
304+
while (pRequest->GetRemainingEntityBytes() > 0)
305+
{
306+
// Allocate memory for the preAllocBodyChunk linked list structure, and also the actual body content
307+
// HUGE_STRING_LEN is hardcoded because this is also hardcoded in apache2_io.c's call to ap_get_brigade
308+
preAllocBodyChunk* chunk = (preAllocBodyChunk*)malloc(sizeof(preAllocBodyChunk) + HUGE_STRING_LEN);
309+
chunk->next = NULL;
310+
311+
// Pointer to rest of allocated memory, for convenience
312+
chunk->data = chunk + 1;
313+
314+
DWORD readcnt = 0;
315+
hr = pRequest->ReadEntityBody(chunk->data, HUGE_STRING_LEN, false, &readcnt, NULL);
316+
if (ERROR_HANDLE_EOF == (hr & 0x0000FFFF))
317+
{
318+
free(chunk);
319+
hr = S_OK;
320+
break;
321+
}
322+
chunk->length = readcnt;
323+
324+
// Append to linked list
325+
*cur = chunk;
326+
cur = &(chunk->next);
327+
328+
if (hr != S_OK)
329+
{
330+
break;
331+
}
332+
}
333+
334+
return hr;
335+
}
336+
289337
HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf)
290338
{
291339
OVERLAPPED ovl;
@@ -752,6 +800,24 @@ CMyHttpModule::OnBeginRequest(
752800
goto Finished;
753801
}
754802

803+
// Get request body without holding lock, because some clients may be slow at sending
804+
LeaveCriticalSection(&m_csLock);
805+
preAllocBodyChunk* requestBodyBufferHead = NULL;
806+
hr = GetRequestBodyFromIIS(pRequest, &requestBodyBufferHead);
807+
if (hr != S_OK)
808+
{
809+
goto FinishedWithoutLock;
810+
}
811+
EnterCriticalSection(&m_csLock);
812+
813+
// Get the config again, in case it changed during the time we released the lock
814+
hr = MODSECURITY_STORED_CONTEXT::GetConfig(pHttpContext, &pConfig);
815+
if (FAILED(hr))
816+
{
817+
hr = S_OK;
818+
goto Finished;
819+
}
820+
755821
// every 3 seconds we check for changes in config file
756822
//
757823
DWORD ctime = GetTickCount();
@@ -841,6 +907,8 @@ CMyHttpModule::OnBeginRequest(
841907
rsc->m_pRequestRec = r;
842908
rsc->m_pHttpContext = pHttpContext;
843909
rsc->m_pProvider = pProvider;
910+
rsc->requestBodyBufferHead = requestBodyBufferHead;
911+
requestBodyBufferHead = NULL; // This is to indicate to the cleanup process to use rsc->requestBodyBufferHead instead of requestBodyBufferHead now
844912

845913
pHttpContext->GetModuleContextContainer()->SetModuleContext(rsc, g_pModuleContext);
846914

@@ -1086,6 +1154,16 @@ CMyHttpModule::OnBeginRequest(
10861154
Finished:
10871155
LeaveCriticalSection(&m_csLock);
10881156

1157+
FinishedWithoutLock:
1158+
// Free the preallocated body in case there was a failure and it wasn't consumed already
1159+
preAllocBodyChunk* chunkToFree = requestBodyBufferHead ? requestBodyBufferHead : rsc->requestBodyBufferHead;
1160+
while (chunkToFree != NULL)
1161+
{
1162+
preAllocBodyChunk* next = chunkToFree->next;
1163+
free(chunkToFree);
1164+
chunkToFree = next;
1165+
}
1166+
10891167
if ( FAILED( hr ) )
10901168
{
10911169
return RQ_NOTIFICATION_FINISH_REQUEST;
@@ -1095,40 +1173,29 @@ CMyHttpModule::OnBeginRequest(
10951173

10961174
apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)
10971175
{
1098-
REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r);
1099-
1100-
*readcnt = 0;
1101-
1102-
if(rsc == NULL)
1103-
{
1104-
*is_eos = 1;
1105-
return APR_SUCCESS;
1106-
}
1176+
REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r);
11071177

1108-
IHttpContext *pHttpContext = rsc->m_pHttpContext;
1109-
IHttpRequest *pRequest = pHttpContext->GetRequest();
1178+
if (rsc->requestBodyBufferHead == NULL)
1179+
{
1180+
*is_eos = 1;
1181+
return APR_SUCCESS;
1182+
}
11101183

1111-
if(pRequest->GetRemainingEntityBytes() == 0)
1112-
{
1113-
*is_eos = 1;
1114-
return APR_SUCCESS;
1115-
}
1184+
*readcnt = length < (unsigned int) rsc->requestBodyBufferHead->length ? length : (unsigned int) rsc->requestBodyBufferHead->length;
1185+
void* src = (char*)rsc->requestBodyBufferHead->data;
1186+
memcpy_s(buf, length, src, *readcnt);
11161187

1117-
HRESULT hr = pRequest->ReadEntityBody(buf, length, false, (DWORD *)readcnt, NULL);
1188+
// Remove the front and proceed to next chunk in the linked list
1189+
preAllocBodyChunk* chunkToFree = rsc->requestBodyBufferHead;
1190+
rsc->requestBodyBufferHead = rsc->requestBodyBufferHead->next;
1191+
free(chunkToFree);
11181192

1119-
if (FAILED(hr))
1193+
if (rsc->requestBodyBufferHead == NULL)
11201194
{
1121-
// End of data is okay.
1122-
if (ERROR_HANDLE_EOF != (hr & 0x0000FFFF))
1123-
{
1124-
// Set the error status.
1125-
rsc->m_pProvider->SetErrorStatus( hr );
1126-
}
1127-
1128-
*is_eos = 1;
1195+
*is_eos = 1;
11291196
}
11301197

1131-
return APR_SUCCESS;
1198+
return APR_SUCCESS;
11321199
}
11331200

11341201
apr_status_t WriteBodyCallback(request_rec *r, char *buf, unsigned int length)

0 commit comments

Comments
 (0)