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
This commit is contained in:
Alessandro Arzilli
2021-05-17 19:31:05 +02:00
committed by GitHub
parent db291698e0
commit 32946b2d7c
3 changed files with 42 additions and 12 deletions

View File

@ -3,7 +3,6 @@ package proc
import ( import (
"bytes" "bytes"
"debug/dwarf" "debug/dwarf"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"go/ast" "go/ast"
@ -905,17 +904,14 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
} }
func convertInt(n uint64, signed bool, size int64) uint64 { func convertInt(n uint64, signed bool, size int64) uint64 {
buf := make([]byte, 64/8) bits := uint64(size) * 8
binary.BigEndian.PutUint64(buf, n) mask := uint64((1 << bits) - 1)
m := 64/8 - int(size) r := n & mask
s := byte(0) if signed && (r>>(bits-1)) != 0 {
if signed && (buf[m]&0x80 > 0) { // sign extension
s = 0xff r |= ^uint64(0) &^ mask
} }
for i := 0; i < m; i++ { return r
buf[i] = s
}
return uint64(binary.BigEndian.Uint64(buf))
} }
func (scope *EvalScope) evalBuiltinCall(node *ast.CallExpr) (*Variable, error) { 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 := xv.newVariable("", 0, typ, scope.Mem)
r.Value = rc r.Value = rc
if r.Kind == reflect.String { switch r.Kind {
case reflect.String:
r.Len = xv.Len + yv.Len 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 return r, nil
} }

View File

@ -73,3 +73,26 @@ func TestAlignAddr(t *testing.T) {
c(example.align, example.in+0x10000, example.tgt+0x10000) 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)
}
}
}

View File

@ -836,6 +836,10 @@ func TestEvalExpression(t *testing.T) {
{`iface2map.(data)`, false, "…", "…", "map[string]interface {}", nil}, {`iface2map.(data)`, false, "…", "…", "map[string]interface {}", nil},
{"issue1578", false, "main.Block {cache: *main.Cache nil}", "main.Block {cache: *main.Cache nil}", "main.Block", 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()) ver, _ := goversion.Parse(runtime.Version())