View Issue Details

IDProjectCategoryView StatusLast Update
0005003libmicrohttpdotherpublic2017-05-28 22:50
Reporterdohmniq Assigned ToKarlson2k  
PrioritynormalSeveritycrashReproducibilityalways
Status closedResolutionfixed 
OSFreeBSDOS Version11 
Product Version0.9.53 
Target Version0.9.55Fixed in Version0.9.55 
Summary0005003: queuing a non-upgrade response (e.g. 404 Not Found) to a client's websocket request causes segfault
DescriptionIf a client calls a ws:// URL but the MHD server wants to respond with a non-101 response (e.g. 404 Not Found) then MHD dies with segmentation fault.

This is inside keepalive_possible() around line 885 in src/microhttpd/connection.c.
The segfault is because inside of the test "NULL == connection->response->upgrade_handler" connection->response itself is NULL.
Presumably connection->response has been 'used'/output and then cleaned up, i.e. free()d.

I'm not sure about keepalive_possible's expected output regarding the RFCs so I'm not providing a patch for that.

I am attaching a patch to test_upgrade.c which covers this case though.
Steps To Reproduceapply attached patch against test_upgrade.c, run gmake in src/microhttpd then run .libs/test_upgrade
Additional Informationgnunet bugtracker could do with an additional option under "Category" with the value "websockets"?

See also bug 4996 for possibly related issue do with wrongly emitting "Connection: Keep-Alive" header?
TagsNo tags attached.
Attached Files
test_upgrade.c.diff (6,503 bytes)   
--- test_upgrade.c.orig	2017-05-04 09:03:02.300982000 +0100
+++ test_upgrade.c	2017-05-04 09:18:04.048613000 +0100
@@ -838,6 +838,33 @@
   return ret;
 }
 
+static int
+ahc_upgrade_404 (void *cls,
+             struct MHD_Connection *connection,
+             const char *url,
+             const char *method,
+             const char *version,
+             const char *upload_data,
+             size_t *upload_data_size,
+             void **con_cls)
+{
+  struct MHD_Response *resp;
+  int ret;
+
+  if (NULL == *con_cls)
+    abort ();
+  if (! pthread_equal (**((pthread_t**)con_cls), pthread_self ()))
+    abort ();
+
+  char page[] = "Not found";
+
+  resp = MHD_create_response_from_buffer( strlen(page), (void*) page, MHD_RESPMEM_MUST_COPY );
+  ret = MHD_queue_response( connection, 404, resp );
+
+  MHD_destroy_response (resp);
+  return ret;
+}
+
 
 /**
  * Run the MHD external event loop using select.
@@ -976,7 +1003,8 @@
  */
 static int
 test_upgrade (int flags,
-              unsigned int pool)
+              unsigned int pool,
+              unsigned int return404)
 {
   struct MHD_Daemon *d = NULL;
   wr_socket sock;
@@ -988,11 +1016,13 @@
 
   done = false;
 
+  MHD_AccessHandlerCallback ahc = return404 ? &ahc_upgrade_404 : &ahc_upgrade;
+
   if (!test_tls)
     d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE,
 			  1080,
 			  NULL, NULL,
-			  &ahc_upgrade, NULL,
+			  ahc, NULL,
 			  MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
 			  MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL,
 			  MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
@@ -1003,7 +1033,7 @@
     d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE | MHD_USE_TLS,
                           1080,
                           NULL, NULL,
-                          &ahc_upgrade, NULL,
+                          ahc, NULL,
                           MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
                           MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL,
                           MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
@@ -1129,6 +1159,7 @@
     printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n", test_tls ? "TLS" : "plain");
   /* try external select */
   res = test_upgrade (0,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1138,6 +1169,7 @@
 
   /* Try external auto */
   res = test_upgrade (MHD_USE_AUTO,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1147,6 +1179,7 @@
 
 #ifdef EPOLL_SUPPORT
   res = test_upgrade (MHD_USE_EPOLL,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1157,6 +1190,7 @@
 
   /* Test thread-per-connection */
   res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1165,6 +1199,7 @@
     printf ("PASSED: Upgrade with thread per connection.\n");
 
   res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1173,6 +1208,7 @@
     printf ("PASSED: Upgrade with thread per connection and 'auto'.\n");
 #ifdef HAVE_POLL
   res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1183,6 +1219,7 @@
 
   /* Test different event loops, with and without thread pool */
   res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1190,13 +1227,15 @@
   else if (verbose)
     printf ("PASSED: Upgrade with internal select.\n");
   res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD,
-                      2);
+                      2,
+                      0);
   error_count += res;
   if (res)
     fprintf (stderr, "FAILED: Upgrade with internal select with thread pool, return code %d.\n", res);
   else if (verbose)
     printf ("PASSED: Upgrade with internal select with thread pool.\n");
   res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1204,7 +1243,8 @@
   else if (verbose)
     printf ("PASSED: Upgrade with internal 'auto'.\n");
   res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
-                      2);
+                      2,
+                      0);
   error_count += res;
   if (res)
     fprintf (stderr, "FAILED: Upgrade with internal 'auto' with thread pool, return code %d.\n", res);
@@ -1212,6 +1252,7 @@
     printf ("PASSED: Upgrade with internal 'auto' with thread pool.\n");
 #ifdef HAVE_POLL
   res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD,
+                      0,
                       0);
   error_count += res;
   if (res)
@@ -1219,7 +1260,8 @@
   else if (verbose)
     printf ("PASSED: Upgrade with internal poll.\n");
   res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD,
-                      2);
+                      2,
+                      0);
   if (res)
     fprintf (stderr, "FAILED: Upgrade with internal poll with thread pool, return code %d.\n", res);
   else if (verbose)
@@ -1227,18 +1269,28 @@
 #endif
 #ifdef EPOLL_SUPPORT
   res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD,
+                      0,
                       0);
   if (res)
     fprintf (stderr, "FAILED: Upgrade with internal epoll, return code %d.\n", res);
   else if (verbose)
     printf ("PASSED: Upgrade with internal epoll.\n");
   res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD,
-                      2);
+                      2,
+                      0);
   if (res)
     fprintf (stderr, "FAILED: Upgrade with internal epoll, return code %d.\n", res);
   else if (verbose)
     printf ("PASSED: Upgrade with internal epoll.\n");
 #endif
+  res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
+                      0,
+                      1);
+  error_count += res;
+  if (res)
+    fprintf (stderr, "FAILED: Upgrade with 404 response, return code %d.\n", res);
+  else if (verbose)
+    printf ("PASSED: Upgrade with 404 response.\n");
   /* report result */
   if (0 != error_count)
     fprintf (stderr,
test_upgrade.c.diff (6,503 bytes)   

Activities

Issue History

Date Modified Username Field Change
2017-05-04 10:45 dohmniq New Issue
2017-05-04 10:45 dohmniq File Added: test_upgrade.c.diff
2017-05-04 22:44 Christian Grothoff Assigned To => Karlson2k
2017-05-04 22:44 Christian Grothoff Status new => resolved
2017-05-04 22:44 Christian Grothoff Fixed in Version => 0.9.55
2017-05-04 22:44 Christian Grothoff Target Version => 0.9.55
2017-05-28 22:50 Christian Grothoff Status resolved => closed
2017-05-28 22:50 Christian Grothoff Resolution open => fixed