CVE-2023-25824: Loop with Unreachable Exit Condition ('Infinite Loop') (CWE-835)
value: Mod_gnutls is a TLS module for Apache HTTPD based on GnuTLS. Versions from 0.9.0 to 0.12.0 (including) did not properly fail blocking read operations on TLS connections when the transport hit timeouts. Instead it entered an endless loop retrying the read operation, consuming CPU resources. This could be exploited for denial of service attacks. If trace level logging was enabled, it would also produce an excessive amount of log output during the loop, consuming disk space. The problem has been fixed in commit d7eec4e598158ab6a98bf505354e84352f9715ec, please update to version 0.12.1. There are no workarounds, users who cannot update should apply the errno fix detailed in the security advisory. 

CWE description: The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.
CVSS v3 base score: 7.5
Published: 2023-02-23T22:15Z
Repository: https://github.com/airtower-luna/mod_gnutls
Language: C
Files changed: src/gnutls_io.c
Commit date: 2023-02-23 11:55:53 +0100
Commit message: Fix loop in blocking read on transport timeout

The loop was cased by an incorrect errno if the transport read in
mgs_transport_read() fails with an APR TIMEUP status, as happens when
mod_reqtimeout limits are exceeded. Setting the transport errno to
EGAIN meant that a blocking read would be retried endlessly, setting
ETIMEDOUT instead makes the read fail as it should.

The bug was introduced in commit 92cb0cca41af946055307e373b6ab6d76d34f356,
first release to include it was 0.9.0.

Many thanks to Félix Arreola Rodríguez for the detailed analysis [1]
on the Debian bug tracker!

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=942737#25

Diff: diff --git a/src/gnutls_io.c b/src/gnutls_io.c
index 0b8e840..ce7711d 100644
--- a/src/gnutls_io.c
+++ b/src/gnutls_io.c
@@ -1,10 +1,10 @@
 /*
  *  Copyright 2004-2005 Paul Querna
  *  Copyright 2008 Nikos Mavrogiannopoulos
  *  Copyright 2011 Dash Shendy
- *  Copyright 2015-2020 Fiona Klute
+ *  Copyright 2015-2022 Fiona Klute
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  *  You may obtain a copy of the License at
  *
@@ -275,10 +275,15 @@ static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
                         "GnuTLS: Fatal Alert From Client: "
                         "(%d) '%s'", rc,
                         gnutls_alert_get_name(rc));
                 ctxt->input_rc = APR_EGENERAL;
                 break;
+            } else if (rc == GNUTLS_E_PULL_ERROR
+                       && APR_STATUS_IS_TIMEUP(ctxt->input_rc)) {
+                ap_log_cerror(
+                    APLOG_MARK, APLOG_TRACE2, ctxt->input_rc, ctxt->c,
+                    "%s: transport read timed out", __func__);
             } else {
                 /* Some Other Error. Report it. Die. */
                 if (gnutls_error_is_fatal(rc)) {
                     ap_log_cerror(
                         APLOG_MARK, APLOG_DEBUG,
@@ -1001,11 +1006,11 @@ ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
         if (ctxt->input_block == APR_BLOCK_READ
             && APR_STATUS_IS_TIMEUP(rc)
             && APR_BRIGADE_EMPTY(ctxt->input_bb))
         {
             ctxt->input_rc = rc;
-            gnutls_transport_set_errno(ctxt->session, EAGAIN);
+            gnutls_transport_set_errno(ctxt->session, ETIMEDOUT);
             return -1;
         }
 
         if (rc != APR_SUCCESS)
         {

Vulnerable code:  *  Copyright 2015-2020 Fiona Klute
            gnutls_transport_set_errno(ctxt->session, EAGAIN);
Fixed code:  *  Copyright 2015-2022 Fiona Klute
            } else if (rc == GNUTLS_E_PULL_ERROR
                       && APR_STATUS_IS_TIMEUP(ctxt->input_rc)) {
                ap_log_cerror(
                    APLOG_MARK, APLOG_TRACE2, ctxt->input_rc, ctxt->c,
                    "%s: transport read timed out", __func__);
            gnutls_transport_set_errno(ctxt->session, ETIMEDOUT);