CVE-2026-47885

Denial of Service
Affects
Spring Framework
in
Spring
No items found.
Versions
>=6.1.0 <=6.1.28, >=6.2.0 <=6.2.19, >=7.0.0 <=7.0.8
Exclamation circle icon
Patch Available

This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.

Overview

Spring Framework is a widely used application framework for the Java platform that provides the core programming and configuration model for modern Java enterprise applications, including the reactive Spring WebFlux web stack. WebFlux parses multipart/form-data request bodies through pluggable HttpMessageReader implementations in the spring-web module. One of them, PartEventHttpMessageReader, is selected when a controller method declares a @RequestBody Flux<PartEvent> argument and streams each incoming part to the application as a sequence of events. The reader exposes two independent size limits: maxInMemorySize, the amount of memory allowed for buffering form fields, and maxPartSize, the maximum size of any single part.

A denial of service vulnerability (CVE-2026-47885) has been identified in PartEventHttpMessageReader, which allows attackers to exhaust the memory of an affected server by sending a multipart request that contains an oversized form-field part, because the reader does not enforce the configured maxPartSize limit when maxInMemorySize is set to -1. An application is only exposed when it uses @RequestBody Flux<PartEvent> endpoints, has explicitly set maxInMemorySize to -1 to lift the in-memory limit, and relies on maxPartSize to bound the size of individual parts.

Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others. If a service receives a very large number of requests, it may cease to be available to legitimate users. In the same way, a service may stop if a programming vulnerability is exploited, or the way the service handles resources it uses.

The advisory rates this vulnerability Medium severity with the CVSS v3.1 vector AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H, which corresponds to a base score of 5.3. The attack vector is Network because the trigger is an ordinary multipart HTTP request, attack complexity is High because the application must combine the non-default maxInMemorySize of -1 with an explicit maxPartSize on a Flux<PartEvent> endpoint, privileges are Low because such upload endpoints are typically available to authenticated users, and the Availability impact is High because the oversized part is buffered in full until the process runs out of memory.

This issue affects Spring Framework >=6.1.0 <=6.1.28, >=6.2.0 <=6.2.19, and >=7.0.0 <=7.0.8, including the End-of-Life 6.1.x and 6.2.x lines supported by NES for Spring Framework.

Details

Module Info

Vulnerability Info

This Medium-severity vulnerability is found in the spring-web package in multiple versions of Spring Framework. PartEventHttpMessageReader splits a multipart body into parts and, for each part, decides how to deliver it based on the part's headers. A part whose Content-Disposition carries no filename and whose content type is absent or text/plain is treated as a form field: its content is joined into a single in-memory buffer and delivered as one FormPartEvent carrying the field's string value. Every other part, including file uploads, is streamed to the application buffer by buffer as it arrives.

The reader offers two limits. maxInMemorySize defaults to 256 KB and caps how much memory a form field may occupy; the documented value -1 means the entire field content is kept in memory without limit. maxPartSize defaults to -1 (no limit) and, once set, is meant to cap the size of any part regardless of its kind. For the form-field path the two limits are combined into a single byte count that is handed to DataBufferUtils.join:

