Skip to content

Commit

Permalink
Check result of Integer.parseUnsignedInt() to be non-negative.
Browse files Browse the repository at this point in the history
Signed-off-by: Santiago Pericasgeertsen <[email protected]>
  • Loading branch information
spericas committed Jan 9, 2024
1 parent afa735d commit 7e9caea
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 4 deletions.
46 changes: 46 additions & 0 deletions common/common/src/main/java/io/helidon/common/IntegerParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common;

import java.util.function.Supplier;

/**
* Utility class to properly report integer parsing errors.
*/
public class IntegerParser {

private IntegerParser() {
}

/**
* Throws exception if value returned is negative. Method {@link Integer#parseUnsignedInt(String, int)}
* can return negative numbers.
*
* @param s string to parse
* @param radix the number radix
* @param supplier throwable value
* @return the number
* @param <T> type of throwable
* @throws T throwable if number is negative
*/
public static <T extends Throwable> int parseNonNegative(String s, int radix, Supplier<T> supplier) throws T {
int v = Integer.parseUnsignedInt(s, radix);
if (v < 0) {
throw supplier.get();
}
return v;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.common;

import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

class IntegerParserTest {

@Test
void testNormalParse() {
int v = IntegerParser.parseNonNegative(
"7FFFFFFF",
16,
() -> new RuntimeException("Oops"));
assertThat(v, is(Integer.MAX_VALUE));
}

@Test
void testNegativeParse() {
assertThrows(RuntimeException.class, () ->
IntegerParser.parseNonNegative("FFFFFFFF",
16,
() -> new RuntimeException("Value cannot be negative")));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,7 +23,9 @@
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;

import io.helidon.common.IntegerParser;
import io.helidon.common.buffers.BufferData;
import io.helidon.common.buffers.Bytes;
import io.helidon.common.buffers.DataReader;
Expand Down Expand Up @@ -55,6 +57,8 @@

abstract class Http1CallChainBase implements WebClientService.Chain {
private static final System.Logger LOGGER = System.getLogger(Http1CallChainBase.class.getName());
private static final Supplier<IllegalArgumentException> NEGATIVE_SIZE_SUPPLIER =
() -> new IllegalArgumentException("Chunk size cannot be negative");

private final BufferData writeBuffer = BufferData.growing(128);
private final HttpClientConfig clientConfig;
Expand Down Expand Up @@ -507,7 +511,7 @@ private void ensureBuffer() {
reader.skip(2); // CRLF
int length;
try {
length = Integer.parseUnsignedInt(hex, 16);
length = IntegerParser.parseNonNegative(hex, 16, NEGATIVE_SIZE_SUPPLIER);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Chunk size is not a number:\n"
+ BufferData.create(hex.getBytes(US_ASCII)).debugDataHex());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,7 +23,9 @@
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.function.Supplier;

import io.helidon.common.IntegerParser;
import io.helidon.common.buffers.BufferData;
import io.helidon.common.buffers.DataReader;
import io.helidon.common.buffers.DataWriter;
Expand Down Expand Up @@ -64,6 +66,11 @@
*/
public class Http1Connection implements ServerConnection, InterruptableTask<Void> {
private static final System.Logger LOGGER = System.getLogger(Http1Connection.class.getName());
private static final Supplier<RequestException> NEGATIVE_SIZE_SUPPLIER =
() -> RequestException.builder()
.type(EventType.BAD_REQUEST)
.message("Chunk size cannot be negative")
.build();

static final byte[] CONTINUE_100 = "HTTP/1.1 100 Continue\r\n\r\n".getBytes(StandardCharsets.UTF_8);

Expand Down Expand Up @@ -264,7 +271,7 @@ private BufferData readEntityFromPipeline(HttpPrologue prologue, WritableHeaders
private BufferData readNextChunk(HttpPrologue prologue, WritableHeaders<?> headers) {
// chunk length processing
String hex = reader.readLine();
int chunkLength = Integer.parseUnsignedInt(hex, 16);
int chunkLength = IntegerParser.parseNonNegative(hex, 16, NEGATIVE_SIZE_SUPPLIER);

currentEntitySizeRead += chunkLength;
if (maxPayloadSize != -1 && currentEntitySizeRead > maxPayloadSize) {
Expand Down

0 comments on commit 7e9caea

Please sign in to comment.