diff --git a/cpp/src/remote_handle.cpp b/cpp/src/remote_handle.cpp index 82d5cfec4f..02ac5aa91f 100644 --- a/cpp/src/remote_handle.cpp +++ b/cpp/src/remote_handle.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -179,6 +179,11 @@ void setup_range_request_impl(CurlHandle& curl, std::size_t file_offset, std::si curl.setopt(CURLOPT_RANGE, byte_range.c_str()); } +bool is_read_out_of_bounds(std::size_t file_offset, std::size_t size, std::size_t nbytes) noexcept +{ + return file_offset > nbytes || size > nbytes - file_offset; +} + /** * @brief Whether the given URL is compatible with the S3 endpoint (including the credential-based * access and presigned URL) which uses HTTP/HTTPS. @@ -762,7 +767,7 @@ std::size_t RemoteHandle::read(void* buf, std::size_t size, std::size_t file_off if (size == 0) { return 0; } - if (file_offset + size > _nbytes) { + if (is_read_out_of_bounds(file_offset, size, _nbytes)) { std::stringstream ss; ss << "cannot read " << file_offset << "+" << size << " bytes into a " << _nbytes << " bytes file (" << _endpoint->str() << ")"; @@ -858,7 +863,7 @@ std::future RemoteHandle::pread(void* buf, "device-memory buffers.", std::invalid_argument); KVIKIO_EXPECT(task_size > 0, "`task_size` must be positive", std::invalid_argument); - if (file_offset + size > _nbytes) { + if (is_read_out_of_bounds(file_offset, size, _nbytes)) { std::stringstream ss; ss << "cannot read " << file_offset << "+" << size << " bytes into a " << _nbytes << " bytes file (" << _endpoint->str() << ")"; diff --git a/cpp/tests/test_remote_handle.cpp b/cpp/tests/test_remote_handle.cpp index 7aebacc683..a264ed4e6b 100644 --- a/cpp/tests/test_remote_handle.cpp +++ b/cpp/tests/test_remote_handle.cpp @@ -1,9 +1,10 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ #include +#include #include #include #include @@ -173,6 +174,21 @@ TEST_F(RemoteHandleTest, read_zero_size_returns_without_range_request) EXPECT_EQ(endpoint_ptr->range_request_calls, 0); } +TEST_F(RemoteHandleTest, read_overflowing_range_throws_without_range_request) +{ + auto endpoint = std::make_unique(); + auto* endpoint_ptr = endpoint.get(); + kvikio::RemoteHandle remote_handle(std::move(endpoint), endpoint_ptr->file_size); + + std::vector output(1); + auto constexpr file_offset = std::numeric_limits::max() - 1; + auto constexpr size = std::size_t{4}; + EXPECT_THAT([&] { remote_handle.read(output.data(), size, file_offset); }, + ThrowsMessage(HasSubstr("cannot read "))); + EXPECT_EQ(endpoint_ptr->setopt_calls, 0); + EXPECT_EQ(endpoint_ptr->range_request_calls, 0); +} + TEST_F(RemoteHandleTest, test_s3_url) { kvikio::test::EnvVarContext env_var_ctx{{"AWS_DEFAULT_REGION", "my_aws_default_region"},