service/dap: limit disassembly range (#3045)

Due to dyanmically loaded libraries there could be aribitrarily large
gaps in the address space, between functions. Limit the memory size we
are willing to disassemble.

Fixes #3040
This commit is contained in:
Alessandro Arzilli 2022-07-15 13:29:44 +02:00 committed by GitHub
parent 6f34add5db
commit 059406e74f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 17 deletions

14
_fixtures/cgodisass.go Normal file

@ -0,0 +1,14 @@
package main
/*
int a(int v) {
return 0xff + v;
}
*/
import "C"
import "fmt"
func main() {
fmt.Println("aaa")
print(C.a(11))
}

@ -3151,6 +3151,10 @@ func alignPCs(bi *proc.BinaryInfo, start, end uint64) (uint64, uint64) {
return end < fn.Entry return end < fn.Entry
}) })
end = bi.Functions[i].Entry end = bi.Functions[i].Entry
const limit = 10 * 1024
if end-start > limit {
end = start + limit
}
} }
return start, end return start, end

@ -55,24 +55,24 @@ func TestMain(m *testing.M) {
// name is for _fixtures/<name>.go // name is for _fixtures/<name>.go
func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) { func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) {
runTestBuildFlags(t, name, test, protest.AllNonOptimized) runTestBuildFlags(t, name, test, protest.AllNonOptimized, false)
} }
// name is for _fixtures/<name>.go // name is for _fixtures/<name>.go
func runTestBuildFlags(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture), buildFlags protest.BuildFlags) { func runTestBuildFlags(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture), buildFlags protest.BuildFlags, defaultDebugInfoDirs bool) {
fixture := protest.BuildFixture(name, buildFlags) fixture := protest.BuildFixture(name, buildFlags)
// Start the DAP server. // Start the DAP server.
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
client := startDAPServerWithClient(t, serverStopped) client := startDAPServerWithClient(t, defaultDebugInfoDirs, serverStopped)
defer client.Close() defer client.Close()
test(client, fixture) test(client, fixture)
<-serverStopped <-serverStopped
} }
func startDAPServerWithClient(t *testing.T, serverStopped chan struct{}) *daptest.Client { func startDAPServerWithClient(t *testing.T, defaultDebugInfoDirs bool, serverStopped chan struct{}) *daptest.Client {
server, _ := startDAPServer(t, serverStopped) server, _ := startDAPServer(t, defaultDebugInfoDirs, serverStopped)
client := daptest.NewClient(server.config.Listener.Addr().String()) client := daptest.NewClient(server.config.Listener.Addr().String())
return client return client
} }
@ -80,16 +80,21 @@ func startDAPServerWithClient(t *testing.T, serverStopped chan struct{}) *daptes
// Starts an empty server and a stripped down config just to establish a client connection. // Starts an empty server and a stripped down config just to establish a client connection.
// To mock a server created by dap.NewServer(config) or serving dap.NewSession(conn, config, debugger) // To mock a server created by dap.NewServer(config) or serving dap.NewSession(conn, config, debugger)
// set those arg fields manually after the server creation. // set those arg fields manually after the server creation.
func startDAPServer(t *testing.T, serverStopped chan struct{}) (server *Server, forceStop chan struct{}) { func startDAPServer(t *testing.T, defaultDebugInfoDirs bool, serverStopped chan struct{}) (server *Server, forceStop chan struct{}) {
// Start the DAP server. // Start the DAP server.
listener, err := net.Listen("tcp", ":0") listener, err := net.Listen("tcp", ":0")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
debugInfoDirs := []string{}
if defaultDebugInfoDirs {
debugInfoDirs = []string{"/usr/lib/debug/.build-id"}
}
disconnectChan := make(chan struct{}) disconnectChan := make(chan struct{})
server = NewServer(&service.Config{ server = NewServer(&service.Config{
Listener: listener, Listener: listener,
DisconnectChan: disconnectChan, DisconnectChan: disconnectChan,
Debugger: debugger.Config{DebugInfoDirectories: debugInfoDirs},
}) })
server.Run() server.Run()
// Give server time to start listening for clients // Give server time to start listening for clients
@ -158,7 +163,7 @@ func TestStopNoClient(t *testing.T) {
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
server, forceStop := startDAPServer(t, serverStopped) server, forceStop := startDAPServer(t, false, serverStopped)
triggerStop(server, forceStop) triggerStop(server, forceStop)
<-serverStopped <-serverStopped
verifyServerStopped(t, server) verifyServerStopped(t, server)
@ -174,7 +179,7 @@ func TestStopNoTarget(t *testing.T) {
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
server, forceStop := startDAPServer(t, serverStopped) server, forceStop := startDAPServer(t, false, serverStopped)
client := daptest.NewClient(server.config.Listener.Addr().String()) client := daptest.NewClient(server.config.Listener.Addr().String())
defer client.Close() defer client.Close()
@ -201,7 +206,7 @@ func TestStopWithTarget(t *testing.T) {
} { } {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
server, forceStop := startDAPServer(t, serverStopped) server, forceStop := startDAPServer(t, false, serverStopped)
client := daptest.NewClient(server.config.Listener.Addr().String()) client := daptest.NewClient(server.config.Listener.Addr().String())
defer client.Close() defer client.Close()
@ -304,7 +309,7 @@ func TestSessionStop(t *testing.T) {
func TestForceStopWhileStopping(t *testing.T) { func TestForceStopWhileStopping(t *testing.T) {
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
server, forceStop := startDAPServer(t, serverStopped) server, forceStop := startDAPServer(t, false, serverStopped)
client := daptest.NewClient(server.config.Listener.Addr().String()) client := daptest.NewClient(server.config.Listener.Addr().String())
client.InitializeRequest() client.InitializeRequest()
@ -1950,7 +1955,7 @@ func TestScopesRequestsOptimized(t *testing.T) {
disconnect: false, disconnect: false,
}}) }})
}, },
protest.EnableOptimization) protest.EnableOptimization, false)
} }
// TestVariablesLoading exposes test cases where variables might be partially or // TestVariablesLoading exposes test cases where variables might be partially or
@ -5407,7 +5412,7 @@ func TestNoDebug_AcceptNoRequestsButDisconnect(t *testing.T) {
func TestLaunchRequestWithRelativeBuildPath(t *testing.T) { func TestLaunchRequestWithRelativeBuildPath(t *testing.T) {
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
client := startDAPServerWithClient(t, serverStopped) client := startDAPServerWithClient(t, false, serverStopped)
defer client.Close() defer client.Close()
fixdir := protest.FindFixturesDir() fixdir := protest.FindFixturesDir()
@ -5506,7 +5511,7 @@ func TestLaunchTestRequest(t *testing.T) {
defer os.Chdir(orgWD) defer os.Chdir(orgWD)
} }
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
client := startDAPServerWithClient(t, serverStopped) client := startDAPServerWithClient(t, false, serverStopped)
defer client.Close() defer client.Close()
runDebugSessionWithBPs(t, client, "launch", runDebugSessionWithBPs(t, client, "launch",
@ -5637,7 +5642,7 @@ func TestLaunchRequestWithEnv(t *testing.T) {
} }
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
client := startDAPServerWithClient(t, serverStopped) client := startDAPServerWithClient(t, false, serverStopped)
defer client.Close() defer client.Close()
runDebugSessionWithBPs(t, client, "launch", func() { // launch runDebugSessionWithBPs(t, client, "launch", func() { // launch
@ -6547,7 +6552,7 @@ func attachDebuggerWithTargetHalted(t *testing.T, fixture string) (*exec.Cmd, *d
// process is halted or debug session never launched.) // process is halted or debug session never launched.)
func runTestWithDebugger(t *testing.T, dbg *debugger.Debugger, test func(c *daptest.Client)) { func runTestWithDebugger(t *testing.T, dbg *debugger.Debugger, test func(c *daptest.Client)) {
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
server, _ := startDAPServer(t, serverStopped) server, _ := startDAPServer(t, false, serverStopped)
client := daptest.NewClient(server.listener.Addr().String()) client := daptest.NewClient(server.listener.Addr().String())
time.Sleep(100 * time.Millisecond) // Give time for connection to be set as dap.Session time.Sleep(100 * time.Millisecond) // Give time for connection to be set as dap.Session
server.sessionMu.Lock() server.sessionMu.Lock()
@ -6676,7 +6681,7 @@ type MultiClientCloseServerMock struct {
func NewMultiClientCloseServerMock(t *testing.T, fixture string) *MultiClientCloseServerMock { func NewMultiClientCloseServerMock(t *testing.T, fixture string) *MultiClientCloseServerMock {
var s MultiClientCloseServerMock var s MultiClientCloseServerMock
s.stopped = make(chan struct{}) s.stopped = make(chan struct{})
s.impl, s.forceStop = startDAPServer(t, s.stopped) s.impl, s.forceStop = startDAPServer(t, false, s.stopped)
_, s.debugger = launchDebuggerWithTargetHalted(t, "http_server") _, s.debugger = launchDebuggerWithTargetHalted(t, "http_server")
return &s return &s
} }
@ -6817,7 +6822,7 @@ func TestBadInitializeRequest(t *testing.T) {
// Only one initialize request is allowed, so use a new server // Only one initialize request is allowed, so use a new server
// for each test. // for each test.
serverStopped := make(chan struct{}) serverStopped := make(chan struct{})
client := startDAPServerWithClient(t, serverStopped) client := startDAPServerWithClient(t, false, serverStopped)
defer client.Close() defer client.Close()
client.InitializeRequestWithArgs(args) client.InitializeRequestWithArgs(args)
@ -7310,3 +7315,37 @@ func TestFindInstructions(t *testing.T) {
}) })
} }
} }
func TestDisassembleCgo(t *testing.T) {
// Test that disassembling a program containing cgo code does not create problems.
// See issue #3040
runTestBuildFlags(t, "cgodisass", func(client *daptest.Client, fixture protest.Fixture) {
runDebugSessionWithBPs(t, client, "launch",
// Launch
func() {
client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
},
// Set breakpoints
fixture.Source, []int{11},
[]onBreakpoint{{
execute: func() {
checkStop(t, client, 1, "main.main", 11)
client.StackTraceRequest(1, 0, 1)
st := client.ExpectStackTraceResponse(t)
if len(st.Body.StackFrames) < 1 {
t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st)
}
// Request the single instruction that the program is stopped at.
pc := st.Body.StackFrames[0].InstructionPointerReference
client.DisassembleRequest(pc, -200, 400)
client.ExpectDisassembleResponse(t)
},
disconnect: true,
}},
)
},
protest.AllNonOptimized, true)
}