Skip to content

Commit 4858154

Browse files
committed
Guard against integer overflow when realizing an integer compact_seq.
Necessary as the compact_seq's internal representation uses doubles for the start/step, and we don't know whether the sequence's start/end might overflow.
1 parent 54b1649 commit 4858154

4 files changed

Lines changed: 29 additions & 8 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ if(RDS2CPP_FETCH_EXTERN)
2020
add_subdirectory(extern)
2121
else()
2222
find_package(ltla_byteme 2.1.3 CONFIG REQUIRED)
23-
find_package(ltla_sanisizer 0.2.1 CONFIG REQUIRED)
23+
find_package(ltla_sanisizer 0.2.2 CONFIG REQUIRED)
2424
endif()
2525

2626
target_link_libraries(rds2cpp INTERFACE ltla::byteme)

cmake/Config.cmake.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
include(CMakeFindDependencyMacro)
44
find_dependency(ltla_byteme 2.1.3 CONFIG REQUIRED)
5-
find_dependency(ltla_sanisizer 0.2.1 CONFIG REQUIRED)
5+
find_dependency(ltla_sanisizer 0.2.2 CONFIG REQUIRED)
66

77
include("${CMAKE_CURRENT_LIST_DIR}/ltla_rds2cppTargets.cmake")

extern/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ FetchContent_Declare(
1111
FetchContent_Declare(
1212
sanisizer
1313
GIT_REPOSITORY https://github.com/LTLA/sanisizer
14-
GIT_TAG master # ^0.2.1
14+
GIT_TAG master # ^0.2.2
1515
)
1616

1717
FetchContent_MakeAvailable(byteme)

include/rds2cpp/parse_altrep.hpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <sstream>
99
#include <limits>
1010
#include <memory>
11+
#include <array>
12+
#include <type_traits>
1113

1214
#include "RObject.hpp"
1315
#include "utils_parse.hpp"
@@ -32,8 +34,8 @@ PairList parse_pairlist_body(Source_&, const Header&, SharedParseInfo&);
3234

3335
namespace altrep_internal {
3436

35-
template<class Vector, class Source_>
36-
Vector parse_numeric_compact_seq(Source_& src) try {
37+
template<class Vector_, class Source_>
38+
Vector_ parse_numeric_compact_seq(Source_& src) try {
3739
auto header = parse_header(src);
3840
if (header[3] != static_cast<unsigned char>(SEXPType::REAL)) {
3941
throw std::runtime_error("expected compact_seq to store sequence information in doubles");
@@ -45,10 +47,29 @@ Vector parse_numeric_compact_seq(Source_& src) try {
4547
throw std::runtime_error("expected compact_seq's sequence information to be of length 3");
4648
}
4749

48-
auto len = sanisizer::from_float<std::size_t>(ranges[0]);
50+
const auto len = sanisizer::from_float<std::size_t>(ranges[0]);
4951
double start = ranges[1], step = ranges[2];
5052

51-
Vector output(len);
53+
typedef I<decltype(std::declval<Vector_>().data[0])> VectorValue;
54+
if constexpr(std::is_integral<VectorValue>::value) {
55+
static_assert(std::is_signed<VectorValue>::value);
56+
if (len) {
57+
std::array<double, 2> endpts { start, start + (ranges[0] - 1) * step };
58+
constexpr int available_bits = std::numeric_limits<VectorValue>::digits;
59+
for (auto x : endpts) {
60+
if (!std::isfinite(x)) {
61+
throw std::runtime_error("non-finite boundaries for integer compact_seq");
62+
}
63+
const auto target = (x >= 0 ? x : 1 - x); // +1 as negative values get a bit more space in signed integers.
64+
int bits = sanisizer::required_bits_for_float(std::trunc(target));
65+
if (bits > available_bits) {
66+
throw std::runtime_error("overflow for integer compact_seq");
67+
}
68+
}
69+
}
70+
}
71+
72+
Vector_ output(len);
5273
for (I<decltype(len)> i = 0; i < len; ++i, start += step) {
5374
output.data[i] = start;
5475
}
@@ -61,7 +82,7 @@ Vector parse_numeric_compact_seq(Source_& src) try {
6182
return output;
6283
} catch (std::exception& e) {
6384
throw traceback("failed to parse compact numeric ALTREP", e);
64-
return Vector();
85+
return Vector_();
6586
}
6687

6788
template<class Vector, class Source_>

0 commit comments

Comments
 (0)