Skip to content

Streaming implementation to prevent OOMs for very long scans with progress #89

@echox

Description

@echox

I get reproducible crashes after scanning a large number of hosts, for example a /24 network.
I'm using -sSV -p- so the scan will take a while.
After ~ 1 hour the application simply exits with the following errors:

goroutine 91 [IO wait, 317 minutes]:
internal/poll.runtime_pollWait(0x7fa9fa9c8ea0, 0x72, 0xc00003ece8)
        /usr/lib/go-1.11/src/runtime/netpoll.go:173 +0x66
internal/poll.(*pollDesc).wait(0xc001942798, 0x72, 0xffffffffffffff01, 0x542a60, 0x5ed140)
        /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:85 +0x9a
internal/poll.(*pollDesc).waitRead(0xc001942798, 0xc00013c401, 0x200, 0x200)
        /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:90 +0x3d
internal/poll.(*FD).Read(0xc001942780, 0xc00013c400, 0x200, 0x200, 0x0, 0x0, 0x0)
        /usr/lib/go-1.11/src/internal/poll/fd_unix.go:169 +0x179
os.(*File).read(0xc0001720b8, 0xc00013c400, 0x200, 0x200, 0xc00013c400, 0x0, 0x0)
        /usr/lib/go-1.11/src/os/file_unix.go:249 +0x4e
os.(*File).Read(0xc0001720b8, 0xc00013c400, 0x200, 0x200, 0xc00003ee58, 0x49b31c, 0xc00003ee60)
        /usr/lib/go-1.11/src/os/file.go:108 +0x69
bytes.(*Buffer).ReadFrom(0xc0001c63f0, 0x542900, 0xc0001720b8, 0x7fa9fa984020, 0xc0001c63f0, 0x1)
        /usr/lib/go-1.11/src/bytes/buffer.go:206 +0xbd
io.copyBuffer(0x542780, 0xc0001c63f0, 0x542900, 0xc0001720b8, 0x0, 0x0, 0x0, 0xc00d2a6660, 0x0, 0x0)
        /usr/lib/go-1.11/src/io/io.go:388 +0x303
io.Copy(0x542780, 0xc0001c63f0, 0x542900, 0xc0001720b8, 0x404a75, 0xc00d2a67e0, 0xc00003efb0)
        /usr/lib/go-1.11/src/io/io.go:364 +0x5a
os/exec.(*Cmd).writerDescriptor.func1(0xc00d2a67e0, 0xc00003efb0)
        /usr/lib/go-1.11/src/os/exec/exec.go:279 +0x4d
os/exec.(*Cmd).Start.func1(0xc003427a20, 0xc00220bc80)
        /usr/lib/go-1.11/src/os/exec/exec.go:400 +0x27
created by os/exec.(*Cmd).Start
        /usr/lib/go-1.11/src/os/exec/exec.go:399 +0x5af

goroutine 90 [IO wait]:
internal/poll.runtime_pollWait(0x7fa9fa9c8820, 0x72, 0xc0000a1ce8)
        /usr/lib/go-1.11/src/runtime/netpoll.go:173 +0x66
internal/poll.(*pollDesc).wait(0xc0019426d8, 0x72, 0xffffffffffffff01, 0x542a60, 0x5ed140)
        /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:85 +0x9a
internal/poll.(*pollDesc).waitRead(0xc0019426d8, 0xc0fdd84b01, 0xd209, 0xd209)
        /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:90 +0x3d
internal/poll.(*FD).Read(0xc0019426c0, 0xc0fdd84bf7, 0xd209, 0xd209, 0x0, 0x0, 0x0)
        /usr/lib/go-1.11/src/internal/poll/fd_unix.go:169 +0x179
os.(*File).read(0xc0001720a0, 0xc0fdd84bf7, 0xd209, 0xd209, 0x6c, 0x0, 0x0)
        /usr/lib/go-1.11/src/os/file_unix.go:249 +0x4e
os.(*File).Read(0xc0001720a0, 0xc0fdd84bf7, 0xd209, 0xd209, 0x6c, 0x0, 0x0)
        /usr/lib/go-1.11/src/os/file.go:108 +0x69
bytes.(*Buffer).ReadFrom(0xc0001c6380, 0x542900, 0xc0001720a0, 0x7fa9fa984020, 0xc0001c6380, 0xc00d2dc901)
        /usr/lib/go-1.11/src/bytes/buffer.go:206 +0xbd
io.copyBuffer(0x542780, 0xc0001c6380, 0x542900, 0xc0001720a0, 0x0, 0x0, 0x0, 0xc00d2a6720, 0x0, 0x0)
        /usr/lib/go-1.11/src/io/io.go:388 +0x303
io.Copy(0x542780, 0xc0001c6380, 0x542900, 0xc0001720a0, 0x404a75, 0xc00d2a67e0, 0xc0004c3fb0)
        /usr/lib/go-1.11/src/io/io.go:364 +0x5a
os/exec.(*Cmd).writerDescriptor.func1(0xc00d2a67e0, 0xc0004c3fb0)
        /usr/lib/go-1.11/src/os/exec/exec.go:279 +0x4d
os/exec.(*Cmd).Start.func1(0xc003427a20, 0xc00220bc40)
        /usr/lib/go-1.11/src/os/exec/exec.go:400 +0x27
created by os/exec.(*Cmd).Start
        /usr/lib/go-1.11/src/os/exec/exec.go:399 +0x5af

goroutine 89 [chan receive]:
main.scan_host.func1(0xc00015a000, 0xc00220bc00, 0x1)
        main.go:139 +0x3f
created by main.scan_host
        main.go:138 +0x432
exit status 2

main.go:139 does contain the progress function.

The code (the progress if statement is not the most elegant but shouldn't be the cause of the error I guess :)):

        s, err := nmap.NewScanner(
                nmap.WithTargets(host),
                nmap.WithTimingTemplate(nmap.TimingAggressive),
                nmap.WithServiceInfo(),
                nmap.WithSYNScan(),
                nmap.WithPorts("-"),
                nmap.WithVerbosity(3),
                //nmap.WithFastMode(),
        )

        if err != nil {
                log.Fatalf("[worker_%v] unable to create nmap scanner: %v", id, err)
        }

        progress := make(chan float32, 1)
        ts := time.Now()
        go func() {
                for p := range progress {
                        if time.Now().After(ts.Add(60 * time.Second)) {
                                ts = time.Now()
                                log.Printf("[worker_%v] portscan progress: %v %%", id, p)
                        }
                }
        }()

        result, w, e := s.RunWithProgress(progress)

I'm not sure if this is a bug in the library, a frozen nmap process or anything else...
Doing a fast-mode scan with the same settings works fine (which is obviously faster ;-)).

I would appreciate any hints for debugging this further because I guess the pasted errors are not that helpful and I have no experience debugging these kind of errors in the go ecosystem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions