proc/eval: fix panic slicing or indexing 'nil'

This commit is contained in:
aarzilli 2015-11-12 12:29:53 +01:00 committed by Derek Parker
parent ff3e2344c4
commit 2deb7fba20
4 changed files with 95 additions and 15 deletions

@ -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())
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())
}
}
}

@ -61,7 +61,11 @@ func (v *Variable) writeTo(buf *bytes.Buffer, top, newlines, includeType bool, i
if newlines {
v.writeStructTo(buf, newlines, includeType, indent)
} else {
fmt.Fprintf(buf, "%s %s/%s", v.Type, v.Children[0].Value, v.Children[1].Value)
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)

@ -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())