Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-41250

Actual fix for MDEV-30452: "OpenSSL 3.x SSL_R_UNEXPECTED_EOF_WHILE_READING handling" is missing from all active branches

    XMLWordPrintable

Details

    • Bug
    • Status: Needs Feedback (View Workflow)
    • Major
    • Resolution: Unresolved
    • 10.11.19, 11.4.13, 12.3.3
    • None
    • Server, SSL
    • None

    Description

      Description

      The VIO code fix for MDEV-30452 — handling OpenSSL 3.x SSL_R_UNEXPECTED_EOF_WHILE_READING in vio/viossl.c — was committed in January 2023 to branch bb-10.10-release-ssl (commit 11ae4ed4bf1) but never merged into any release branch. The JIRA ticket was closed as "Fixed in 10.10.3", but the 10.10.3 release (tag mariadb-10.10.3, commit e3e72644cfc) only includes the test-file adjustments, not the VIO code changes. Since 10.10 reached EOL in May 2023, the fix exists only on an orphaned branch.

      All currently supported versions — 10.11.x, 11.4.x, 11.8.x, 12.3.x — are missing this fix as of September 2026.


      Impact

      Without this fix, when a client disconnects without sending close_notify (common with Java connection pools, abrupt network failures, or load balancers), OpenSSL 3.x pushes SSL_R_UNEXPECTED_EOF_WHILE_READING onto the per-thread error queue. MariaDB's vio_ssl_close() never clears these errors. In pool-of-threads mode, where worker threads are reused across connections, the stale error queue corrupts SSL_get_error() results for the next connection assigned to the same worker. The next connection then breaks with "Socket error" or "Got an error writing communication packets" even though nothing is wrong with it.

      The bug requires all of: OpenSSL 3.x, SSL enabled, pool-of-threads, network connections (not localhost), and clients that disconnect without close_notify. On OpenSSL 1.1.1, the same situation produces SSL_ERROR_SYSCALL with errno=0, which does not add entries to the error queue — so the leak does not occur.


      The three missing code changes in vio/viossl.c

      All three changes exist in commit 11ae4ed4bf1 on branch bb-10.10-release-ssl. None exist on any active branch.

      1. vio_ssl_close() — error queue not cleared after SSL_shutdown() failure

      This is the critical fix for the thread pool scenario.

      Current code on all active branches (10.11 through 12.3):

          default: /* Shutdown failed */
            DBUG_PRINT("vio_error", ("SSL_shutdown() failed, error: %d",
                                     SSL_get_error(ssl, r)));
            break;
      

      In release builds, DBUG_PRINT expands to do { } while (0) — SSL_get_error() is never called, ERR_clear_error() is never called. The OpenSSL error queue retains whatever SSL_shutdown() left on it.

      Note: MariaDB calls SSL_set_quiet_shutdown(ssl, 1) before SSL_shutdown(), which should prevent any actual shutdown protocol. However, after SSL_read() hits SSL_R_UNEXPECTED_EOF_WHILE_READING, OpenSSL's internal SSLfatal() moves the state machine to an error state where SSL_in_init() returns 1. In that state, SSL_shutdown() ignores the quiet_shutdown flag and returns -1, pushing SSL_R_SHUTDOWN_WHILE_IN_INIT onto the error queue.

      MDEV-30452 fix:

          default: /* Shutdown failed */
      #ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
            {
              unsigned long err= ERR_peek_last_error();
              int ssl_error= SSL_get_error(ssl, r);
              if (ssl_error == SSL_ERROR_SSL &&
                  (ERR_GET_REASON(err) == SSL_R_UNEXPECTED_EOF_WHILE_READING ||
                   ERR_GET_REASON(err) == SSL_R_SHUTDOWN_WHILE_IN_INIT))
              {
                ERR_clear_error();
                break;
              }
              DBUG_PRINT("vio_error", ("SSL_shutdown() failed, error: %d",
                                       ssl_error));
            }
      #else
            DBUG_PRINT("vio_error", ("SSL_shutdown() failed, error: %d",
                                     SSL_get_error(ssl, r)));
      #endif
            break;
      

      2. ssl_set_sys_error() — wrong errno for unexpected EOF

      Current signature on all active branches:

      static void ssl_set_sys_error(int ssl_error)
      

      The SSL_ERROR_SSL case unconditionally sets errno = EPROTO. On OpenSSL 3.x, an unexpected EOF (peer disconnect without close_notify) triggers SSL_ERROR_SSL — but this is not a protocol error, it is the equivalent of what OpenSSL 1.1.1 reported as SSL_ERROR_SYSCALL with errno=0.

      MDEV-30452 adds an unsigned long err parameter and checks the reason code before setting errno:

      static void ssl_set_sys_error(int ssl_error, unsigned long err)
      {
        ...
        case SSL_ERROR_SSL:
      #ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
          if (ERR_GET_REASON(err) == SSL_R_UNEXPECTED_EOF_WHILE_READING)
          {
            errno= 0;
            break;
          }
      #endif
          /* Protocol error. */
          error= EPROTO;
          break;
      

      3. ssl_should_retry() — ERR_peek_error() scoping

      Current code on all active branches:

      static my_bool ssl_should_retry(Vio *vio, int ret, ...)
      {
        ...
      #if defined(ERR_LIB_X509) && defined(X509_R_CERT_ALREADY_IN_HASH_TABLE)
        unsigned long err = ERR_peek_error();    // inside #if block
        ...
      #endif
        ssl_error= SSL_get_error(ssl, ret);
        ...
        default:
          ssl_set_sys_error(ssl_error);          // cannot pass err
      

      The variable err is scoped inside the #if block and cannot be passed to ssl_set_sys_error(). MDEV-30452 moves the ERR_peek_error() call before the #if block so the error reason code is captured before SSL_get_error() can modify the queue:

      static my_bool ssl_should_retry(Vio *vio, int ret, ...)
      {
        ...
        unsigned long err = ERR_peek_error();    // unconditional, before #if
       
      #if defined(ERR_LIB_X509) && defined(X509_R_CERT_ALREADY_IN_HASH_TABLE)
        ...
      #endif
        ssl_error= SSL_get_error(ssl, ret);
        ...
        default:
          ssl_set_sys_error(ssl_error, err);     // err now available
      


      How the fix was lost:

      • 2023-01-23 — MDEV-30452 filed: Galera test galera_var_reject_queries fails on OpenSSL 3.x due to SSL_R_UNEXPECTED_EOF_WHILE_READING.
      • 2023-01-31 — Julius Goryavsky commits the VIO code fix (commit 11ae4ed4bf1) to branch bb-10.10-release-ssl. This branch was a work branch that was never merged back into 10.10.
      • 2023-02-01 — A separate commit (e3e72644cfc) with only test-file adjustments (error code expectations in 8 .test/.result files) is committed to the 10.10 mainline. This commit contains zero changes to vio/viossl.c.
      • 2023-02-06 — MDEV-30452 closed as "Fixed in 10.10.3".
      • 2023-02 — MariaDB 10.10.3 released. Contains the test changes from e3e72644cfc. Does not contain the VIO fix from 11ae4ed4bf1.
      • 2023-05 — MariaDB 10.10 reaches EOL.
      • 2023 onward — The 10.10->10.11 merge carries e3e72644cfc (test changes) forward. git merge-base --is-ancestor e3e72644cfc mariadb-10.11.19 returns true, which makes it appear as if MDEV-30452 was merged — but only the tests were; the code fix was not.
      • Present — Branch bb-10.10-release-ssl (last commit 2023-01-31) remains on GitHub. Zero active branches contain the VIO fix.

      Verification:

      To confirm the fix is missing from any branch, check for SSL_R_UNEXPECTED_EOF_WHILE_READING in vio/viossl.c:

      $ git show mariadb-10.11.19:vio/viossl.c | grep -c SSL_R_UNEXPECTED_EOF_WHILE_READING
      0
      $ git show mariadb-11.4.13:vio/viossl.c | grep -c SSL_R_UNEXPECTED_EOF_WHILE_READING
      0
      $ git show mariadb-12.3.3:vio/viossl.c | grep -c SSL_R_UNEXPECTED_EOF_WHILE_READING
      0
      $ git show origin/bb-10.10-release-ssl:vio/viossl.c | grep -c SSL_R_UNEXPECTED_EOF_WHILE_READING
      4
      

      Confirm that the code-fix commit was never merged:

      $ git merge-base --is-ancestor 11ae4ed4bf1 mariadb-10.11.19 && echo yes || echo no
      no
      $ git merge-base --is-ancestor 11ae4ed4bf1 mariadb-12.3.3 && echo yes || echo no
      no
      

      Confirm that the test-only commit WAS merged (this is why the JIRA ticket looks "fixed"):

      $ git merge-base --is-ancestor e3e72644cfc mariadb-10.11.19 && echo yes || echo no
      yes
      


      Proposed action:

      Forward-port the vio/viossl.c changes from commit 11ae4ed4bf1 (branch bb-10.10-release-ssl) into all active release branches: 10.11, 11.4, 11.8, 12.3.

      The surrounding code in vio/viossl.c has not changed between 10.10 and 12.3 in the relevant functions. The viosslfactories.c changes in the same commit are cleanup unrelated to the SSL error handling and can be evaluated separately.


      References:


      This bug analysis and report was assisted by AI.
      The original customer report against RHEL 9: https://redhat.atlassian.net/browse/RHEL-155151

      Attachments

        Activity

          People

            shipjain Shipra Jain
            mschorm Michal Schorm
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.