Skip to content

Avoid int overflow in expiration time calculations - #37208

Open
a-kogun wants to merge 1 commit into
spring-projects:mainfrom
a-kogun:fix-int-overflow-expiration
Open

Avoid int overflow in expiration time calculations#37208
a-kogun wants to merge 1 commit into
spring-projects:mainfrom
a-kogun:fix-int-overflow-expiration

Conversation

@a-kogun

@a-kogun a-kogun commented Aug 28, 2026

Copy link
Copy Markdown

Two places still compute an expiration time by multiplying an int
number of seconds by 1000 without widening to long:

  • FlashMap.startExpirationPeriod(int) (spring-webmvc)
  • MockMvcWebConnection.createCookie(...) (spring-test)

Above 2_147_483 seconds (~24.9 days) the multiplication overflows and
yields a negative offset, so the expiration time lands in the past.
With Integer.MAX_VALUE, timeToLive * 1000 evaluates to -1000, i.e.
one second ago.

Impact

  • FlashMap: a timeout set via the public
    AbstractFlashMapManager.setFlashMapTimeout(int) is treated as
    already expired, so flash attributes are silently dropped.
  • MockMvcWebConnection: because storeCookies compares the computed
    expiry against now, a cookie with a large max-age is passed to
    cookieManager.removeCookie(...) instead of addCookie(...) — the
    cookie is discarded rather than stored.

Fix

Widen the literal to 1000L in both places. This is the same change
already applied to this exact pattern in gh-25613
(ExecutorConfigurationSupport.setAwaitTerminationSeconds(int) and
AbstractResourceBasedMessageSource.setCacheSeconds(int)), and matches
how those methods read today.

Tests

Two regression tests are included, both of which fail before the change:

  • FlashMapTests.notExpiredWithLargeTimeToLive() — fails with
    Expecting actual: <now-1000> to be greater than: <now>.
  • MockMvcWebClientBuilderTests.cookieWithLargeMaxAgeIsStored() — fails
    with expected: "foo" but was: "NA", showing the cookie was dropped.

A scan of src/main/java found no other live instances of this pattern:
AbstractSockJsService.ONE_YEAR and DurationFormatterUtils.micros are
already declared long.

FlashMap.startExpirationPeriod(int) and MockMvcWebConnection's cookie
handling both multiplied an int number of seconds by 1000 without
widening to long. Above 2_147_483 seconds (about 24.9 days) the
multiplication overflows to a negative offset, so the computed
expiration time lands in the past.

For FlashMap, a flash map configured through
AbstractFlashMapManager.setFlashMapTimeout(int) with a large timeout is
then treated as expired immediately. For MockMvcWebConnection, a cookie
with a large max-age is removed from the CookieManager instead of being
stored.

This applies the same widening already used for this pattern in
spring-projectsgh-25613.

Signed-off-by: kogun <akogun@gmail.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Aug 28, 2026
@sbrannen sbrannen added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Aug 28, 2026
@sbrannen sbrannen self-assigned this Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: web Issues in web modules (web, webmvc, webflux, websocket) status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants