Skip to content

Commit e8dc201

Browse files
committed
GH-50355: [C++][Gandiva] fix out-of-bounds read in utf8_length_ignore_invalid
1 parent 3f10504 commit e8dc201

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

cpp/src/gandiva/precompiled/string_ops.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ gdv_int32 utf8_length_ignore_invalid(const char* data, gdv_int32 data_len) {
206206
// if invalid byte or incomplete glyph, ignore it
207207
char_len = 1;
208208
}
209-
for (int j = 1; j < char_len; ++j) {
209+
for (int j = 1; j < char_len && i + j < data_len; ++j) {
210210
if ((data[i + j] & 0xC0) != 0x80) { // bytes following head-byte of glyph
211211
char_len += 1;
212212
}

cpp/src/gandiva/precompiled/string_ops_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <gtest/gtest.h>
2020

2121
#include <limits>
22+
#include <vector>
2223

2324
#include "gandiva/execution_context.h"
2425
#include "gandiva/precompiled/types.h"
@@ -1608,6 +1609,29 @@ TEST(TestStringOps, TestRpadString) {
16081609
EXPECT_EQ(std::string(out_str + 5000, 2), "α");
16091610
}
16101611

1612+
TEST(TestStringOps, TestPadMalformedUtf8NoOverread) {
1613+
gandiva::ExecutionContext ctx;
1614+
uint64_t ctx_ptr = reinterpret_cast<gdv_int64>(&ctx);
1615+
gdv_int32 out_len = 0;
1616+
1617+
// A 4-byte utf8 lead byte followed by non-continuation bytes and no trailing
1618+
// space. utf8_length_ignore_invalid() used to extend the glyph length past
1619+
// the end of the buffer while scanning the continuation bytes. The input is
1620+
// held in an exactly-sized heap buffer so any over-read trips AddressSanitizer.
1621+
std::vector<char> text = {'\xF0', 'a', 'a', 'a'};
1622+
const auto text_len = static_cast<gdv_int32>(text.size());
1623+
1624+
const char* out_str =
1625+
lpad_utf8_int32_utf8(ctx_ptr, text.data(), text_len, 6, " ", 1, &out_len);
1626+
EXPECT_EQ(out_len, 9);
1627+
EXPECT_EQ(std::string(out_str + out_len - text_len, text_len),
1628+
std::string(text.begin(), text.end()));
1629+
1630+
out_str = rpad_utf8_int32_utf8(ctx_ptr, text.data(), text_len, 6, " ", 1, &out_len);
1631+
EXPECT_EQ(out_len, 9);
1632+
EXPECT_EQ(std::string(out_str, text_len), std::string(text.begin(), text.end()));
1633+
}
1634+
16111635
TEST(TestStringOps, TestRtrim) {
16121636
gandiva::ExecutionContext ctx;
16131637
uint64_t ctx_ptr = reinterpret_cast<gdv_int64>(&ctx);

0 commit comments

Comments
 (0)