Skip to content

Commit 036cbf6

Browse files
Fix: replace reachable assert with error return in header field callbacks and bump llhttp to v9.3.1 (#204)
1 parent 192b96c commit 036cbf6

File tree

6 files changed

+136
-10
lines changed

6 files changed

+136
-10
lines changed

docs/doxygen/include/size_table.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,22 @@
1414
</tr>
1515
<tr>
1616
<td>api.c (llhttp)</td>
17-
<td><center>2.8K</center></td>
18-
<td><center>2.2K</center></td>
17+
<td><center>7.5K</center></td>
18+
<td><center>6.8K</center></td>
1919
</tr>
2020
<tr>
2121
<td>http.c (llhttp)</td>
22-
<td><center>0.3K</center></td>
22+
<td><center>0.4K</center></td>
2323
<td><center>0.3K</center></td>
2424
</tr>
2525
<tr>
2626
<td>llhttp.c (llhttp)</td>
27-
<td><center>19.1K</center></td>
28-
<td><center>17.1K</center></td>
27+
<td><center>25.8K</center></td>
28+
<td><center>23.0K</center></td>
2929
</tr>
3030
<tr>
3131
<td><b>Total estimates</b></td>
32-
<td><b><center>25.5K</center></b></td>
33-
<td><b><center>22.3K</center></b></td>
32+
<td><b><center>37.0K</center></b></td>
33+
<td><b><center>32.8K</center></b></td>
3434
</tr>
3535
</table>

manifest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ license: "MIT"
66

77
dependencies:
88
- name: "llhttp"
9-
version: "release/v6.1.1"
9+
version: "release/v9.3.1"
1010
license: "MIT"
1111
repository:
1212
type: "git"

source/core_http_client.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,12 @@ static int httpParserOnHeaderFieldCallback( llhttp_t * pHttpParser,
717717
assert( pHttpParser->data != NULL );
718718
assert( pLoc != NULL );
719719

720+
if( length == 0U )
721+
{
722+
LogError( ( "Received zero-length header field name from parser." ) );
723+
return LLHTTP_STOP_PARSING;
724+
}
725+
720726
pParsingContext = ( HTTPParsingContext_t * ) ( pHttpParser->data );
721727
pResponse = pParsingContext->pResponse;
722728

@@ -2323,7 +2329,12 @@ static int findHeaderFieldParserCallback( llhttp_t * pHttpParser,
23232329
assert( pHttpParser != NULL );
23242330
assert( pHttpParser->data != NULL );
23252331
assert( pFieldLoc != NULL );
2326-
assert( fieldLen > 0U );
2332+
2333+
if( fieldLen == 0U )
2334+
{
2335+
LogError( ( "Received zero-length header field name from parser." ) );
2336+
return LLHTTP_STOP_PARSING;
2337+
}
23272338

23282339
pContext = ( findHeaderContext_t * ) pHttpParser->data;
23292340

test/unit-test/core_http_send_utest.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ static llhttp_errno_t llhttp_execute_error( llhttp_t * pParser,
445445
return httpParsingErrno;
446446
}
447447

448+
448449
/* Mock helper that parses the status line starting from pNext. */
449450
static void helper_parse_status_line( const char ** pNext,
450451
llhttp_t * pParser,
@@ -575,6 +576,33 @@ static void helper_parse_body( const char ** pNext,
575576
}
576577
}
577578

579+
/* Mocked llhttp_execute callback that parses the status line, then invokes
580+
* on_header_field with a zero-length field name to exercise the early return
581+
* guard in httpParserOnHeaderFieldCallback. */
582+
static llhttp_errno_t llhttp_execute_zero_length_header_field( llhttp_t * pParser,
583+
const char * pData,
584+
size_t len,
585+
int cmock_num_calls )
586+
{
587+
( void ) cmock_num_calls;
588+
( void ) len;
589+
const char * pNext = pData;
590+
llhttp_settings_t * pSettings = ( llhttp_settings_t * ) pParser->settings;
591+
int cbReturnValue = 0;
592+
593+
pSettings->on_message_begin( pParser );
594+
helper_parse_status_line( &pNext, pParser, pSettings );
595+
596+
/* Invoke on_header_field with length zero. The real callback should return
597+
* LLHTTP_STOP_PARSING. */
598+
cbReturnValue = pSettings->on_header_field( pParser, pNext, 0 );
599+
TEST_ASSERT_EQUAL( LLHTTP_STOP_PARSING, cbReturnValue );
600+
601+
pParser->error = HPE_USER;
602+
httpParserExecuteCallCount++;
603+
return HPE_USER;
604+
}
605+
578606
/* Mocked llhttp_execute callback that expects a whole response to be in
579607
* the given data to parse. */
580608
static llhttp_errno_t llhttp_execute_whole_response( llhttp_t * pParser,
@@ -1960,3 +1988,24 @@ void test_HTTPClient_Send_parsing_errors( void )
19601988
0 );
19611989
TEST_ASSERT_EQUAL( HTTPParserInternalError, returnStatus );
19621990
}
1991+
1992+
/* Test that httpParserOnHeaderFieldCallback returns LLHTTP_STOP_PARSING when
1993+
* the parser invokes it with a zero-length header field name (lines 720-724
1994+
* of core_http_client.c). */
1995+
void test_HTTPClient_Send_zero_length_header_field( void )
1996+
{
1997+
HTTPStatus_t returnStatus = HTTPSuccess;
1998+
1999+
llhttp_execute_Stub( llhttp_execute_zero_length_header_field );
2000+
2001+
returnStatus = HTTPClient_Send( &transportInterface,
2002+
&requestHeaders,
2003+
NULL,
2004+
0,
2005+
&response,
2006+
0 );
2007+
2008+
/* HPE_USER falls through to the default case in processLlhttpError,
2009+
* which maps to HTTPParserInternalError. */
2010+
TEST_ASSERT_EQUAL( HTTPParserInternalError, returnStatus );
2011+
}

test/unit-test/core_http_utest.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,36 @@ llhttp_errno_t parserExecuteExpectationsCb( llhttp_t * parser,
306306
return parserErrNo;
307307
}
308308

309+
/**
310+
* @brief Callback variant for llhttp_execute() mock that expects the
311+
* on_header_field callback to return LLHTTP_STOP_PARSING when invoked
312+
* with a zero-length header field name.
313+
*/
314+
llhttp_errno_t parserExecuteZeroLenFieldCb( llhttp_t * parser,
315+
const char * data,
316+
size_t len,
317+
int cmock_num_calls )
318+
{
319+
( void ) cmock_num_calls;
320+
321+
TEST_ASSERT_EQUAL( pCapturedParser, parser );
322+
TEST_ASSERT_NOT_NULL( parser );
323+
TEST_ASSERT_EQUAL( pCapturedSettings, parser->settings );
324+
TEST_ASSERT_EQUAL( expectedBufferSize, len );
325+
TEST_ASSERT_EQUAL( pExpectedBuffer, data );
326+
327+
if( invokeHeaderFieldCallback == 1U )
328+
{
329+
TEST_ASSERT_EQUAL( LLHTTP_STOP_PARSING,
330+
( ( llhttp_settings_t * ) ( parser->settings ) )->on_header_field( parser,
331+
pFieldLocToReturn,
332+
fieldLenToReturn ) );
333+
}
334+
335+
parser->error = parserErrNo;
336+
return parserErrNo;
337+
}
338+
309339
/**
310340
* @brief Fills the test input buffer and expectation buffers with pre-existing data
311341
* before calling the API function under test.
@@ -1458,6 +1488,42 @@ void test_Http_ReadHeader_Invalid_Response_Only_Header_Field_Found()
14581488
TEST_ASSERT_EQUAL( HTTPInvalidResponse, retCode );
14591489
}
14601490

1491+
/**
1492+
* @brief Test that a zero-length header field name from the parser causes
1493+
* findHeaderFieldParserCallback to return LLHTTP_STOP_PARSING, resulting
1494+
* in HTTPInvalidResponse from HTTPClient_ReadHeader.
1495+
*/
1496+
void test_Http_ReadHeader_Zero_Length_Header_Field( void )
1497+
{
1498+
/* Add expectations for llhttp init dependencies. */
1499+
llhttp_settings_init_ExpectAnyArgs();
1500+
llhttp_init_ExpectAnyArgs();
1501+
1502+
/* Override the default llhttp_execute mock callback to use the variant
1503+
* that expects LLHTTP_STOP_PARSING from on_header_field. */
1504+
llhttp_execute_AddCallback( parserExecuteZeroLenFieldCb );
1505+
1506+
/* Configure the llhttp_execute mock to invoke on_header_field with
1507+
* a zero-length field name. */
1508+
invokeHeaderFieldCallback = 1U;
1509+
pFieldLocToReturn = &pTestResponse[ headerFieldInRespLoc ];
1510+
fieldLenToReturn = 0U;
1511+
1512+
/* The callback returns LLHTTP_STOP_PARSING (HPE_USER) for the zero-length
1513+
* field. Since no header was found (fieldFound == 0) and parserErrno is
1514+
* not HPE_OK, findHeaderInResponse returns HTTPInvalidResponse. */
1515+
parserErrNo = HPE_USER;
1516+
llhttp_execute_ExpectAnyArgsAndReturn( HPE_USER );
1517+
1518+
/* Call the function under test. */
1519+
retCode = HTTPClient_ReadHeader( &testResponse,
1520+
HEADER_IN_BUFFER,
1521+
HEADER_IN_BUFFER_LEN,
1522+
&pValueLoc,
1523+
&valueLen );
1524+
TEST_ASSERT_EQUAL( HTTPInvalidResponse, retCode );
1525+
}
1526+
14611527

14621528
/**
14631529
* @brief Test happy path with zero-initialized requestHeaders and requestInfo.

0 commit comments

Comments
 (0)