private Publisher<? extends PartEvent> createEvents(HttpHeaders headers, Flux<MultipartParser.BodyToken> bodyTokens) {
    if (MultipartUtils.isFormField(headers)) {
        Flux<DataBuffer> contents = bodyTokens.map(MultipartParser.BodyToken::buffer);
        int maxSize;
        if (this.maxPartSize == -1) {
            maxSize = this.maxInMemorySize;
        }
        else {
            // maxInMemorySize is an int, so we can safely cast the long result of Math.min
            maxSize = (int) Math.min(this.maxInMemorySize, this.maxPartSize);
        }
        return DataBufferUtils.join(contents, maxSize)
                .map(content -> {
                    String value = content.toString(MultipartUtils.charset(headers));
                    DataBufferUtils.release(content);
                    return DefaultPartEvents.form(headers, value);
                })
                .switchIfEmpty(Mono.fromCallable(() -> DefaultPartEvents.form(headers)));
    }
    else {
        boolean isFilePart = headers.getContentDisposition().getFilename() != null;
        AtomicLong partSize = new AtomicLong();
        return bodyTokens
                .concatMap(body -> {
                    DataBuffer buffer = body.buffer();
                    if (tooLarge(partSize, buffer)) {
                        DataBufferUtils.release(buffer);
                        return Mono.error(new DataBufferLimitException("Part exceeded the limit of " +
                                this.maxPartSize + " bytes"));
                    }
                    else {
                        return (isFilePart ? Mono.just(DefaultPartEvents.file(headers, buffer, body.isLast())) :
                                Mono.just(DefaultPartEvents.create(headers, body.buffer(), body.isLast())));
                    }
                })
                .switchIfEmpty(Mono.fromCallable(() ->
                        isFilePart ? DefaultPartEvents.file(headers) : DefaultPartEvents.create(headers)));
    }
}

The else branch of the maxSize computation assumes that both values are real limits. When maxInMemorySize is -1 and maxPartSize holds a positive value, Math.min(-1, maxPartSize) evaluates to -1, so maxSize is -1 and DataBufferUtils.join(contents, -1) is called. DataBufferUtils.join collects the buffers into a LimitedDataBufferList whose size check returns immediately whenever its limit is negative, which means no limit is applied at all. The configured maxPartSize is silently discarded for every form-field part, and the reader keeps allocating memory for as long as the client keeps sending data for that part.

Only the form-field path is affected. Non-form parts go through the tooLarge check in the else branch, which compares the running part size against maxPartSize on every buffer independently of maxInMemorySize, so file uploads are bounded correctly on all versions.

An attacker who can reach a @RequestBody Flux<PartEvent> endpoint on an application configured this way sends a multipart request with a single form-field part whose body is arbitrarily long. The server buffers the whole part in heap memory before the application sees a single event, so a few concurrent requests, or one request with a sufficiently large part, drive the JVM to an OutOfMemoryError and make the service unavailable. The configuration that enables the attack is reached in practice through ServerCodecConfigurer.defaultCodecs().maxInMemorySize(-1), which is applied to every registered PartEventHttpMessageReader, or by setting maxInMemorySize to -1 directly on the reader while also setting maxPartSize.

This vulnerability was introduced in 2023 with Spring Framework 6.1.0.

Mitigation

Only recent versions of Spring Framework receive community support. The 6.1.x and 6.2.x lines are End-of-Life and will not receive public updates to address this issue, so there is no publicly available fix for those lines other than through a commercial support partner.

Applications that cannot upgrade immediately can remove their exposure by leaving maxInMemorySize at a positive value on PartEventHttpMessageReader, since the maxPartSize limit is honored for form fields whenever maxInMemorySize is not -1; a maxInMemorySize equal to the intended maxPartSize gives the same effective bound for form-field parts.

Users of the affected components should apply one of the following mitigations:

  • Upgrade to a currently supported version of Spring Framework. The open-source fix ships in Spring Framework 7.0.9 on the 7.0.x line.
  • Leverage a commercial support partner like HeroDevs for post-EOL security support, which provides the fix for the 6.1.x and 6.2.x lines in nes-v6.1.30 and nes-v6.2.21.
Vulnerability Details
Severity
Level
CVSS Assessment
Low
>=0 <4
Medium
>=4 <6
High
>=6 <8
Critical
>=8 <10
Medium
ID
CVE-2026-47885
PROJECT Affected
Spring Framework
Versions Affected
>=6.1.0 <=6.1.28, >=6.2.0 <=6.2.19, >=7.0.0 <=7.0.8
NES Versions Affected
Published date
August 27, 2026
≈ Fix date
August 25, 2026
Category
Denial of Service
Vex Document
Download VEXHow do I use it?
Sign up for the latest vulnerability alerts fixed in
NES for Spring
Rss feed icon
Subscribe via RSS
or

By submitting the form I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.