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

2 回复

问题出现在第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")

如果在这里出现错误,导致 reqnil,你只会打印“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。

回到顶部