]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Fix unresponsive cupsd process caused by a slow client
authorZdenek Dohnal <zdohnal@redhat.com>
Mon, 13 Oct 2025 08:16:48 +0000 (10:16 +0200)
committerZdenek Dohnal <zdohnal@redhat.com>
Tue, 18 Nov 2025 16:05:49 +0000 (17:05 +0100)
If client is very slow, it will slow cupsd process for other clients.
The fix is the best effort without turning scheduler cupsd into
multithreaded process which would be too complex and error-prone when
backporting to 2.4.x series.

The fix for unencrypted communication is to follow up on communication
only if there is the whole line on input, and the waiting time is
guarded by timeout.

Encrypted communication now starts after we have the whole client hello
packet, which conflicts with optional upgrade support to HTTPS via
methods other than method OPTIONS, so this optional support defined in
RFC 2817, section 3.1 is removed. Too slow or incomplete requests are
handled by connection timeout.

Fixes CVE-2025-58436

cups/http-private.h
cups/http.c
cups/tls-openssl.c
scheduler/client.c
scheduler/client.h
scheduler/select.c

index d9854faed72d8d49ab52be801b1430bf86fcfee7..2d90350329e1a963af220c6fc077d43a2e6f2072 100644 (file)
@@ -121,6 +121,7 @@ extern "C" {
  * Constants...
  */
 
+#  define _HTTP_MAX_BUFFER     32768   /* Size of read buffer */
 #  define _HTTP_MAX_SBUFFER    65536   /* Size of (de)compression buffer */
 #  define _HTTP_RESOLVE_DEFAULT        0       /* Just resolve with default options */
 #  define _HTTP_RESOLVE_STDERR 1       /* Log resolve progress to stderr */
@@ -233,8 +234,8 @@ struct _http_s                              /**** HTTP connection structure ****/
   http_encoding_t      data_encoding;  /* Chunked or not */
   int                  _data_remaining;/* Number of bytes left (deprecated) */
   int                  used;           /* Number of bytes used in buffer */
-  char                 buffer[HTTP_MAX_BUFFER];
-                                       /* Buffer for incoming data */
+  char                 _buffer[HTTP_MAX_BUFFER];
+                                       /* Old read buffer (deprecated) */
   int                  _auth_type;     /* Authentication in use (deprecated) */
   unsigned char                _md5_state[88]; /* MD5 state (deprecated) */
   char                 nonce[HTTP_MAX_VALUE];
@@ -308,6 +309,8 @@ struct _http_s                              /**** HTTP connection structure ****/
                                        /* Allocated field values */
                        *default_fields[HTTP_FIELD_MAX];
                                        /* Default field values, if any */
+  char                 buffer[_HTTP_MAX_BUFFER];
+                                       /* Read buffer */
 };
 #  endif /* !_HTTP_NO_PRIVATE */
 
