From eff7f7e9e4f19255e9241f7ee40b9c1e7dbc229c Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 13 May 2026 15:09:14 -0400 Subject: [PATCH] ext/openssl: Reorder reneg rate-limit decay to avoid integer divide to zero php_openssl_limit_handshake_reneg() computes the bucket decay as elapsed * (limit / window). Both operands are zend_long, so with the documented defaults limit=2 and window=300 the inner division truncates to 0 and the decay term collapses to 0 for every elapsed value. The leaky bucket stops leaking and the cap fires after exactly limit renegotiations regardless of how widely spaced in time, not "limit per window seconds" as documented. Compute (elapsed * limit) / window so the truncation only applies once, after the multiply that brings the operand into a useful range. Guard against window <= 0 to keep the divide safe under user-supplied values the existing init handler does not validate. --- ext/openssl/xp_ssl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index ffacd8a107b7..9cef92814be0 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -1131,7 +1131,9 @@ static void php_openssl_limit_handshake_reneg(const SSL *ssl) /* {{{ */ elapsed_time = (now.tv_sec - sslsock->reneg->prev_handshake); sslsock->reneg->prev_handshake = now.tv_sec; - sslsock->reneg->tokens -= (elapsed_time * (sslsock->reneg->limit / sslsock->reneg->window)); + if (sslsock->reneg->window > 0) { + sslsock->reneg->tokens -= (elapsed_time * sslsock->reneg->limit) / sslsock->reneg->window; + } if (sslsock->reneg->tokens < 0) { sslsock->reneg->tokens = 0;