Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

go function - panic: runtime error: invalid memory address or nil pointer dereference

Tags:

go

Thanks in advance for everyone's help. I've marked lines 41, 42, and 58 where the problem occurs. I have not been able to track what is throwing the error. It appears all the variables are properly assigned. I use the command:

go run file.go 'command' 'ip address'
ex. - go run file.go uptime 8.8.8.8

The error is:

panic: runtime error: invalid memory address or nil pointer dereference  
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x54fb36]

goroutine 20 [running]:
golang.org/x/crypto/ssh.(*Client).NewSession(0x0, 0x3, 0xc42009e310, 0x10)
    /home/user/go/src/golang.org/x/crypto/ssh/client.go:130 +0x26  
main.executeCmd(0x7ffce5f83a51, 0x6, 0x7ffce5f83a58, 0xd, 0x5d3077, 0x2, 0xc42009a820, 0x0, 0x0)  
    /home/user/ssh.go:58 +0x16f  
main.main.func1(0xc4200d2000, 0x7ffce5f83a51, 0x6, 0xc42009a820,   0x7ffce5f83a58, 0xd, 0x5d3077, 0x2)  
    /home/user/ssh.go:42 +0x7a  
created by main.main  
    /home/user/ssh.go:41 +0x41b

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "time"

    "golang.org/x/crypto/ssh"
)

func main() {
    cmd := os.Args[1]
    hosts := os.Args[2:]

    results := make(chan string, 10)
    timeout := time.After(10 * time.Second)

    port := "22"

    var hostKey ssh.PublicKey
    key, err := ioutil.ReadFile("/home/user/file.pem")
    if err != nil {
        log.Fatalf("unable to read private key: %v", err)
    }
    signer, err := ssh.ParsePrivateKey(key)
    if err != nil {
        log.Fatalf("unable to parse private key: %v", err)
    }

    config := &ssh.ClientConfig{
        User: "ec2-user",
        Auth: []ssh.AuthMethod{
            ssh.PublicKeys(signer),
        },
        HostKeyCallback: ssh.FixedHostKey(hostKey),
    }
    for _, hostname := range hosts {
        go func(hostname string, port string) {  // LINE 41
            results <- executeCmd(cmd, hostname, port, config) // LINE 42
        }(hostname, port)
    }

    for i := 0; i < len(hosts); i++ {
        select {
        case res := <-results:
            fmt.Print(res)
        case <-timeout:
            fmt.Println("Timed out!")
            return
        }
    }
}
func executeCmd(command, hostname string, port string, config *ssh.ClientConfig) (string, error) {
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", hostname, port), config)
if err != nil {
    return "", err
}
defer conn.Close()
session, err := conn.NewSession() //LINE 58
if err != nil {
    return "", err
}
defer session.Close()

var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
if err := session.Run(command); err != nil {
    return "", err
}

return fmt.Sprintf("%s -> %s", hostname, stdoutBuf.String()), nil

}

like image 887
SO03112 Avatar asked Feb 18 '26 15:02

SO03112


1 Answers

As @JoshuaKolden pointed out, you need to check the error coming out of ssh.Dial and then not continue if the value is not nil. Here's a version of your executeCmd function with error checking:

func executeCmd(command, hostname string, port string, config *ssh.ClientConfig) (string, error) {
    conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", hostname, port), config)
    if err != nil {
        return "", err
    }
    defer conn.Close()
    session, err := conn.NewSession() //LINE 58
    if err != nil {
        return "", err
    }
    defer session.Close()

    var stdoutBuf bytes.Buffer
    session.Stdout = &stdoutBuf
    if err := session.Run(command); err != nil {
        return "", err
    }

    return fmt.Sprintf("%s -> %s", hostname, stdoutBuf.String()), nil
}

You will of course need to adapt the goroutine that calls executeCmd for error handling as well. Maybe print the error message to os.Stderr to find out what the problem is.

The reason why you get a panic is because you have an error coming out of ssh.Dial, leaving the value of the variable conn as nil. If you look at client.go line 130 in the ssh package you can see that it does a method call on the pointer receiver c to OpenChannel. Normally this isn't a problem for struct methods that take a pointer receiver, but in this case OpenChannel is a method of the Conn interface that is embedded in the Client struct. Go panics when an interface method is invoked with a nil receiver.

Edit: A potential way to use this new version of the function in your goroutine:

go func(hostname string, port string) {  // LINE 41
    result, err := executeCmd(cmd, hostname, port, config) // LINE 42
    if err != nil {
        fmt.Fprintf(os.Stderr, "error running cmd on host %s port %s: %s", hostname, port, err)
        return
    }
    results <- result
}(hostname, port)

Unfortunately, you cannot send to a channel and assign to another variable in the same statement. Also, you need to check for the error and do something if it's not nil, in this case I chose printing it to standard error.

like image 69
kbolino Avatar answered Feb 21 '26 13:02

kbolino