proc: do not check return args when loading return vals of call injection

When the function we are calling is an autogenerated stub (because, for
example, we are calling it through a function pointer) the declaration
line of variables is meaningless and could cause us to discard valid
return arguments.
This commit is contained in:
aarzilli 2021-08-23 10:03:54 +02:00 committed by Alessandro Arzilli
parent de322cd113
commit 4a004e4bc1
5 changed files with 31 additions and 15 deletions

@ -23,6 +23,7 @@ const (
VariablesOnlyVisible VariablesFlags = 1 << iota VariablesOnlyVisible VariablesFlags = 1 << iota
VariablesSkipInlinedSubroutines VariablesSkipInlinedSubroutines
VariablesTrustDeclLine VariablesTrustDeclLine
VariablesNoDeclLineCheck
) )
// Variables returns a list of variables contained inside 'root'. // Variables returns a list of variables contained inside 'root'.
@ -60,7 +61,7 @@ func variablesInternal(v []Variable, root *godwarf.Tree, depth int, pc uint64, l
// are defined. // are defined.
o = 1 o = 1
} }
if declLine, ok := root.Val(dwarf.AttrDeclLine).(int64); !ok || line >= int(declLine)+o { if declLine, ok := root.Val(dwarf.AttrDeclLine).(int64); (flags&VariablesNoDeclLineCheck != 0) || !ok || line >= int(declLine)+o {
return append(v, Variable{root, depth}) return append(v, Variable{root, depth})
} }
return v return v

@ -899,7 +899,7 @@ func (rbpi *returnBreakpointInfo) Collect(t *Target, thread Thread) []*Variable
return returnInfoError("could not read function entry", err, thread.ProcessMemory()) return returnInfoError("could not read function entry", err, thread.ProcessMemory())
} }
vars, err := scope.Locals() vars, err := scope.Locals(0)
if err != nil { if err != nil {
return returnInfoError("could not evaluate return variables", err, thread.ProcessMemory()) return returnInfoError("could not evaluate return variables", err, thread.ProcessMemory())
} }

@ -49,13 +49,21 @@ type EvalScope struct {
// The goroutine executing the expression evaluation shall signal that the // The goroutine executing the expression evaluation shall signal that the
// evaluation is complete by closing the continueRequest channel. // evaluation is complete by closing the continueRequest channel.
callCtx *callContext callCtx *callContext
// If trustArgOrder is true function arguments that don't have an address
// will have one assigned by looking at their position in the argument
// list.
trustArgOrder bool
} }
type localsFlags uint8
const (
// If localsTrustArgOrder is set function arguments that don't have an
// address will have one assigned by looking at their position in the argument
// list.
localsTrustArgOrder localsFlags = 1 << iota
// If localsNoDeclLineCheck the declaration line isn't checked at
// all to determine if the variable is in scope.
localsNoDeclLineCheck
)
// ConvertEvalScope returns a new EvalScope in the context of the // ConvertEvalScope returns a new EvalScope in the context of the
// specified goroutine ID and stack frame. // specified goroutine ID and stack frame.
// If deferCall is > 0 the eval scope will be relative to the specified deferred call. // If deferCall is > 0 the eval scope will be relative to the specified deferred call.
@ -201,12 +209,12 @@ func isAssignment(err error) (int, bool) {
} }
// Locals returns all variables in 'scope'. // Locals returns all variables in 'scope'.
func (scope *EvalScope) Locals() ([]*Variable, error) { func (scope *EvalScope) Locals(flags localsFlags) ([]*Variable, error) {
if scope.Fn == nil { if scope.Fn == nil {
return nil, errors.New("unable to find function context") return nil, errors.New("unable to find function context")
} }
trustArgOrder := scope.trustArgOrder && scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 12) && scope.Fn != nil && (scope.PC == scope.Fn.Entry) trustArgOrder := (flags&localsTrustArgOrder != 0) && scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 12) && scope.Fn != nil && (scope.PC == scope.Fn.Entry)
dwarfTree, err := scope.image().getDwarfTree(scope.Fn.offset) dwarfTree, err := scope.image().getDwarfTree(scope.Fn.offset)
if err != nil { if err != nil {
@ -214,6 +222,9 @@ func (scope *EvalScope) Locals() ([]*Variable, error) {
} }
variablesFlags := reader.VariablesOnlyVisible variablesFlags := reader.VariablesOnlyVisible
if flags&localsNoDeclLineCheck != 0 {
variablesFlags = reader.VariablesNoDeclLineCheck
}
if scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 15) { if scope.BinInfo.Producer() != "" && goversion.ProducerAfterOrEqual(scope.BinInfo.Producer(), 1, 15) {
variablesFlags |= reader.VariablesTrustDeclLine variablesFlags |= reader.VariablesTrustDeclLine
} }
@ -417,7 +428,7 @@ func (scope *EvalScope) SetVariable(name, value string) error {
// LocalVariables returns all local variables from the current function scope. // LocalVariables returns all local variables from the current function scope.
func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) { func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
vars, err := scope.Locals() vars, err := scope.Locals(0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -431,7 +442,7 @@ func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
// FunctionArguments returns the name, value, and type of all current function arguments. // FunctionArguments returns the name, value, and type of all current function arguments.
func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) { func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
vars, err := scope.Locals() vars, err := scope.Locals(0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1134,7 +1145,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return nilVariable, nil return nilVariable, nil
} }
vars, err := scope.Locals() vars, err := scope.Locals(0)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -883,9 +883,13 @@ func funcCallStep(callScope *EvalScope, fncall *functionCallState, thread Thread
// pretend we are still inside the function we called // pretend we are still inside the function we called
fakeFunctionEntryScope(retScope, fncall.fn, int64(regs.SP()), regs.SP()-uint64(bi.Arch.PtrSize())) fakeFunctionEntryScope(retScope, fncall.fn, int64(regs.SP()), regs.SP()-uint64(bi.Arch.PtrSize()))
retScope.trustArgOrder = !bi.regabi var flags localsFlags
flags |= localsNoDeclLineCheck // if the function we are calling is an autogenerated stub then declaration lines have no meaning
if !bi.regabi {
flags |= localsTrustArgOrder
}
fncall.retvars, err = retScope.Locals() fncall.retvars, err = retScope.Locals(flags)
if err != nil { if err != nil {
fncall.err = fmt.Errorf("could not get return values: %v", err) fncall.err = fmt.Errorf("could not get return values: %v", err)
break break

@ -3544,7 +3544,7 @@ func testDeclLineCount(t *testing.T, p *proc.Target, lineno int, tgtvars []strin
assertLineNumber(p, t, lineno, "Program did not continue to correct next location") assertLineNumber(p, t, lineno, "Program did not continue to correct next location")
scope, err := proc.GoroutineScope(p, p.CurrentThread()) scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno)) assertNoError(err, t, fmt.Sprintf("GoroutineScope (:%d)", lineno))
vars, err := scope.Locals() vars, err := scope.Locals(0)
assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno)) assertNoError(err, t, fmt.Sprintf("Locals (:%d)", lineno))
if len(vars) != len(tgtvars) { if len(vars) != len(tgtvars) {
t.Fatalf("wrong number of variables %d (:%d)", len(vars), lineno) t.Fatalf("wrong number of variables %d (:%d)", len(vars), lineno)