proc/eval: fix panic slicing or indexing 'nil'
This commit is contained in:
parent
ff3e2344c4
commit
2deb7fba20
72
proc/eval.go
72
proc/eval.go
@ -345,7 +345,7 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
|
||||
}
|
||||
return xev.mapAccess(idxev)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid expression \"%s\" (type %s does not support indexing)", exprToString(node.X), xev.DwarfType.String())
|
||||
return nil, fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString())
|
||||
|
||||
}
|
||||
}
|
||||
@ -404,7 +404,7 @@ func (scope *EvalScope) evalReslice(node *ast.SliceExpr) (*Variable, error) {
|
||||
}
|
||||
return xev, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("can not slice \"%s\" (type %s)", exprToString(node.X), xev.DwarfType.String())
|
||||
return nil, fmt.Errorf("can not slice \"%s\" (type %s)", exprToString(node.X), xev.TypeString())
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,12 +415,12 @@ func (scope *EvalScope) evalPointerDeref(node *ast.StarExpr) (*Variable, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if xev.DwarfType == nil {
|
||||
return nil, fmt.Errorf("expression \"%s\" can not be dereferenced", exprToString(node.X))
|
||||
if xev.Kind != reflect.Ptr {
|
||||
return nil, fmt.Errorf("expression \"%s\" (%s) can not be dereferenced", exprToString(node.X), xev.TypeString())
|
||||
}
|
||||
|
||||
if xev.Kind != reflect.Ptr {
|
||||
return nil, fmt.Errorf("expression \"%s\" (%s) can not be dereferenced", exprToString(node.X), xev.DwarfType.String())
|
||||
if xev == nilVariable {
|
||||
return nil, fmt.Errorf("nil can not be dereferenced")
|
||||
}
|
||||
|
||||
if len(xev.Children) == 1 {
|
||||
@ -441,7 +441,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xev.Addr == 0 {
|
||||
if xev.Addr == 0 || xev.DwarfType == nil {
|
||||
return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
|
||||
}
|
||||
|
||||
@ -519,6 +519,14 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
|
||||
}
|
||||
|
||||
func negotiateType(op token.Token, xv, yv *Variable) (dwarf.Type, error) {
|
||||
if xv == nilVariable {
|
||||
return nil, negotiateTypeNil(op, yv)
|
||||
}
|
||||
|
||||
if yv == nilVariable {
|
||||
return nil, negotiateTypeNil(op, xv)
|
||||
}
|
||||
|
||||
if op == token.SHR || op == token.SHL {
|
||||
if xv.Value == nil || xv.Value.Kind() != constant.Int {
|
||||
return nil, fmt.Errorf("shift of type %s", xv.Kind)
|
||||
@ -562,6 +570,18 @@ func negotiateType(op token.Token, xv, yv *Variable) (dwarf.Type, error) {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func negotiateTypeNil(op token.Token, v *Variable) error {
|
||||
if op != token.EQL && op != token.NEQ {
|
||||
return fmt.Errorf("operator %s can not be applied to \"nil\"", op.String())
|
||||
}
|
||||
switch v.Kind {
|
||||
case reflect.Ptr, reflect.UnsafePointer, reflect.Chan, reflect.Map, reflect.Interface, reflect.Slice, reflect.Func:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("can not compare %s to nil", v.Kind.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
|
||||
switch node.Op {
|
||||
case token.INC, token.DEC, token.ARROW:
|
||||
@ -662,6 +682,24 @@ func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
|
||||
var eql bool
|
||||
var err error
|
||||
|
||||
if xv == nilVariable {
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return yv.isNil(), nil
|
||||
case token.NEQ:
|
||||
return !yv.isNil(), nil
|
||||
}
|
||||
}
|
||||
|
||||
if yv == nilVariable {
|
||||
switch op {
|
||||
case token.EQL:
|
||||
return xv.isNil(), nil
|
||||
case token.NEQ:
|
||||
return !xv.isNil(), nil
|
||||
}
|
||||
}
|
||||
|
||||
switch xv.Kind {
|
||||
case reflect.Ptr:
|
||||
eql = xv.Children[0].Addr == yv.Children[0].Addr
|
||||
@ -679,11 +717,13 @@ func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
|
||||
}
|
||||
eql, err = equalChildren(xv, yv, false)
|
||||
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
|
||||
if xv != nilVariable && yv != nilVariable {
|
||||
return false, fmt.Errorf("can not compare %s variables", xv.Kind.String())
|
||||
case reflect.Interface:
|
||||
if xv.Children[0].RealType.String() != yv.Children[0].RealType.String() {
|
||||
eql = false
|
||||
} else {
|
||||
eql, err = compareOp(token.EQL, &xv.Children[0], &yv.Children[0])
|
||||
}
|
||||
|
||||
eql = xv.base == yv.base
|
||||
default:
|
||||
return false, fmt.Errorf("unimplemented comparison of %s variables", xv.Kind.String())
|
||||
}
|
||||
@ -694,6 +734,18 @@ func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
|
||||
return eql, err
|
||||
}
|
||||
|
||||
func (v *Variable) isNil() bool {
|
||||
switch v.Kind {
|
||||
case reflect.Ptr:
|
||||
return v.Children[0].Addr == 0
|
||||
case reflect.Interface:
|
||||
return false
|
||||
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
|
||||
return v.base == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) {
|
||||
r := true
|
||||
for i := range xv.Children {
|
||||
|
||||
@ -234,6 +234,16 @@ func (v *Variable) clone() *Variable {
|
||||
return &r
|
||||
}
|
||||
|
||||
func (v *Variable) TypeString() string {
|
||||
if v == nilVariable {
|
||||
return "nil"
|
||||
}
|
||||
if v.DwarfType != nil {
|
||||
return v.DwarfType.String()
|
||||
}
|
||||
return v.Kind.String()
|
||||
}
|
||||
|
||||
func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
|
||||
if v.Unreadable != nil {
|
||||
return v.clone(), nil
|
||||
@ -643,7 +653,11 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
|
||||
}
|
||||
return nil, fmt.Errorf("%s has no member %s", v.Name, memberName)
|
||||
default:
|
||||
return nil, fmt.Errorf("%s (type %s) is not a struct", v.Name, structVar.DwarfType)
|
||||
if v.Name == "" {
|
||||
return nil, fmt.Errorf("type %s is not a struct", structVar.TypeString())
|
||||
} else {
|
||||
return nil, fmt.Errorf("%s (type %s) is not a struct", v.Name, structVar.TypeString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -60,9 +60,13 @@ func (v *Variable) writeTo(buf *bytes.Buffer, top, newlines, includeType bool, i
|
||||
case reflect.Chan:
|
||||
if newlines {
|
||||
v.writeStructTo(buf, newlines, includeType, indent)
|
||||
} else {
|
||||
if len(v.Children) == 0 {
|
||||
fmt.Fprintf(buf, "%s nil", v.Type)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%s %s/%s", v.Type, v.Children[0].Value, v.Children[1].Value)
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
v.writeStructTo(buf, newlines, includeType, indent)
|
||||
case reflect.Map:
|
||||
|
||||
@ -387,6 +387,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
|
||||
// channels
|
||||
{"ch1", true, "chan int 0/2", "", "chan int", nil},
|
||||
{"chnil", true, "chan int nil", "", "chan int", nil},
|
||||
{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},
|
||||
|
||||
// maps
|
||||
@ -463,6 +464,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"c1.sa[0] == nil", false, "false", "", "", nil},
|
||||
{"c1.sa[0] != nil", false, "true", "", "", nil},
|
||||
{"nilslice == nil", false, "true", "", "", nil},
|
||||
{"nil == nilslice", false, "true", "", "", nil},
|
||||
{"nilslice != nil", false, "false", "", "", nil},
|
||||
{"nilptr == nil", false, "true", "", "", nil},
|
||||
{"nilptr != nil", false, "false", "", "", nil},
|
||||
@ -474,10 +476,12 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"m1 == nil", false, "false", "", "", nil},
|
||||
{"mnil == m1", false, "", "", "", fmt.Errorf("can not compare map variables")},
|
||||
{"mnil == nil", false, "true", "", "", nil},
|
||||
{"nil == 2", false, "", "", "", fmt.Errorf("can not compare int to nil")},
|
||||
{"2 == nil", false, "", "", "", fmt.Errorf("can not compare int to nil")},
|
||||
|
||||
// errors
|
||||
{"&3", false, "", "", "", fmt.Errorf("can not take address of \"3\"")},
|
||||
{"*3", false, "", "", "", fmt.Errorf("expression \"3\" can not be dereferenced")},
|
||||
{"*3", false, "", "", "", fmt.Errorf("expression \"3\" (int) can not be dereferenced")},
|
||||
{"&(i2 + i3)", false, "", "", "", fmt.Errorf("can not take address of \"(i2 + i3)\"")},
|
||||
{"i2 + p1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"*int\"")},
|
||||
{"i2 + f1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"float64\"")},
|
||||
@ -487,6 +491,12 @@ func TestEvalExpression(t *testing.T) {
|
||||
{"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")},
|
||||
{"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")},
|
||||
{"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("no type entry found")},
|
||||
{"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")},
|
||||
{"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")},
|
||||
{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
|
||||
{"nil[0]", false, "", "", "", fmt.Errorf("expression \"nil\" (nil) does not support indexing")},
|
||||
{"nil[2:10]", false, "", "", "", fmt.Errorf("can not slice \"nil\" (type nil)")},
|
||||
{"nil.member", false, "", "", "", fmt.Errorf("type nil is not a struct")},
|
||||
|
||||
// typecasts
|
||||
{"uint(i2)", false, "2", "", "uint", nil},
|
||||
@ -508,7 +518,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
assertVariable(t, variable, tc)
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error %s, got non (%s)", tc.err.Error(), tc.name)
|
||||
t.Fatalf("Expected error %s, got no error (%s)", tc.err.Error(), tc.name)
|
||||
}
|
||||
if tc.err.Error() != err.Error() {
|
||||
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user