Skip to content

fix(array): validate binary_view and string_view payloads - #994

Merged
zeroshade merged 3 commits into
apache:mainfrom
fallintoplace:fix/validate-binary-string-view
Jul 24, 2026
Merged

fix(array): validate binary_view and string_view payloads#994
zeroshade merged 3 commits into
apache:mainfrom
fallintoplace:fix/validate-binary-string-view

Conversation

@fallintoplace

Copy link
Copy Markdown
Contributor

Summary

  • add BinaryView and StringView validators
  • validate view header ranges, referenced buffers, prefixes, and inline padding during ValidateFull
  • validate UTF-8 for StringView payloads and add regressions for malformed headers

Why

BinaryView and StringView accessors currently trust header metadata directly. Malformed view headers can pass validation today even when they reference missing data or carry invalid payload metadata.

Validation

  • go test ./arrow/array
  • go test ./arrow/compute/...

@fallintoplace
fallintoplace requested a review from zeroshade as a code owner July 23, 2026 21:32

@zeroshade zeroshade left a comment

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.

Thanks for this — the per-view invariants are a faithful, complete port of Arrow C++ ValidateBinaryView (negative size, inline padding-zero, buffer index/offset bounds, prefix match, and UTF-8 for StringView), and the cheap Validate / full ValidateFull split respects the O(1) vs O(n) contract. Tests pass and go vet is clean.

Requesting changes on two items:

  1. Blocking: ValidateFull panics with a nil-pointer dereference on any empty (length-0) BinaryView/StringView, including the standard builder output. validateViewValues unconditionally dereferences buffers[1], which is nil for empty arrays (Validate returns early and never touches it). This regressed in the "tighten view validation helpers" commit. Repro + one-line guard inline.
  2. CI lint: gofmt fails on the inline-padding slice expression. Suggestion inline.

Non-blocking: a couple of tests worth adding (inline on the test file) — an empty-array case would have caught #1, and the out-of-line path is currently under-tested.

Details inline.

Comment thread arrow/array/binary.go

func validateViewValues(arr ViewLike, dataBuffers []*memory.Buffer, validateValue func(int, []byte) error) error {
data := arr.Data().(*Data)
rawViews := data.buffers[1].Bytes()

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.

Blocking: ValidateFull panics on empty (length-0) view arrays.

Validate() (via validateViewLayout) returns early when length == 0 and never dereferences buffers[1], but for an empty view array buffers[1] is nil, so this hoisted data.buffers[1].Bytes() panics.

Reproduced on the standard builder output (and on hand-built data):

array.ValidateFull(array.NewBinaryViewBuilder(mem).NewBinaryViewArray()) // panic: invalid memory address or nil pointer dereference
array.ValidateFull(array.NewStringViewBuilder(mem).NewStringViewArray()) // panic

This regressed in the "tighten view validation helpers" commit — patch 1 only touched buffers[1] inside the loop, so empty arrays were safe. It also undercuts the helper's intent: validation should report malformed data, not crash on a valid empty array.

Suggested change
rawViews := data.buffers[1].Bytes()
if data.length == 0 {
return nil
}
rawViews := data.buffers[1].Bytes()

Comment thread arrow/array/binary.go Outdated
if view.IsInline() {
rawOffset := (data.offset + i) * arrow.ViewHeaderSizeBytes
raw := rawViews[rawOffset : rawOffset+arrow.ViewHeaderSizeBytes]
for _, b := range raw[4+view.Len():arrow.ViewHeaderSizeBytes] {

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.

gofmt fails here (CI lint) — it wants spaces around : when the slice bounds are expressions:

Suggested change
for _, b := range raw[4+view.Len():arrow.ViewHeaderSizeBytes] {
for _, b := range raw[4+view.Len() : arrow.ViewHeaderSizeBytes] {

})
}

func TestBinaryViewValidate(t *testing.T) {

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.

Nice regression set. A few high-value cases are missing — the first would have caught the empty-array panic in validateViewValues:

  • Empty (length-0) array via array.ValidateFull (currently panics).
  • Drive the public array.Validate / array.ValidateFull entrypoints, not just the methods directly, so framework dispatch + layout checks are covered.
  • An array with offset > 0 to lock in the (data.offset+i)*ViewHeaderSizeBytes indexing.
  • Out-of-line happy path plus untested failure modes: prefix mismatch, negative / out-of-range buffer offset, and offset+size > buffer length.

@zeroshade zeroshade left a comment

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.

Thanks for this — the per-view invariants are a faithful, complete port of Arrow C++ ValidateBinaryView (negative size, inline padding-zero, buffer index/offset bounds, prefix match, and UTF-8 for StringView), and the cheap Validate / full ValidateFull split respects the O(1) vs O(n) contract. Tests pass and go vet is clean.

Requesting changes on two items:

  1. Blocking: ValidateFull panics with a nil-pointer dereference on any empty (length-0) BinaryView/StringView, including the standard builder output. validateViewValues unconditionally dereferences buffers[1], which is nil for empty arrays (Validate returns early and never touches it). This regressed in the "tighten view validation helpers" commit. Repro + one-line guard inline.
  2. CI lint: gofmt fails on the inline-padding slice expression. Suggestion inline.

Non-blocking: a couple of tests worth adding (inline on the test file) — an empty-array case would have caught #1, and the out-of-line path is currently under-tested.

Details inline.

@zeroshade
zeroshade dismissed their stale review July 24, 2026 16:27

Duplicate of the review directly above (accidental double-submit); see that one for the actual feedback.

@zeroshade zeroshade left a comment

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.

Issues fixed, just needs to have the merge conflicts resolved

@fallintoplace
fallintoplace force-pushed the fix/validate-binary-string-view branch from 2e52107 to dab1fe1 Compare July 24, 2026 17:23
@zeroshade
zeroshade merged commit 69c2fd5 into apache:main Jul 24, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants