JoeDog / siege

Siege is an http load tester and benchmarking utility
GNU General Public License v3.0
5.95k stars 385 forks source link

Segfault when no status line received #78

Closed lenerd closed 8 years ago

lenerd commented 8 years ago

Siege expects a HTTP response with a valid status line (e.g. HTTP/1.0 200 OK).

It gets parsed here:

    if (strncasecmp(line, "http", 4) == 0) {
      response_set_code(resp, line);
    }

and here:

BOOLEAN
response_set_code(RESPONSE this, char *line)
{ // we expect the start line: HTTP/1.0 200 OK
  char *tmp = line;
  char  arr[32];

  if (strncasecmp(line, "http", 4) == 0) {
    int num = atoi(tmp+9);
    if (num > 1) {
      memset(arr, '\0', sizeof(arr));
      strncpy(arr, line, 8);
      hash_add(this->headers, PROTOCOL, arr);
      hash_add(this->headers, RESPONSE_CODE, line+9);
      return TRUE;
    }   
  }   
  return FALSE; 
} 

If there is no such status line (or another line starting with "http" and having a number > 0 at position 9) in the header the entry RESPONSE_CODE in the headers dictionary will not be populated. On retrieval by hash_get a null pointer is be returned

In the functions response_get_code, response_success and response_failure the pointer from hash_get is thrown into atoi without further checking and it results in a segmentation fault, if RESPONSE_CODE is not found.

int
response_get_code(RESPONSE this)
{ 
  return atoi((char *)hash_get(this->headers, RESPONSE_CODE));
}

Steps to reproduce:

  1. echo -n "foo\r\n\r\n" | nc -lp 1337
  2. siege -c 1 -r 1 http://localhost:1337

Backtrace from GDB:

#0  0x00007f3d1dea0d45 in ____strtoll_l_internal () from /usr/lib/libc.so.6
#1  0x00007f3d1de9e2e0 in atoi () from /usr/lib/libc.so.6
#2  0x0000000000417b11 in response_success (this=0x7f3d1801c5b0)
    at response.c:133
#3  0x0000000000405fbd in __http (this=0x1257890, U=0x1257580)
    at browser.c:523
#4  0x0000000000405b25 in __request (this=0x1257890, U=0x1257580)
    at browser.c:406
#5  0x000000000040561f in start (this=0x1257890) at browser.c:295
#6  0x000000000040b20f in crew_thread (crew=0x12579b0) at crew.c:141
#7  0x00007f3d1e425454 in start_thread () from /usr/lib/libpthread.so.0
#8  0x00007f3d1df527df in clone () from /usr/lib/libc.so.6
JoeDog commented 8 years ago

Can you give me a sense of what you're trying to do? I can solve this problem like this:

int response_success(RESPONSE this) { if ((char )hash_get(this->headers, RESPONSE_CODE) == NULL) { return 0; } int code = atoi((char )hash_get(this->headers, RESPONSE_CODE)); return (code < 400 || code == 401 || code == 407) ? 1 : 0; }

int response_failure(RESPONSE this) { if ((char )hash_get(this->headers, RESPONSE_CODE) == NULL) { return 0; } int code = atoi((char )hash_get(this->headers, RESPONSE_CODE)); return (code >= 400 && code != 401 && code != 407) ? 1 : 0; }

But that doesn't necessarily mean it works for your usecase.

On Fri, Sep 9, 2016 at 8:53 AM, Lennart Braun notifications@github.com wrote:

Siege expects a HTTP response with a valid status line (e.g. HTTP/1.0 200 OK).

It gets parsed here https://github.com/JoeDog/siege/blob/127bf1d38be63cfb81592cbc1402296f80a89ac1/src/response.c#L98-L115:

if (strncasecmp(line, "http", 4) == 0) {
  response_set_code(resp, line);
}

and here https://github.com/JoeDog/siege/blob/127bf1d38be63cfb81592cbc1402296f80a89ac1/src/http.c#L461-L463 :

BOOLEAN response_set_code(RESPONSE this, char line) { // we expect the start line: HTTP/1.0 200 OK char tmp = line; char arr[32];

if (strncasecmp(line, "http", 4) == 0) { int num = atoi(tmp+9); if (num > 1) { memset(arr, '\0', sizeof(arr)); strncpy(arr, line, 8); hash_add(this->headers, PROTOCOL, arr); hash_add(this->headers, RESPONSE_CODE, line+9); return TRUE; } } return FALSE; }

If there is no such status line (or another line starting with "http" and having a number > 0 at position 9) in the header the entry RESPONSE_CODE in the headers dictionary will not be populated. On retrieval by hash_get a null pointer is be returned

In the functions response_get_code https://github.com/JoeDog/siege/blob/127bf1d38be63cfb81592cbc1402296f80a89ac1/src/response.c#L117-L121, response_success https://github.com/JoeDog/siege/blob/127bf1d38be63cfb81592cbc1402296f80a89ac1/src/response.c#L130-L135 and response_failure https://github.com/JoeDog/siege/blob/127bf1d38be63cfb81592cbc1402296f80a89ac1/src/response.c#L137-L142 the pointer from hash_get is thrown into atoi without further checking and it results in a segmentation fault, if RESPONSE_CODE is not found.

int response_get_code(RESPONSE this) { return atoi((char *)hash_get(this->headers, RESPONSE_CODE)); }

Steps to reproduce:

  1. echo -n "foo\r\n\r\n" | nc -lp 1337
  2. siege -c 1 -r 1 http://localhost:1337

Backtrace from GDB:

0 0x00007f3d1dea0d45 in ____strtoll_l_internal () from /usr/lib/libc.so.6

1 0x00007f3d1de9e2e0 in atoi () from /usr/lib/libc.so.6

2 0x0000000000417b11 in response_success (this=0x7f3d1801c5b0)

at response.c:133

3 0x0000000000405fbd in __http (this=0x1257890, U=0x1257580)

at browser.c:523

4 0x0000000000405b25 in __request (this=0x1257890, U=0x1257580)

at browser.c:406

5 0x000000000040561f in start (this=0x1257890) at browser.c:295

6 0x000000000040b20f in crew_thread (crew=0x12579b0) at crew.c:141

7 0x00007f3d1e425454 in start_thread () from /usr/lib/libpthread.so.0

8 0x00007f3d1df527df in clone () from /usr/lib/libc.so.6

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/JoeDog/siege/issues/78, or mute the thread https://github.com/notifications/unsubscribe-auth/AFMT5vhk4tsv94yVAU1c3seCKbmXr7I9ks5qoVbegaJpZM4J5BGa .

lenerd commented 8 years ago

The application I tested had a bug that resulted in a missing HTTP response header. It is just more helpful to get an error message than a segmentation fault.

Maybe you want to return a 1 in response_failure to indicate that there is a problem?

JoeDog commented 8 years ago

Good suggestion. Thanks.

On Mon, Sep 12, 2016 at 1:07 PM, Lennart Braun notifications@github.com wrote:

The application I tested had a bug that resulted in a missing HTTP response header. It is just more helpful to get an error message than a segmentation fault.

Maybe you want to return a 1 in response_failure to indicate that there is a problem?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/JoeDog/siege/issues/78#issuecomment-246417309, or mute the thread https://github.com/notifications/unsubscribe-auth/AFMT5vQI7kgbd2pVq_QBqqGczr000mwZks5qpYbNgaJpZM4J5BGa .