index 7a42cb3d69b0704ce4837130b4882aefcae03222..214e451585c9c69e6a65e7d8752c03005301e716 100644 (file)
@@ -53,7 +53,7 @@ static http_t         *http_create(const char *host, int port,
 static void            http_debug_hex(const char *prefix, const char *buffer,
                                       int bytes);
 #endif /* DEBUG */
-static ssize_t         http_read(http_t *http, char *buffer, size_t length);
+static ssize_t         http_read(http_t *http, char *buffer, size_t length, int timeout);
 static ssize_t         http_read_buffered(http_t *http, char *buffer, size_t length);
 static ssize_t         http_read_chunk(http_t *http, char *buffer, size_t length);
 static int             http_send(http_t *http, http_state_t request,
@@ -1206,7 +1206,7 @@ httpGets(char   *line,                    /* I - Line to read into */
         return (NULL);
       }
 
-      bytes = http_read(http, http->buffer + http->used, (size_t)(HTTP_MAX_BUFFER - http->used));
+      bytes = http_read(http, http->buffer + http->used, (size_t)(_HTTP_MAX_BUFFER - http->used), http->wait_value);
 
       DEBUG_printf(("4httpGets: read " CUPS_LLFMT " bytes.", CUPS_LLCAST bytes));
 
@@ -1726,24 +1726,13 @@ httpPeek(http_t *http,                  /* I - HTTP connection */
 
     ssize_t    buflen;                 /* Length of read for buffer */
 
-    if (!http->blocking)
-    {
-      while (!httpWait(http, http->wait_value))
-      {
-       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
-         continue;
-
-       return (0);
-      }
-    }
-
     if ((size_t)http->data_remaining > sizeof(http->buffer))
       buflen = sizeof(http->buffer);
     else
       buflen = (ssize_t)http->data_remaining;
 
     DEBUG_printf(("2httpPeek: Reading %d bytes into buffer.", (int)buflen));
-    bytes = http_read(http, http->buffer, (size_t)buflen);
+    bytes = http_read(http, http->buffer, (size_t)buflen, http->wait_value);
 
     DEBUG_printf(("2httpPeek: Read " CUPS_LLFMT " bytes into buffer.",
                   CUPS_LLCAST bytes));
@@ -1764,9 +1753,9 @@ httpPeek(http_t *http,                    /* I - HTTP connection */
     int                zerr;                   /* Decompressor error */
     z_stream   stream;                 /* Copy of decompressor stream */
 
-    if (http->used > 0 && ((z_stream *)http->stream)->avail_in < HTTP_MAX_BUFFER)
+    if (http->used > 0 && ((z_stream *)http->stream)->avail_in < _HTTP_MAX_BUFFER)
     {
-      size_t buflen = HTTP_MAX_BUFFER - ((z_stream *)http->stream)->avail_in;
+      size_t buflen = _HTTP_MAX_BUFFER - ((z_stream *)http->stream)->avail_in;
                                        /* Number of bytes to copy */
 
       if (((z_stream *)http->stream)->avail_in > 0 &&
@@ -2024,7 +2013,7 @@ httpRead2(http_t *http,                   /* I - HTTP connection */
 
       if (bytes == 0)
       {
-        ssize_t buflen = HTTP_MAX_BUFFER - (ssize_t)((z_stream *)http->stream)->avail_in;
+        ssize_t buflen = _HTTP_MAX_BUFFER - (ssize_t)((z_stream *)http->stream)->avail_in;
                                        /* Additional bytes for buffer */
 
         if (buflen > 0)
@@ -2774,7 +2763,7 @@ int                                       /* O - 1 to continue, 0 to stop */
 _httpUpdate(http_t        *http,       /* I - HTTP connection */
             http_status_t *status)     /* O - Current HTTP status */
 {
-  char         line[32768],            /* Line from connection... */
+  char         line[_HTTP_MAX_BUFFER], /* Line from connection... */
                *value;                 /* Pointer to value on line */
   http_field_t field;                  /* Field index */
   int          major, minor;           /* HTTP version numbers */
@@ -2782,12 +2771,46 @@ _httpUpdate(http_t        *http,        /* I - HTTP connection */
 
   DEBUG_printf(("_httpUpdate(http=%p, status=%p), state=%s", (void *)http, (void *)status, httpStateString(http->state)));
 
+  /* When doing non-blocking I/O, make sure we have a whole line... */
+  if (!http->blocking)
+  {
+    ssize_t    bytes;                  /* Bytes "peeked" from connection */
+
+    /* See whether our read buffer is full... */
+    DEBUG_printf(("2_httpUpdate: used=%d", http->used));
+
+    if (http->used > 0 && !memchr(http->buffer, '\n', (size_t)http->used) && (size_t)http->used < sizeof(http->buffer))
+    {
+      /* No, try filling in more data... */
+      if ((bytes = http_read(http, http->buffer + http->used, sizeof(http->buffer) - (size_t)http->used, /*timeout*/0)) > 0)
+      {
+       DEBUG_printf(("2_httpUpdate: Read %d bytes.", (int)bytes));
+       http->used += (int)bytes;
+      }
+    }
+
+    /* Peek at the incoming data... */
+    if (!http->used || !memchr(http->buffer, '\n', (size_t)http->used))
+    {
+      /* Don't have a full line, tell the reader to try again when there is more data... */
+      DEBUG_puts("1_htttpUpdate: No newline in buffer yet.");
+      if ((size_t)http->used == sizeof(http->buffer))
+       *status = HTTP_STATUS_ERROR;
+      else
+       *status = HTTP_STATUS_CONTINUE;
+      return (0);
+    }
+
+    DEBUG_puts("2_httpUpdate: Found newline in buffer.");
+  }
+
  /*
   * Grab a single line from the connection...
   */
 
   if (!httpGets(line, sizeof(line), http))
   {
+    DEBUG_puts("1_httpUpdate: Error reading request line.");
     *status = HTTP_STATUS_ERROR;
     return (0);
   }
@@ -4140,7 +4163,8 @@ http_debug_hex(const char *prefix,        /* I - Prefix for line */
 static ssize_t                         /* O - Number of bytes read or -1 on error */
 http_read(http_t *http,                        /* I - HTTP connection */
           char   *buffer,              /* I - Buffer */
-          size_t length)               /* I - Maximum bytes to read */
+          size_t length,               /* I - Maximum bytes to read */
+          int    timeout)              /* I - Wait timeout */
 {
   ssize_t      bytes;                  /* Bytes read */
 
@@ -4149,7 +4173,7 @@ http_read(http_t *http,                   /* I - HTTP connection */
 
   if (!http->blocking || http->timeout_value > 0.0)
   {
-    while (!httpWait(http, http->wait_value))
+    while (!_httpWait(http, timeout, 1))
     {
       if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
        continue;
@@ -4252,7 +4276,7 @@ http_read_buffered(http_t *http,  /* I - HTTP connection */
     else
       bytes = (ssize_t)length;
 
-    DEBUG_printf(("8http_read: Grabbing %d bytes from input buffer.",
+    DEBUG_printf(("8http_read_buffered: Grabbing %d bytes from input buffer.",
                   (int)bytes));
 
     memcpy(buffer, http->buffer, (size_t)bytes);
@@ -4262,7 +4286,7 @@ http_read_buffered(http_t *http,  /* I - HTTP connection */
       memmove(http->buffer, http->buffer + bytes, (size_t)http->used);
   }
   else
-    bytes = http_read(http, buffer, length);
+    bytes = http_read(http, buffer, length, http->wait_value);
 
   return (bytes);
 }
@@ -4603,15 +4627,15 @@ http_set_timeout(int    fd,             /* I - File descriptor */
 static void
 http_set_wait(http_t *http)            /* I - HTTP connection */
 {
-  if (http->blocking)
-  {
-    http->wait_value = (int)(http->timeout_value * 1000);
+  http->wait_value = (int)(http->timeout_value * 1000);
 
-    if (http->wait_value <= 0)
+  if (http->wait_value <= 0)
+  {
+    if (http->blocking)
       http->wait_value = 60000;
+    else
+      http->wait_value = 1000;
   }
-  else
-    http->wait_value = 10000;
 }
 
 
index 9fcbe0af3b64d5c340ed1cb9b632390426eaaf97..f746f4cba498c63165636dda1ade4e3a7242fe7e 100644 (file)
@@ -215,12 +215,14 @@ cupsMakeServerCredentials(
   // Save them...
   if ((bio = BIO_new_file(keyfile, "wb")) == NULL)
   {
+    DEBUG_printf(("1cupsMakeServerCredentials: Unable to create private key file '%s': %s", keyfile, strerror(errno)));
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
     goto done;
   }
 
   if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL))
   {
+    DEBUG_puts("1cupsMakeServerCredentials: PEM_write_bio_PrivateKey failed.");
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write private key."), 1);
     BIO_free(bio);
     goto done;
@@ -230,12 +232,14 @@ cupsMakeServerCredentials(
 
   if ((bio = BIO_new_file(crtfile, "wb")) == NULL)
   {
+    DEBUG_printf(("1cupsMakeServerCredentials: Unable to create certificate file '%s': %s", crtfile, strerror(errno)));
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
     goto done;
   }
 
   if (!PEM_write_bio_X509(bio, cert))
   {
+    DEBUG_puts("1cupsMakeServerCredentials: PEM_write_bio_X509 failed.");
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate."), 1);
     BIO_free(bio);
     goto done;
@@ -1082,10 +1086,10 @@ _httpTLSStart(http_t *http)             // I - Connection to server
 
       if (!cupsMakeServerCredentials(tls_keypath, cn, 0, NULL, time(NULL) + 3650 * 86400))
       {
-       DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
+       DEBUG_printf(("4_httpTLSStart: cupsMakeServerCredentials failed: %s", cupsLastErrorString()));
        http->error  = errno = EINVAL;
        http->status = HTTP_STATUS_ERROR;
-       _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
+//     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
        SSL_CTX_free(context);
         _cupsMutexUnlock(&tls_mutex);
 
@@ -1346,14 +1350,17 @@ http_bio_read(BIO  *h,                  // I - BIO data
 
   http = (http_t *)BIO_get_data(h);
 
-  if (!http->blocking)
+  if (!http->blocking || http->timeout_value > 0.0)
   {
    /*
     * Make sure we have data before we read...
     */
 
-    if (!_httpWait(http, 10000, 0))
+    while (!_httpWait(http, http->wait_value, 0))
     {
+      if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
+       continue;
+
 #ifdef WIN32
       http->error = WSAETIMEDOUT;
 #else
index f0349a6c91fd8832b4e81dad55868bff8220f4db..9593c9138c4373fda41256fadd8cb63daa086a97 100644 (file)
 
 static int             check_if_modified(cupsd_client_t *con,
                                          struct stat *filestats);
-static int             compare_clients(cupsd_client_t *a, cupsd_client_t *b,
-                                       void *data);
 #ifdef HAVE_TLS
-static int             cupsd_start_tls(cupsd_client_t *con, http_encryption_t e);
+static int             check_start_tls(cupsd_client_t *con);
 #endif /* HAVE_TLS */
+static int             compare_clients(cupsd_client_t *a, cupsd_client_t *b,
+                                       void *data);
 static char            *get_file(cupsd_client_t *con, struct stat *filestats,
                                  char *filename, size_t len);
 static http_status_t   install_cupsd_conf(cupsd_client_t *con);
@@ -367,14 +367,20 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   if (lis->encryption == HTTP_ENCRYPTION_ALWAYS)
   {
    /*
-    * https connection; go secure...
+    * HTTPS connection, force TLS negotiation...
     */
 
-    if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS))
-      cupsdCloseClient(con);
+    con->tls_start = time(NULL);
+    con->encryption = HTTP_ENCRYPTION_ALWAYS;
   }
   else
+  {
+   /*
+    * HTTP connection, but check for HTTPS negotiation on first data...
+    */
+
     con->auto_ssl = 1;
+  }
 #endif /* HAVE_TLS */
 }
 
@@ -613,17 +619,46 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
     con->auto_ssl = 0;
 
-    if (recv(httpGetFd(con->http), buf, 1, MSG_PEEK) == 1 &&
-        (!buf[0] || !strchr("DGHOPT", buf[0])))
+    if (recv(httpGetFd(con->http), buf, 5, MSG_PEEK) == 5 && buf[0] == 0x16 && buf[1] == 3 && buf[2])
     {
      /*
-      * Encrypt this connection...
+      * Client hello record, encrypt this connection...
       */
 
-      cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw first byte %02X, auto-negotiating SSL/TLS session.", buf[0] & 255);
+      cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw client hello record, auto-negotiating TLS session.");
+      con->tls_start = time(NULL);
+      con->encryption = HTTP_ENCRYPTION_ALWAYS;
+    }
+  }
 
-      if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS))
-        cupsdCloseClient(con);
+  if (con->tls_start)
+  {
+   /*
+    * Try negotiating TLS...
+    */
+
+    int tls_status = check_start_tls(con);
+
+    if (tls_status < 0)
+    {
+     /*
+      * TLS negotiation failed, close the connection.
+      */
+
+      cupsdCloseClient(con);
+      return;
+    }
+    else if (tls_status == 0)
+    {
+     /*
+      * Nothing to do yet...
+      */
+
+      if ((time(NULL) - con->tls_start) > 5)
+      {
+       // Timeout, close the connection...
+       cupsdCloseClient(con);
+      }
 
       return;
     }
@@ -787,9 +822,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
         * Parse incoming parameters until the status changes...
        */
 
-        while ((status = httpUpdate(con->http)) == HTTP_STATUS_CONTINUE)
-         if (!httpGetReady(con->http))
-           break;
+       status = httpUpdate(con->http);
 
        if (status != HTTP_STATUS_OK && status != HTTP_STATUS_CONTINUE)
        {
@@ -951,11 +984,10 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          return;
        }
 
-        if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED))
-        {
-         cupsdCloseClient(con);
-         return;
-       }
+       con->tls_start = time(NULL);
+       con->tls_upgrade = 1;
+       con->encryption = HTTP_ENCRYPTION_REQUIRED;
+       return;
 #else
        if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
@@ -994,32 +1026,11 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
       if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION),
                             "Upgrade") && !httpIsEncrypted(con->http))
       {
-#ifdef HAVE_TLS
-       /*
-        * Do encryption stuff...
-       */
-
-        httpClearFields(con->http);
-
-       if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL,
-                            CUPSD_AUTH_NONE))
-       {
-         cupsdCloseClient(con);
-         return;
-       }
-
-        if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED))
-        {
-         cupsdCloseClient(con);
-         return;
-       }
-#else
        if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
        }
-#endif /* HAVE_TLS */
       }
 
       if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_STATUS_OK)
@@ -2689,6 +2700,69 @@ check_if_modified(
 }
 
 
+#ifdef HAVE_TLS
+/*
+ * 'check_start_tls()' - Start encryption on a connection.
+ */
+
+static int                             /* O - 0 to continue, 1 on success, -1 on error */
+check_start_tls(cupsd_client_t *con)   /* I - Client connection */
+{
+  unsigned char        chello[4096];           /* Client hello record */
+  ssize_t      chello_bytes;           /* Bytes read/peeked */
+  int          chello_len;             /* Length of record */
+
+
+ /*
+  * See if we have a good and complete client hello record...
+  */
+
+  if ((chello_bytes = recv(httpGetFd(con->http), (char *)chello, sizeof(chello), MSG_PEEK)) < 5)
+    return (0);                                /* Not enough bytes (yet) */
+
+  if (chello[0] != 0x016 || chello[1] != 3 || chello[2] == 0)
+    return (-1);                       /* Not a TLS Client Hello record */
+
+  chello_len = (chello[3] << 8) | chello[4];
+
+  if ((chello_len + 5) > chello_bytes)
+    return (0);                                /* Not enough bytes yet */
+
+ /*
+  * OK, we do, try negotiating...
+  */
+
+  con->tls_start = 0;
+
+  if (httpEncryption(con->http, con->encryption))
+  {
+    cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s", cupsLastErrorString());
+    return (-1);
+  }
+
+  cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted.");
+
+  if (con->tls_upgrade)
+  {
+    // Respond to the original OPTIONS command...
+    con->tls_upgrade = 0;
+
+    httpClearFields(con->http);
+    httpClearCookie(con->http);
+    httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
+
+    if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
+    {
+      cupsdCloseClient(con);
+      return (-1);
+    }
+  }
+
+  return (1);
+}
+#endif /* HAVE_TLS */
+
+
 /*
  * 'compare_clients()' - Compare two client connections.
  */
@@ -2709,28 +2783,6 @@ compare_clients(cupsd_client_t *a,       /* I - First client */
 }
 
 
-#ifdef HAVE_TLS
-/*
- * 'cupsd_start_tls()' - Start encryption on a connection.
- */
-
-static int                             /* O - 0 on success, -1 on error */
-cupsd_start_tls(cupsd_client_t    *con,        /* I - Client connection */
-                http_encryption_t e)   /* I - Encryption mode */
-{
-  if (httpEncryption(con->http, e))
-  {
-    cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s",
-                   cupsLastErrorString());
-    return (-1);
-  }
-
-  cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted.");
-  return (0);
-}
-#endif /* HAVE_TLS */
-
-
 /*
  * 'get_file()' - Get a filename and state info.
  */
index 9fe4e2ea6229647637862a6fc7c0bc3d07e95f23..2939ce997e5924abbf6c8329553b595b1496176f 100644 (file)
@@ -53,6 +53,9 @@ struct cupsd_client_s
   cups_lang_t          *language;      /* Language to use */
 #ifdef HAVE_TLS
   int                  auto_ssl;       /* Automatic test for SSL/TLS */
+  time_t               tls_start;      /* Do TLS negotiation? */
+  int                  tls_upgrade;    /* Doing TLS upgrade via OPTIONS? */
+  http_encryption_t    encryption;     /* Type of TLS negotiation */
 #endif /* HAVE_TLS */
   http_addr_t          clientaddr;     /* Client's server address */
   char                 clientname[256];/* Client's server name for connection */
index 2e64f2a7ed23a957f09b605b86b4db23802572aa..ac6205c51b97b6e32c73ba2f5c5165f91878b72d 100644 (file)
@@ -408,6 +408,9 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
 
   cupsd_in_select = 1;
 
+  // Prevent 100% CPU by releasing control before the kevent call...
+  usleep(1);
+
   if (timeout >= 0 && timeout < 86400)
   {
     ktimeout.tv_sec  = timeout;
@@ -452,6 +455,9 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
     struct epoll_event *event;         /* Current event */
 
 
+    // Prevent 100% CPU by releasing control before the epoll_wait call...
+    usleep(1);
+
     if (timeout >= 0 && timeout < 86400)
       nfds = epoll_wait(cupsd_epoll_fd, cupsd_epoll_events, MaxFDs,
                        timeout * 1000);
@@ -544,6 +550,9 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
     }
   }
 
+  // Prevent 100% CPU by releasing control before the poll call...
+  usleep(1);
+
   if (timeout >= 0 && timeout < 86400)
     nfds = poll(cupsd_pollfds, (nfds_t)count, timeout * 1000);
   else
@@ -597,6 +606,9 @@ cupsdDoSelect(long timeout)         /* I - Timeout in seconds */
   cupsd_current_input  = cupsd_global_input;
   cupsd_current_output = cupsd_global_output;
 
+  // Prevent 100% CPU by releasing control before the select call...
+  usleep(1);
+
   if (timeout >= 0 && timeout < 86400)
   {
     stimeout.tv_sec  = timeout;