This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 5d4c79d5 fix(arreflect): validate primitive conversion assignment 
overflow checks (#903)
5d4c79d5 is described below

commit 5d4c79d59c7d94402eee495185005794a8e59b09
Author: Minh Vu <[email protected]>
AuthorDate: Fri Jul 10 18:28:14 2026 +0200

    fix(arreflect): validate primitive conversion assignment overflow checks 
(#903)
    
    ## Summary
    Prevent silent corruption in `arreflect` primitive conversions by
    validating numeric assignment bounds before setting through reflection.
    
    ## Why this fix
    `setPrimitiveValue` used `SetInt/SetUint/SetFloat` directly for Arrow
    numeric types. That allowed values outside the target Go type range to
    wrap/truncate, so bad data could pass through without errors until
    consumed by fast paths relying on value integrity.
    
    ## Changes
    - Added explicit overflow checks with:
      - `OverflowInt`
      - `OverflowUint`
      - `OverflowFloat`
    - Return `ErrTypeMismatch`-wrapped errors on overflow.
    - Preserve existing valid conversion behavior.
    
    ## Files
    - `arrow/array/arreflect/reflect_arrow_to_go.go`
    - `arrow/array/arreflect/reflect_arrow_to_go_test.go`
    
    ## Validation
    - Added tests for overflow cases:
      - `int64` -> `int8`
      - `uint64` -> `uint8`
      - `float64` -> `float32`
    
    ### Bug details
    - Severity: 82/100
    - Ask product?: 0/100
---
 arrow/array/arreflect/reflect_arrow_to_go.go      | 57 +++++++++++++++++++----
 arrow/array/arreflect/reflect_arrow_to_go_test.go | 40 ++++++++++++++++
 2 files changed, 87 insertions(+), 10 deletions(-)

diff --git a/arrow/array/arreflect/reflect_arrow_to_go.go 
b/arrow/array/arreflect/reflect_arrow_to_go.go
index c5b1288b..086dbe6c 100644
--- a/arrow/array/arreflect/reflect_arrow_to_go.go
+++ b/arrow/array/arreflect/reflect_arrow_to_go.go
@@ -161,52 +161,89 @@ func setPrimitiveValue(v reflect.Value, arr arrow.Array, 
i int) error {
                if !isIntKind(v.Kind()) {
                        return fmt.Errorf("cannot set int8 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetInt(int64(arr.(*array.Int8).Value(i)))
+               val := int64(arr.(*array.Int8).Value(i))
+               if v.OverflowInt(val) {
+                       return fmt.Errorf("cannot set int8 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetInt(val)
        case arrow.INT16:
                if !isIntKind(v.Kind()) {
                        return fmt.Errorf("cannot set int16 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetInt(int64(arr.(*array.Int16).Value(i)))
+               val := int64(arr.(*array.Int16).Value(i))
+               if v.OverflowInt(val) {
+                       return fmt.Errorf("cannot set int16 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetInt(val)
        case arrow.INT32:
                if !isIntKind(v.Kind()) {
                        return fmt.Errorf("cannot set int32 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetInt(int64(arr.(*array.Int32).Value(i)))
+               val := int64(arr.(*array.Int32).Value(i))
+               if v.OverflowInt(val) {
+                       return fmt.Errorf("cannot set int32 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetInt(val)
        case arrow.INT64:
                if !isIntKind(v.Kind()) {
                        return fmt.Errorf("cannot set int64 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetInt(arr.(*array.Int64).Value(i))
+               val := arr.(*array.Int64).Value(i)
+               if v.OverflowInt(val) {
+                       return fmt.Errorf("cannot set int64 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetInt(val)
        case arrow.UINT8:
                if !isUintKind(v.Kind()) {
                        return fmt.Errorf("cannot set uint8 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetUint(uint64(arr.(*array.Uint8).Value(i)))
+               val := uint64(arr.(*array.Uint8).Value(i))
+               if v.OverflowUint(val) {
+                       return fmt.Errorf("cannot set uint8 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetUint(val)
        case arrow.UINT16:
                if !isUintKind(v.Kind()) {
                        return fmt.Errorf("cannot set uint16 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetUint(uint64(arr.(*array.Uint16).Value(i)))
+               val := uint64(arr.(*array.Uint16).Value(i))
+               if v.OverflowUint(val) {
+                       return fmt.Errorf("cannot set uint16 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetUint(val)
        case arrow.UINT32:
                if !isUintKind(v.Kind()) {
                        return fmt.Errorf("cannot set uint32 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetUint(uint64(arr.(*array.Uint32).Value(i)))
+               val := uint64(arr.(*array.Uint32).Value(i))
+               if v.OverflowUint(val) {
+                       return fmt.Errorf("cannot set uint32 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetUint(val)
        case arrow.UINT64:
                if !isUintKind(v.Kind()) {
                        return fmt.Errorf("cannot set uint64 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetUint(arr.(*array.Uint64).Value(i))
+               val := arr.(*array.Uint64).Value(i)
+               if v.OverflowUint(val) {
+                       return fmt.Errorf("cannot set uint64 value %d into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetUint(val)
        case arrow.FLOAT32:
                if !isFloatKind(v.Kind()) {
                        return fmt.Errorf("cannot set float32 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetFloat(float64(arr.(*array.Float32).Value(i)))
+               val := float64(arr.(*array.Float32).Value(i))
+               v.SetFloat(val)
        case arrow.FLOAT64:
                if !isFloatKind(v.Kind()) {
                        return fmt.Errorf("cannot set float64 into %s: %w", 
v.Type(), ErrTypeMismatch)
                }
-               v.SetFloat(arr.(*array.Float64).Value(i))
+               val := arr.(*array.Float64).Value(i)
+               if v.OverflowFloat(val) {
+                       return fmt.Errorf("cannot set float64 value %f into %s: 
%w", val, v.Type(), ErrTypeMismatch)
+               }
+               v.SetFloat(val)
        default:
                return fmt.Errorf("unsupported primitive type %v: %w", 
arr.DataType(), ErrUnsupportedType)
        }
diff --git a/arrow/array/arreflect/reflect_arrow_to_go_test.go 
b/arrow/array/arreflect/reflect_arrow_to_go_test.go
index 1a17b33a..eceb5abf 100644
--- a/arrow/array/arreflect/reflect_arrow_to_go_test.go
+++ b/arrow/array/arreflect/reflect_arrow_to_go_test.go
@@ -17,6 +17,7 @@
 package arreflect
 
 import (
+       "math"
        "reflect"
        "testing"
        "time"
@@ -217,6 +218,19 @@ func TestSetPrimitiveValue(t *testing.T) {
                assert.ErrorIs(t, 
setPrimitiveValue(reflect.ValueOf(&bad).Elem(), arr, 0), ErrTypeMismatch)
        })
 
+       t.Run("int64 overflow into int8 returns error", func(t *testing.T) {
+               b := array.NewInt64Builder(mem)
+               defer b.Release()
+               b.Append(int64(300))
+               arr := b.NewArray().(*array.Int64)
+               defer arr.Release()
+
+               got := int8(7)
+               err := setPrimitiveValue(reflect.ValueOf(&got).Elem(), arr, 0)
+               assert.ErrorIs(t, err, ErrTypeMismatch)
+               assert.Equal(t, int8(7), got)
+       })
+
        t.Run("uint16", func(t *testing.T) {
                b := array.NewUint16Builder(mem)
                defer b.Release()
@@ -232,6 +246,19 @@ func TestSetPrimitiveValue(t *testing.T) {
                assert.ErrorIs(t, 
setPrimitiveValue(reflect.ValueOf(&bad).Elem(), arr, 0), ErrTypeMismatch)
        })
 
+       t.Run("uint64 overflow into uint8 returns error", func(t *testing.T) {
+               b := array.NewUint64Builder(mem)
+               defer b.Release()
+               b.Append(300)
+               arr := b.NewArray().(*array.Uint64)
+               defer arr.Release()
+
+               got := uint8(7)
+               err := setPrimitiveValue(reflect.ValueOf(&got).Elem(), arr, 0)
+               assert.ErrorIs(t, err, ErrTypeMismatch)
+               assert.Equal(t, uint8(7), got)
+       })
+
        t.Run("uint32", func(t *testing.T) {
                b := array.NewUint32Builder(mem)
                defer b.Release()
@@ -288,6 +315,19 @@ func TestSetPrimitiveValue(t *testing.T) {
                assert.ErrorIs(t, 
setPrimitiveValue(reflect.ValueOf(&bad).Elem(), arr, 0), ErrTypeMismatch)
        })
 
+       t.Run("float64 overflow into float32 returns error", func(t *testing.T) 
{
+               b := array.NewFloat64Builder(mem)
+               defer b.Release()
+               b.Append(math.MaxFloat64)
+               arr := b.NewArray().(*array.Float64)
+               defer arr.Release()
+
+               got := float32(1)
+               err := setPrimitiveValue(reflect.ValueOf(&got).Elem(), arr, 0)
+               assert.ErrorIs(t, err, ErrTypeMismatch)
+               assert.Equal(t, float32(1), got)
+       })
+
        t.Run("float64 mismatch", func(t *testing.T) {
                b := array.NewFloat64Builder(mem)
                defer b.Release()

Reply via email to