提交 0b3c7d80 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

proc/native: fix target program crash caused by call injection (linux) (#1538)

RestoreRegisters on linux would also restore FS_BASE and GS_BASE, if
the target goroutine migrated to a different thread during the call
injection this would result in two threads of the target process
pointing to the same TLS area which would greatly confuse the target
runtime, leading to fatal panics with nonsensical stack traces.

Other backends are unaffected:

- native/windows doesn't store the TLS in the same CONTEXT struct as
  the other register values.
- native/darwin doesn't support function calls (and wouldn't store the
  TLS value in the same struct)
- gdbserial/rr doesn't support function calls (because it's a
  recording)
- gsdbserial/lldb extracts the value of TLS by executing code in the
  target process.
上级 cba328f8
......@@ -88,7 +88,19 @@ func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
var restoreRegistersErr error
t.dbp.execPtraceFunc(func() {
restoreRegistersErr = sys.PtraceSetRegs(t.ID, (*sys.PtraceRegs)(sr.Regs))
oldRegs := (*sys.PtraceRegs)(sr.Regs)
var currentRegs sys.PtraceRegs
restoreRegistersErr = sys.PtraceGetRegs(t.ID, &currentRegs)
if restoreRegistersErr != nil {
return
}
// restoreRegisters is only supposed to restore CPU registers, not FS_BASE and GS_BASE
oldRegs.Fs_base = currentRegs.Fs_base
oldRegs.Gs_base = currentRegs.Gs_base
restoreRegistersErr = sys.PtraceSetRegs(t.ID, oldRegs)
if restoreRegistersErr != nil {
return
}
......
......@@ -4286,3 +4286,52 @@ func TestAncestors(t *testing.T) {
}
})
}
func testCallConcurrentCheckReturns(p proc.Process, t *testing.T, gid1 int) bool {
for _, thread := range p.ThreadList() {
g, _ := proc.GetG(thread)
if g == nil || g.ID != gid1 {
continue
}
retvals := thread.Common().ReturnValues(normalLoadConfig)
if len(retvals) != 0 {
return true
}
}
return false
}
func TestCallConcurrent(t *testing.T) {
protest.MustSupportFunctionCalls(t, testBackend)
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
bp := setFileBreakpoint(p, t, fixture, 24)
assertNoError(proc.Continue(p), t, "Continue()")
//_, err := p.ClearBreakpoint(bp.Addr)
//assertNoError(err, t, "ClearBreakpoint() returned an error")
gid1 := p.SelectedGoroutine().ID
t.Logf("starting injection in %d / %d", p.SelectedGoroutine().ID, p.CurrentThread().ThreadID())
assertNoError(proc.CallFunction(p, "Foo(10, 1)", &normalLoadConfig, false), t, "EvalExpressionWithCalls()")
returned := testCallConcurrentCheckReturns(p, t, gid1)
curthread := p.CurrentThread()
if curbp := curthread.Breakpoint(); curbp.Breakpoint == nil || curbp.ID != bp.ID || returned {
return
}
_, err := p.ClearBreakpoint(bp.Addr)
assertNoError(err, t, "ClearBreakpoint() returned an error")
for {
returned = testCallConcurrentCheckReturns(p, t, gid1)
if returned {
break
}
t.Logf("Continuing... %v", returned)
assertNoError(proc.Continue(p), t, "Continue()")
}
proc.Continue(p)
})
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册