From 32946b2d7c9953fd424b615be9350011e84c116a Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Mon, 17 May 2021 19:31:05 +0200 Subject: [PATCH] proc: correctly truncate the result of binary ops on integers (#2463) Truncates the result of binary operations on integers to the size of the resulting type. Also rewrites convertInt to not require allocations. Fixes #2454 --- pkg/proc/eval.go | 27 +++++++++++++++------------ pkg/proc/proc_unexported_test.go | 23 +++++++++++++++++++++++ service/test/variables_test.go | 4 ++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index 1313a514..fa35095d 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -3,7 +3,6 @@ package proc import ( "bytes" "debug/dwarf" - "encoding/binary" "errors" "fmt" "go/ast" @@ -905,17 +904,14 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) { } func convertInt(n uint64, signed bool, size int64) uint64 { - buf := make([]byte, 64/8) - binary.BigEndian.PutUint64(buf, n) - m := 64/8 - int(size) - s := byte(0) - if signed && (buf[m]&0x80 > 0) { - s = 0xff + bits := uint64(size) * 8 + mask := uint64((1 << bits) - 1) + r := n & mask + if signed && (r>>(bits-1)) != 0 { + // sign extension + r |= ^uint64(0) &^ mask } - for i := 0; i < m; i++ { - buf[i] = s - } - return uint64(binary.BigEndian.Uint64(buf)) + return r } func (scope *EvalScope) evalBuiltinCall(node *ast.CallExpr) (*Variable, error) { @@ -1636,8 +1632,15 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) { r := xv.newVariable("", 0, typ, scope.Mem) r.Value = rc - if r.Kind == reflect.String { + switch r.Kind { + case reflect.String: r.Len = xv.Len + yv.Len + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, _ := constant.Int64Val(r.Value) + r.Value = constant.MakeInt64(int64(convertInt(uint64(n), true, typ.Size()))) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + n, _ := constant.Uint64Val(r.Value) + r.Value = constant.MakeUint64(convertInt(n, false, typ.Size())) } return r, nil } diff --git a/pkg/proc/proc_unexported_test.go b/pkg/proc/proc_unexported_test.go index 62327ea8..ce269475 100644 --- a/pkg/proc/proc_unexported_test.go +++ b/pkg/proc/proc_unexported_test.go @@ -73,3 +73,26 @@ func TestAlignAddr(t *testing.T) { c(example.align, example.in+0x10000, example.tgt+0x10000) } } + +func TestConvertInt(t *testing.T) { + var testCases = []struct { + in uint64 + signed bool + size int64 + tgt uint64 + }{ + {1, false, 1, 1}, + {uint64(0xf0), true, 1, 0xfffffffffffffff0}, + {uint64(0xf0), false, 1, 0xf0}, + {uint64(0x70), true, 1, 0x70}, + {uint64(0x90f0), true, 2, 0xffffffffffff90f0}, + {uint64(0x90f0), false, 2, 0x90f0}, + } + for _, tc := range testCases { + out := convertInt(tc.in, tc.signed, tc.size) + t.Logf("in=%#016x signed=%v size=%d -> %#016x\n", tc.in, tc.signed, tc.size, out) + if out != tc.tgt { + t.Errorf("expected=%#016x got=%#016x\n", tc.tgt, out) + } + } +} diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 4bab82a9..a40e9710 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -836,6 +836,10 @@ func TestEvalExpression(t *testing.T) { {`iface2map.(data)`, false, "…", "…", "map[string]interface {}", nil}, {"issue1578", false, "main.Block {cache: *main.Cache nil}", "main.Block {cache: *main.Cache nil}", "main.Block", nil}, + {"ni8 << 2", false, "-20", "-20", "int8", nil}, + {"ni8 << 8", false, "0", "0", "int8", nil}, + {"ni8 >> 1", false, "-3", "-3", "int8", nil}, + {"bytearray[0] * bytearray[0]", false, "144", "144", "uint8", nil}, } ver, _ := goversion.Parse(runtime.Version())