-
Notifications
You must be signed in to change notification settings - Fork 405
Replace manual Content-Length parsing with strtoumax #8434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4e4e41e
78c5a2d
1bdb9a5
703a82c
8fb2501
cf87387
8ddee82
82da59f
15f733d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| #include <strings.h> | ||
| #include <sys/socket.h> | ||
| #include <netinet/in.h> | ||
| #include <inttypes.h> | ||
|
|
||
| #if FD_HAS_ZSTD | ||
| #define FD_HTTP_ZSTD_COMPRESSION_LEVEL 3 | ||
|
|
@@ -320,6 +321,29 @@ fd_http_server_listen( fd_http_server_t * http, | |
| return http; | ||
| } | ||
|
|
||
| /* parse_ulong parses a decimal unsigned long from a string. Returns | ||
| ULONG_MAX on error (empty string, invalid format, or overflow). */ | ||
|
|
||
| static ulong | ||
| parse_ulong( char const * p, | ||
| ulong sz ) { | ||
| if( FD_UNLIKELY( p==NULL || sz==0UL || sz>21UL ) ) return ULONG_MAX; | ||
| if( FD_UNLIKELY( p[0]<'0' || p[0]>'9' ) ) return ULONG_MAX; | ||
|
|
||
| uchar buf[ 32 ]; | ||
| fd_memcpy( buf, p, sz ); | ||
| buf[ sz ] = '\0'; | ||
|
|
||
| char * endptr; | ||
| errno = 0; | ||
| uintmax_t val = strtoumax( (char const *)buf, &endptr, 10 ); | ||
|
|
||
| if( FD_UNLIKELY( endptr==(char const *)buf || *endptr!='\0' ) ) return ULONG_MAX; | ||
| if( FD_UNLIKELY( errno==ERANGE || val>ULONG_MAX ) ) return ULONG_MAX; | ||
|
|
||
| return (ulong)val; | ||
| } | ||
|
|
||
| static void | ||
| close_conn( fd_http_server_t * http, | ||
| ulong conn_idx, | ||
|
|
@@ -509,19 +533,10 @@ read_conn_http( fd_http_server_t * http, | |
| return; | ||
| } | ||
|
|
||
| for( ulong i=0UL; i<content_length_len; i++ ) { | ||
| if( FD_UNLIKELY( content_length[ i ]<'0' || content_length[ i ]>'9' ) ) { | ||
| close_conn( http, conn_idx, FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST ); | ||
| return; | ||
| } | ||
|
|
||
| ulong next = content_len*10UL + (ulong)(content_length[ i ]-'0'); | ||
| if( FD_UNLIKELY( next<content_len ) ) { /* Overflow */ | ||
| close_conn( http, conn_idx, FD_HTTP_SERVER_CONNECTION_CLOSE_LARGE_REQUEST ); | ||
| return; | ||
| } | ||
|
|
||
| content_len = next; | ||
| content_len = parse_ulong( content_length, content_length_len ); | ||
| if( FD_UNLIKELY( content_len==ULONG_MAX ) ) { | ||
| close_conn( http, conn_idx, FD_HTTP_SERVER_CONNECTION_CLOSE_BAD_REQUEST ); | ||
| return; | ||
| } | ||
|
Comment on lines
+536
to
540
|
||
|
|
||
| ulong total_len = (ulong)result+content_len; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -2,6 +2,52 @@ | |||
| #include "fd_http_server_private.h" | ||||
| #include "../../util/fd_util.h" | ||||
|
|
||||
| #include <arpa/inet.h> | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
| #include <sys/socket.h> | ||||
| #include <unistd.h> | ||||
|
|
||||
| struct overflow_close_state { | ||||
| ulong close_cnt; | ||||
| int last_reason; | ||||
| }; | ||||
|
|
||||
| typedef struct overflow_close_state overflow_close_state_t; | ||||
|
|
||||
| static fd_http_server_response_t | ||||
| request_noop( fd_http_server_request_t const * request ) { | ||||
| (void)request; | ||||
| fd_http_server_response_t response = { | ||||
| .status = 400, | ||||
| }; | ||||
| return response; | ||||
| } | ||||
|
|
||||
| static void | ||||
| close_capture( ulong conn_id, | ||||
| int reason, | ||||
| void * ctx ) { | ||||
| (void)conn_id; | ||||
| overflow_close_state_t * state = (overflow_close_state_t *)ctx; | ||||
| state->close_cnt++; | ||||
| state->last_reason = reason; | ||||
| } | ||||
|
|
||||
| static void | ||||
| send_all( int fd, | ||||
| char const * req, | ||||
| ulong req_sz ) { | ||||
| ulong sent = 0UL; | ||||
| while( sent<req_sz ) { | ||||
| long n = send( fd, req+sent, req_sz-sent, 0 ); | ||||
| if( FD_UNLIKELY( n<0L ) ) { | ||||
| FD_LOG_ERR(( "send failed (%i-%s)", errno, fd_io_strerror( errno ) )); | ||||
| } | ||||
| sent += (ulong)n; | ||||
| } | ||||
| } | ||||
|
|
||||
| void | ||||
| test_oring( void ) { | ||||
| fd_http_server_params_t params = { | ||||
|
|
@@ -21,8 +67,9 @@ test_oring( void ) { | |||
| .ws_message = NULL, | ||||
| }; | ||||
|
|
||||
| uchar scratch[ 1633024 ] __attribute__((aligned(128UL))); | ||||
| FD_TEST( fd_http_server_footprint( params )==1633024 ); | ||||
| ulong actual_footprint = fd_http_server_footprint( params ); | ||||
| uchar scratch[ 329344 ] __attribute__((aligned(128UL))); | ||||
| FD_TEST( actual_footprint==329344 ); | ||||
| fd_http_server_t * http = fd_http_server_join( fd_http_server_new( scratch, params, callbacks, NULL ) ); | ||||
|
|
||||
| http->stage_off = 6UL; | ||||
|
|
@@ -82,12 +129,78 @@ test_oring( void ) { | |||
| FD_TEST( http->stage_comp_len==0UL ); | ||||
| } | ||||
|
|
||||
| void | ||||
| test_content_length_overflow_close( void ) { | ||||
| fd_http_server_params_t params = { | ||||
| .max_connection_cnt = 1UL, | ||||
| .max_ws_connection_cnt = 0UL, | ||||
| .max_request_len = 1024UL, | ||||
| .max_ws_recv_frame_len = 1024UL, | ||||
| .max_ws_send_frame_cnt = 1UL, | ||||
| .outgoing_buffer_sz = 1024UL, | ||||
| }; | ||||
|
|
||||
| overflow_close_state_t state = {0}; | ||||
| fd_http_server_callbacks_t callbacks = { | ||||
| .request = request_noop, | ||||
| .close = close_capture, | ||||
| .ws_open = NULL, | ||||
| .ws_close = NULL, | ||||
| .ws_message = NULL, | ||||
| }; | ||||
|
|
||||
| FD_LOG_NOTICE(( "footprint %lu", fd_http_server_footprint( params ) )); | ||||
|
||||
| FD_LOG_NOTICE(( "footprint %lu", fd_http_server_footprint( params ) )); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_ulong()usesULONG_MAXas an error sentinel but also allows returning the legitimate valueULONG_MAX(e.g., parsing "18446744073709551615"). Inread_conn_http()the caller treatscontent_len==ULONG_MAXas a parse failure, so a valid Content-Length ofULONG_MAXbecomes indistinguishable from an error, and overflow cases can’t be classified separately.Consider changing the helper to return a success flag / error code (e.g.,
int parse_ulong(..., ulong *out)), so callers can distinguish invalid format vs overflow and still acceptULONG_MAXas a valid value when desired.