Restrict memory allocation in ContentCachingRequestWrapper

Prior to this commit, the `ContentCachingRequestWrapper` could allocate
a `FastByteArrayOutputStream` block that was larger than the content
cache limit given as a consturctor argument. This was due to an
optimization applied in gh-31834 for allocating the right content cache
size when the request size is known.

Fixes gh-32987
This commit is contained in:
Brian Clozel 2024-06-10 09:46:01 +02:00
parent 6681394886
commit 0ca393c0dc
2 changed files with 22 additions and 1 deletions

View File

@ -89,7 +89,12 @@ public class ContentCachingRequestWrapper extends HttpServletRequestWrapper {
public ContentCachingRequestWrapper(HttpServletRequest request, int contentCacheLimit) {
super(request);
int contentLength = request.getContentLength();
this.cachedContent = (contentLength > 0) ? new FastByteArrayOutputStream(contentLength) : new FastByteArrayOutputStream();
if (contentLength > 0) {
this.cachedContent = new FastByteArrayOutputStream(Math.min(contentLength, contentCacheLimit));
}
else {
this.cachedContent = new FastByteArrayOutputStream();
}
this.contentCacheLimit = contentCacheLimit;
}

View File

@ -17,12 +17,15 @@
package org.springframework.web.util;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
@ -89,6 +92,19 @@ class ContentCachingRequestWrapperTests {
assertThat(wrapper.getContentAsString()).isEqualTo(new String("Hel".getBytes(CHARSET), CHARSET));
}
@Test
void shouldNotAllocateMoreThanCacheLimit() throws Exception {
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(createGetRequest("Hello World"), CONTENT_CACHE_LIMIT);
Field field = ReflectionUtils.findField(ContentCachingRequestWrapper.class, "cachedContent");
ReflectionUtils.makeAccessible(field);
FastByteArrayOutputStream cachedContent = (FastByteArrayOutputStream) ReflectionUtils.getField(field, wrapper);
field = ReflectionUtils.findField(FastByteArrayOutputStream.class, "initialBlockSize");
ReflectionUtils.makeAccessible(field);
int blockSize = (int) ReflectionUtils.getField(field, cachedContent);
assertThat(blockSize).isEqualTo(CONTENT_CACHE_LIMIT);
}
@Test
void cachedContentWithOverflow() throws Exception {
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(