Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions cpp/src/gandiva/gdv_function_stubs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "arrow/util/base64.h"
#include "arrow/util/bit_util.h"
#include "arrow/util/double_conversion_internal.h"
#include "arrow/util/int_util_overflow.h"
#include "arrow/util/value_parsing.h"

#include "gandiva/encrypt_utils.h"
Expand Down Expand Up @@ -663,8 +664,14 @@ const char* mask_utf8_utf8_utf8_utf8(int64_t context, const char* data, int32_t
return nullptr;
}

int32_t max_length =
std::max(upper_length, std::max(lower_length, num_length)) * data_len;
int32_t max_length;
if (ARROW_PREDICT_FALSE(arrow::internal::MultiplyWithOverflow(
std::max(upper_length, std::max(lower_length, num_length)), data_len,
&max_length))) {
gdv_fn_context_set_error_msg(context, "Could not allocate memory for output string");
*out_len = 0;
return nullptr;
}
char* out = reinterpret_cast<char*>(gdv_fn_context_arena_malloc(context, max_length));
if (out == nullptr) {
gdv_fn_context_set_error_msg(context, "Could not allocate memory for output string");
Expand Down
40 changes: 39 additions & 1 deletion cpp/src/gandiva/gdv_function_stubs_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,7 @@ TEST(TestGdvFnStubs, TestInitCap) {
EXPECT_EQ(std::string(out_str, out_len), "{Õhp,Pqśv}Ń+");
EXPECT_FALSE(ctx.has_error());

out_str = gdv_fn_initcap_utf8(ctx_ptr, "sɦasasdsɦsd\"sdsdɦ", 19, &out_len);
out_str = gdv_fn_initcap_utf8(ctx_ptr, "sɦasasdsɦsd\"sdsdɦ", 20, &out_len);
EXPECT_EQ(std::string(out_str, out_len), "Sɦasasdsɦsd\"Sdsdɦ");
EXPECT_FALSE(ctx.has_error());

Expand Down Expand Up @@ -1188,6 +1188,44 @@ TEST(TestGdvFnStubs, TestMaskTruncatedUtf8NoOverread) {
EXPECT_TRUE(ctx.has_error());
}

TEST(TestGdvFnStubs, TestMaskOutputLengthOverflow) {
gandiva::ExecutionContext ctx;
auto ctx_ptr = reinterpret_cast<int64_t>(&ctx);
int32_t out_len = -1;
const std::string data(65536, 'A');
const std::string replacement(65536, 'X');
auto result = mask_utf8_utf8_utf8_utf8(
ctx_ptr, data.data(), static_cast<int32_t>(data.size()), replacement.data(),
static_cast<int32_t>(replacement.size()), "x", 1, "n", 1, &out_len);
EXPECT_EQ(result, nullptr);
EXPECT_EQ(out_len, 0);
EXPECT_TRUE(ctx.has_error());
}

TEST(TestGdvFnStubs, TestCaseConversionTruncatedUtf8) {
const std::string inputs[] = {"a\xc2\xa2", "a\xe2\x82\xac", "a\xf0\x9f\x98\x80"};
for (auto convert : {gdv_fn_lower_utf8, gdv_fn_upper_utf8, gdv_fn_initcap_utf8}) {
for (const auto& input : inputs) {
const auto full_len = static_cast<int32_t>(input.size());
for (int32_t len = 2; len < full_len; ++len) {
gandiva::ExecutionContext ctx;
auto ctx_ptr = reinterpret_cast<int64_t>(&ctx);
int32_t out_len = -1;
// Bytes beyond len complete the glyph, but are outside the input slice.
convert(ctx_ptr, input.data(), len, &out_len);
EXPECT_EQ(out_len, 0);
EXPECT_TRUE(ctx.has_error());
}
gandiva::ExecutionContext ctx;
auto ctx_ptr = reinterpret_cast<int64_t>(&ctx);
int32_t out_len = -1;
convert(ctx_ptr, input.data(), full_len, &out_len);
EXPECT_GT(out_len, 0);
EXPECT_FALSE(ctx.has_error());
}
}
}

TEST(TestGdvFnStubs, TestTranslate) {
gandiva::ExecutionContext ctx;
int64_t ctx_ptr = reinterpret_cast<int64_t>(&ctx);
Expand Down
24 changes: 24 additions & 0 deletions cpp/src/gandiva/gdv_string_function_stubs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ const char* gdv_fn_lower_utf8(int64_t context, const char* data, int32_t data_le
}

// Control reaches here when we encounter a multibyte character
// Ensure the multibyte sequence fits within the buffer to avoid
// reading past data_len (truncated trailing multibyte sequence).
if (char_len > data_len - i) {
gdv_fn_set_error_for_invalid_utf8(context, data[i]);
*out_len = 0;
return "";
}

const auto* in_char = (const uint8_t*)(data + i);

// Decode the multibyte character
Expand Down Expand Up @@ -366,6 +374,14 @@ const char* gdv_fn_upper_utf8(int64_t context, const char* data, int32_t data_le
}

// Control reaches here when we encounter a multibyte character
// Ensure the multibyte sequence fits within the buffer to avoid
// reading past data_len (truncated trailing multibyte sequence).
if (char_len > data_len - i) {
gdv_fn_set_error_for_invalid_utf8(context, data[i]);
*out_len = 0;
return "";
}

const auto* in_char = (const uint8_t*)(data + i);

// Decode the multibyte character
Expand Down Expand Up @@ -584,6 +600,14 @@ const char* gdv_fn_initcap_utf8(int64_t context, const char* data, int32_t data_
char_len = gdv_fn_utf8_char_length(data[i]);

// Control reaches here when we encounter a multibyte character
// Ensure the multibyte sequence fits within the buffer to avoid
// reading past data_len (truncated trailing multibyte sequence).
if (char_len > data_len - i) {
gdv_fn_set_error_for_invalid_utf8(context, data[i]);
*out_len = 0;
return "";
}

const auto* in_char = (const uint8_t*)(data + i);

// Decode the multibyte character
Expand Down
Loading