Skip to content

GH-50440: [C++][Gandiva] fix out-of-bounds read in set_error_for_date#50441

Open
Arawoof06 wants to merge 3 commits into
apache:mainfrom
Arawoof06:date-error-format-overread
Open

GH-50440: [C++][Gandiva] fix out-of-bounds read in set_error_for_date#50441
Arawoof06 wants to merge 3 commits into
apache:mainfrom
Arawoof06:date-error-format-overread

Conversation

@Arawoof06

@Arawoof06 Arawoof06 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

set_error_for_date formats the invalid-date/timestamp message with snprintf(error, size, "%s%s", msg, input), but input is a Gandiva string passed as pointer plus length and is not NUL-terminated, so the %s conversion scans past the length bytes looking for a terminator and over-reads the values buffer (past its end for the last value). It is reached from castDATE_utf8/castTIMESTAMP_utf8 on any invalid CAST(str AS DATE/TIMESTAMP) input. Switching to the bounded %.*s form with the explicit length keeps the read inside the string, matching every other error formatter in the precompiled sources.

What changes are included in this PR?

Use %.*s with length in set_error_for_date, which covers all of its call sites in both castDATE_utf8 and castTIMESTAMP_utf8.

Are these changes tested?

Yes. Added TestTime.TestCastDateInvalidUnterminated, which feeds an invalid date held in an exactly-sized heap buffer with no trailing NUL so the over-read trips AddressSanitizer, and checks the error string is bounded to the input length.

Are there any user-facing changes?

No.

This PR contains a "Critical Fix". It fixes a heap out-of-bounds read reachable from user-supplied CAST-to-date/timestamp input.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ GitHub issue #50440 has been automatically assigned in GitHub to PR creator.

@kou

kou commented Jul 13, 2026

Copy link
Copy Markdown
Member

@dmitry-chirkov-dremio @lriggs @akravchukdremio @xxlaykxx Could you review this?

EXPECT_EQ(castDATE_date32(1), 86400000);
}

TEST(TestTime, TestCastDateInvalidUnterminated) {

@dmitry-chirkov-dremio dmitry-chirkov-dremio Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that time and timestamp are going through the same code path. Is this so?
If yes, would there be value in adding two more tests for those data types?

p.s. rest of PR looks good to me just need an evaluation of value of additional tests as helper isn't really data type specific.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, both castTIMESTAMP_utf8 and castTIME_utf8 hit the same set_error_for_date helper, so the fix already covers them. Added a test for each anyway (2000-01-01 24:00:00 for timestamp, 24H00H00 for time) so the over-read path is exercised from all three entry points with an unterminated buffer.

@github-actions github-actions Bot added awaiting committer review Awaiting committer review and removed awaiting review Awaiting review labels Jul 13, 2026
Signed-off-by: abdul rawoof <abdulr@bugqore.com>

@dmitry-chirkov-dremio dmitry-chirkov-dremio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines +70 to +79
// The invalid-value error message is built from the raw input pointer. Hold
// the input in an exactly-sized heap buffer with no trailing NUL so that
// formatting the error must respect `length` instead of scanning for a NUL;
// any over-read past the buffer trips AddressSanitizer.
const char bytes[] = {'1', '9', '7', '2', '2', '2', '2', '2', '2', '2'};
const auto length = static_cast<int32_t>(sizeof(bytes));
std::unique_ptr<char[]> input(new char[length]);
std::memcpy(input.get(), bytes, length);

EXPECT_EQ(castDATE_utf8(context_ptr, input.get(), length), 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simplify this? I think that this will include X in the error message without this fix.

Suggested change
// The invalid-value error message is built from the raw input pointer. Hold
// the input in an exactly-sized heap buffer with no trailing NUL so that
// formatting the error must respect `length` instead of scanning for a NUL;
// any over-read past the buffer trips AddressSanitizer.
const char bytes[] = {'1', '9', '7', '2', '2', '2', '2', '2', '2', '2'};
const auto length = static_cast<int32_t>(sizeof(bytes));
std::unique_ptr<char[]> input(new char[length]);
std::memcpy(input.get(), bytes, length);
EXPECT_EQ(castDATE_utf8(context_ptr, input.get(), length), 0);
const std::string input = "1972222222X";
EXPECT_EQ(castDATE_utf8(context_ptr, input.get(), input.length() - 1), 0);

BTW, can we use different numbers instead of multiple 2 something like 197201234X.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, that's cleaner. Switched all three to a std::string with a trailing sentinel and pass length - 1, so the over-read shows up as the sentinel leaking into the message even without ASAN. Used 197201234X for the date one per your suggestion.

@github-actions github-actions Bot added awaiting changes Awaiting changes and removed awaiting committer review Awaiting committer review labels Jul 15, 2026
Signed-off-by: abdul rawoof <abdulr@bugqore.com>
@github-actions github-actions Bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants