Golang中如何排查nil指针解引用的问题
Golang中如何排查nil指针解引用的问题 错误信息:
panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x48 pc=0x63f3a3]
goroutine 79 [running]: main.Checker(0xc00000e260, 0xc000016de0, 0x15, 0xc00001e240) /home/wizard/Desktop/go/src/github.com/wp-install/check.go:97 +0x333 created by main.main /home/wizard/Desktop/go/src/github.com/wp-install/check.go:48 +0x315 exit status 2
我的代码:
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/anikhasibul/queue"
)
const (
maxFileDescriptors = 6000
)
var wg sync.WaitGroup
var q = queue.New(1000)
func main() {
file, err := os.Open("list.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
outfile, err := os.Create("urls.txt")
if err != nil {
log.Fatal(err)
}
defer outfile.Close()
results := make(chan string)
go func() {
for output := range results {
fmt.Fprintln(outfile, output)
}
close(results)
}()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
wg.Add(1)
q.Add()
go Checker(q, scanner.Text(), results)
}
wg.Wait()
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
// CleanUp Recovers
func CleanUp() {
if r := recover(); r != nil {
}
}
// Checker Checks the vulnerbility of Wordpress Installation Vulnerbility
func Checker(q *queue.Q, url string, results chan string) {
defer q.Done()
defer wg.Done()
// defer CleanUp()
if url == "" {
}
sub := []string{"/", "/blog/", "/wp/", "/wordpress/"}
fixed := []string{"wp-admin/install.php", "wp-admin/setup-config.php"}
for _, ls := range sub {
parent := url + ls
for _, ms := range fixed {
mainurl := parent + ms
client := &http.Client{
Timeout: 5 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := http.NewRequest(
http.MethodGet,
mainurl,
nil,
)
if err != nil {
fmt.Print("DEAD")
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:57.0) Gecko/20100101 Firefox/57.0")
res, err := client.Do(req)
if err != nil {
fmt.Println("DEAD")
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println("Couldn't Read")
}
defer res.Body.Close()
ch := string(body)
if strings.Contains(ch, "Select a default language") {
results <- mainurl
fmt.Printf("[FOUND] %s\n", mainurl)
}
fmt.Printf("[NOT FOUND] %s\n", mainurl)
}
}
}
哪里存在 defer 泄漏?需要解决方案 
更多关于Golang中如何排查nil指针解引用的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
问题出现在第97行:
main.Checker(0xc00000e260, 0xc000016de0, 0x15, 0xc00001e240) /home/wizard/Desktop/go/src/github.com/wp-install/check.go:97 +0x333
我没有仔细阅读你的代码来精确确定是哪一行,但你的代码中有很多像这样的错误检查:
req, err := http.NewRequest(
http.MethodGet,
mainurl,
nil,
)
if err != nil {
fmt.Print("DEAD")
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:57.0) Gecko/20100101 Firefox/57.0")
如果在这里出现错误,导致 req 为 nil,你只会打印“DEAD”,然后在 req.Header... 这一行因为解引用 nil 而崩溃。不要这样做。你需要实际地退出或处理这个错误,而不是仅仅打印点东西然后假装什么都没发生继续执行。
更多关于Golang中如何排查nil指针解引用的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
从堆栈跟踪看,panic发生在check.go:97行的Checker函数中。根据代码分析,第97行是:
body, err := ioutil.ReadAll(res.Body)
问题在于res可能是nil指针。当client.Do(req)返回错误时,你只打印了"DEAD"但没有返回,导致后续代码继续执行并解引用nil的res。
修复方案:
res, err := client.Do(req)
if err != nil {
fmt.Println("DEAD")
return // 必须返回,避免解引用nil指针
}
另外,http.NewRequest也可能返回错误,同样需要处理:
req, err := http.NewRequest(
http.MethodGet,
mainurl,
nil,
)
if err != nil {
fmt.Print("DEAD")
return // 需要返回
}
完整修复的Checker函数:
func Checker(q *queue.Q, url string, results chan string) {
defer q.Done()
defer wg.Done()
if url == "" {
return
}
sub := []string{"/", "/blog/", "/wp/", "/wordpress/"}
fixed := []string{"wp-admin/install.php", "wp-admin/setup-config.php"}
for _, ls := range sub {
parent := url + ls
for _, ms := range fixed {
mainurl := parent + ms
client := &http.Client{
Timeout: 5 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := http.NewRequest(
http.MethodGet,
mainurl,
nil,
)
if err != nil {
fmt.Println("DEAD: failed to create request")
continue
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; rv:57.0) Gecko/20100101 Firefox/57.0")
res, err := client.Do(req)
if err != nil {
fmt.Println("DEAD: request failed")
continue
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println("Couldn't Read")
continue
}
ch := string(body)
if strings.Contains(ch, "Select a default language") {
results <- mainurl
fmt.Printf("[FOUND] %s\n", mainurl)
} else {
fmt.Printf("[NOT FOUND] %s\n", mainurl)
}
}
}
}
关于defer泄漏的问题:原代码中如果client.Do失败,res为nil,但仍有defer res.Body.Close()语句,这会导致panic。修复后的代码确保只有在成功获取响应后才执行defer。

