From e9a9d7b6c5bfc93701488b03c03e2e3893598382 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 22 May 2026 16:55:08 +0200 Subject: [PATCH 1/2] fix: cap retry-after values in rate limiter --- src/sentry_ratelimiter.c | 16 ++++++++++++---- tests/unit/test_ratelimiter.c | 30 ++++++++++++++++++++++++++++++ tests/unit/tests.inc | 1 + 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/sentry_ratelimiter.c b/src/sentry_ratelimiter.c index 04aa250cb1..02687ffedf 100644 --- a/src/sentry_ratelimiter.c +++ b/src/sentry_ratelimiter.c @@ -4,11 +4,21 @@ #include "sentry_utils.h" #define MAX_RATE_LIMITS 4 +#define MAX_RETRY_AFTER (24 * 60 * 60) // 24h struct sentry_rate_limiter_s { uint64_t disabled_until[MAX_RATE_LIMITS]; }; +static uint64_t +calculate_disabled_until(uint64_t retry_after) +{ + if (retry_after > MAX_RETRY_AFTER) { + retry_after = MAX_RETRY_AFTER; + } + return sentry__monotonic_time() + retry_after * 1000; // ms +} + sentry_rate_limiter_t * sentry__rate_limiter_new(void) { @@ -34,8 +44,7 @@ sentry__rate_limiter_update_from_header( if (!sentry__slice_consume_uint64(&slice, &retry_after)) { return false; } - retry_after *= 1000; - retry_after += sentry__monotonic_time(); + retry_after = calculate_disabled_until(retry_after); if (!sentry__slice_consume_if(&slice, ':')) { return false; @@ -79,8 +88,7 @@ sentry__rate_limiter_update_from_http_retry_after( sentry_slice_t slice = sentry__slice_from_str(retry_after); uint64_t eta = 60; sentry__slice_consume_uint64(&slice, &eta); - rl->disabled_until[SENTRY_RL_CATEGORY_ANY] - = sentry__monotonic_time() + eta * 1000; + rl->disabled_until[SENTRY_RL_CATEGORY_ANY] = calculate_disabled_until(eta); return true; } diff --git a/tests/unit/test_ratelimiter.c b/tests/unit/test_ratelimiter.c index 55311a8364..f5ebbb5c50 100644 --- a/tests/unit/test_ratelimiter.c +++ b/tests/unit/test_ratelimiter.c @@ -35,3 +35,33 @@ SENTRY_TEST(rate_limit_parsing) sentry__rate_limiter_free(rl); } + +SENTRY_TEST(rate_limit_retry_after) +{ + const uint64_t max_retry_after = 24 * 60 * 60 * 1000; + + sentry_rate_limiter_t *rl = sentry__rate_limiter_new(); + TEST_ASSERT(!!rl); + + uint64_t before = sentry__monotonic_time(); + TEST_CHECK( + sentry__rate_limiter_update_from_header(rl, "999999999999999:error::")); + uint64_t after = sentry__monotonic_time(); + + uint64_t disabled_until + = sentry__rate_limiter_get_disabled_until(rl, SENTRY_RL_CATEGORY_ERROR); + TEST_CHECK(disabled_until >= before + max_retry_after); + TEST_CHECK(disabled_until <= after + max_retry_after); + + before = sentry__monotonic_time(); + TEST_CHECK(sentry__rate_limiter_update_from_http_retry_after( + rl, "999999999999999")); + after = sentry__monotonic_time(); + + disabled_until + = sentry__rate_limiter_get_disabled_until(rl, SENTRY_RL_CATEGORY_ANY); + TEST_CHECK(disabled_until >= before + max_retry_after); + TEST_CHECK(disabled_until <= after + max_retry_after); + + sentry__rate_limiter_free(rl); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index d673a13e9e..961fe424df 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -235,6 +235,7 @@ XX(procmaps_parser) XX(propagation_context_init) XX(query_consent_requirement) XX(rate_limit_parsing) +XX(rate_limit_retry_after) XX(raw_envelope_event_id) XX(read_envelope_from_file) XX(read_write_envelope_to_file_null) From 161f2aef1658840d4e636e9e6d8a174b25211a43 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 22 May 2026 17:06:01 +0200 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f379e72b..faabb60b33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Windows: fix HTTP rate limit response header parsing. ([#1732](https://github.com/getsentry/sentry-native/pull/1732)) - POSIX: prevent condition-variable timeout overflow from busy-spinning flush and shutdown waits. ([#1731](https://github.com/getsentry/sentry-native/pull/1731)) - Native/macOS: fix thread stack descriptor. ([#1726](https://github.com/getsentry/sentry-native/pull/1726)) +- Cap rate-limit retry-after values at 24 hours to prevent a MITM-provided response from disabling event delivery for the process lifetime. ([#1744](https://github.com/getsentry/sentry-native/pull/1744)) ## 0.14.2