You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
NilAway does not handle explicit type conversions (like t := (*T)(nil)) and will miss very obvious nil pointer dereferences. I have an idea for how to support these explicit type conversions, but I am not certain I've identified every code location that would need to change.
Current Behavior
NilAway has three false negatives in the following code:
package main
typeStructstruct {
Fieldint
}
typeStructPtr*Structfuncmain() {
varexplicit*Structexplicit= (*Struct)(nil) // explicit type conversion_=explicit.Field// PANIC, false negativeexplicitShort:= (*Struct)(nil) // explicit type conversion_=explicitShort.Field// PANIC, false negativeexplicitWithoutParens:=StructPtr(nil) // explicit type conversion without parentheses_=explicitWithoutParens.Field// PANIC, false negativevarimplicit*Structimplicit=nil// implicit type conversion; literal `nil` is untyped and is converted to `*Struct`_=implicit.Field// PANIC, true positive
}
The output of NilAway is the following:
$ nilaway ./...
main.go:22:6: error: Potential nil panic detected. Observed nil flow from source to dereference point:
-> typconv/main.go:22:6: literal `nil` accessed field `Field`
Each access of the struct's field panics with a nil pointer dereference, but NilAway only detects the nil dereference of implicit.
Expected Behavior
NilAway should report a potential nil panic for all four field accesses in the above code. In the language of NilAway, explicit type conversions should be potential producers of nilness.
Root Cause
Explicit type conversions and function calls have different semantics, but they are both represented in the AST as *ast.CallExpr. In other words, an *ast.CallExpr does not always represent a function call. When NilAway is considering whether an expression expr of type *ast.CallExpr can produce nil, it incorrectly assumes that the expression is a function call and expr.Fun is a function type (or builtin). Explicit type conversions are handled incorrectly and NilAway falsely claims that the expression cannot produce nil.
Solution
I was able to add a simple handler for type conversions in the *ast.CallExpr case of assertiontree.RootAssertionNode.ParseExprAsProducer (link). For example,
case*ast.CallExpr:
ifr.Pass().TypesInfo.Types[expr.Fun].IsType() {
// This *ast.CallExpr is an explicit type conversionifutil.ExprBarsNilness(r.Pass(), expr.Fun) {
// The conversion is to a type that cannot be nilreturnnil, nil
}
// The conversion can be nil if and only if the converted expression can be nil.//// An explicit type conversion MUST have exactly one argument according to the// Go specification, so we can assume that expr.Args[0] exists for a// type-checked program. We could include a sanity check on the length of// expr.Args if we wanted to be absolutely safe, though.returnr.ParseExprAsProducer(expr.Args[0], false)
}
// Now we can assume that `expr` is a function/method call// Rest of the case continues as normal...
When I add the above code, NilAway catches all four nil panics:
main.go:22:6: error: Potential nil panic detected. Observed nil flow from source to dereference point:
-> typconv/main.go:22:6: literal `nil` accessed field `Field`
main.go:18:6: error: Potential nil panic detected. Observed nil flow from source to dereference point:
-> typconv/main.go:18:6: literal `nil` accessed field `Field`
main.go:15:6: error: Potential nil panic detected. Observed nil flow from source to dereference point:
-> typconv/main.go:15:6: literal `nil` accessed field `Field`
main.go:12:6: error: Potential nil panic detected. Observed nil flow from source to dereference point:
-> typconv/main.go:12:6: literal `nil` accessed field `Field`
I am willing and able to submit a PR for this change, but I imagine there are other locations in NilAway that I have not looked at that should be updated to handle explicit type conversions. I figured that an issue was the best way to provide you all the information you should need to identify other places in the code that need to be fixed.
Please let me know if you have any further questions. Thank you for your hard work on a great analysis tool!
The text was updated successfully, but these errors were encountered:
@thomasfleming-cs , you are right, this is indeed a false negative. Thank you for the detailed description! We appreciate your hard work of debugging NilAway's complex codebase, and welcome you to submit a PR with your solution. :) If needed, we can always add follow-up PRs for covering other cases.
Summary
NilAway does not handle explicit type conversions (like
t := (*T)(nil)
) and will miss very obvious nil pointer dereferences. I have an idea for how to support these explicit type conversions, but I am not certain I've identified every code location that would need to change.Current Behavior
NilAway has three false negatives in the following code:
The output of NilAway is the following:
Each access of the struct's field panics with a nil pointer dereference, but NilAway only detects the nil dereference of
implicit
.Expected Behavior
NilAway should report a potential nil panic for all four field accesses in the above code. In the language of NilAway, explicit type conversions should be potential producers of nilness.
Root Cause
Explicit type conversions and function calls have different semantics, but they are both represented in the AST as
*ast.CallExpr
. In other words, an*ast.CallExpr
does not always represent a function call. When NilAway is considering whether an expressionexpr
of type*ast.CallExpr
can produce nil, it incorrectly assumes that the expression is a function call andexpr.Fun
is a function type (or builtin). Explicit type conversions are handled incorrectly and NilAway falsely claims that the expression cannot produce nil.Solution
I was able to add a simple handler for type conversions in the
*ast.CallExpr
case ofassertiontree.RootAssertionNode.ParseExprAsProducer
(link). For example,(Note: the logic for determining whether the expression is a type conversion is based on Go's SSA compiler: https://cs.opensource.google/go/x/tools/+/refs/tags/v0.16.1:go/ssa/builder.go;l=636)
When I add the above code, NilAway catches all four nil panics:
I am willing and able to submit a PR for this change, but I imagine there are other locations in NilAway that I have not looked at that should be updated to handle explicit type conversions. I figured that an issue was the best way to provide you all the information you should need to identify other places in the code that need to be fixed.
Please let me know if you have any further questions. Thank you for your hard work on a great analysis tool!
The text was updated successfully, but these errors were encountered: