1. 程式人生 > >訪問進程環境變量environ時的一個坑

訪問進程環境變量environ時的一個坑

sde strcmp clas 執行 程序 表示 pri 而且 ptr

在unistd.h中定義了變量char **environ;來表示當前所有環境變量,一般來說訪問特定環境變量可以用getenv,但是想遍歷所有環境變量就得使用environ。

即在程序內全局聲明extern char **environ;當然設定main函數第3個參數也可以,不過不推薦,因為ISO C的main函數沒有第三個參數。

environ維護了一個char*數組,每個元素都是一個指針指向函數棧幀頂部的環境變量,數組結尾是NULL。

於是正確的遍歷姿勢是下面這樣

    for (int i = 0; environ[i] != NULL; i++)
        puts(environ[i]);

然後我試了下錯誤的姿勢

	for (char *ptr = environ[0]; ptr; ptr++)
		puts(ptr);

結果是程序dump了,審查了下發現錯誤出在ptr++上,因為ptr類型是char*,執行++後指針只向前移動1 byte。

於是就變成這樣

	for (char *ptr = environ[0]; ptr; ptr += (strlen(ptr) + 1)
		puts(environ[i]);

代碼已經比較醜陋了,而且還多出了不必要的計算,即strlen函數,但是程序依然dump了。

我的調試方式是這樣的

    for (char *ptr = environ[0]; ptr; ptr += (strlen(ptr) + 1))
    {
        static int i = 0;
        if (strcmp(ptr, environ[i]) != 0)
        {
            printf("error: %d\n", i);
            break;
        }
        puts(ptr);
        i++;
    }

錯誤如下

Program received signal SIGSEGV, Segmentation fault.
__strcmp_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S:204
204    ../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S: No such file or directory.

對啊,environ數組最後一個元素是NULL,但是strcmp必須接收非NULL指針作為參數(因為strcmp的參數s1、s2必須可以用*s1、*s2來訪問,NULL是地址0,是用戶無法訪問的地址,用戶訪問無法訪問的地址時就會產生SIGSEGV信號)。

於是我定位到了strcmp這句

(gdb) b 15 if environ[i]==0
(gdb) p ptr
$1 = 0x7fffffffefe3 "/home/xyz/TLPI/a.out"
(gdb) p environ[i]
$2 = 0x0

原因也清楚了。在C程序的存儲空間高地址是命令行參數和環境變量依次排列,如下圖

技術分享

n1是環境變量的數量,n2是命令行參數的數量。因此在ptr指向最後一個環境變量時,ptr+=(strlen[ptr]+1)後指向的是argv[0]。

字符指針數組environ保存了n1+1個元素,多出一個元素是NULL。而ptr+=(strlen[ptr]+1)則是直接訪問程序的存儲空間,並沒有一個終止符。

當ptr到達內存中不可訪問的區域(即argv[n2-1]的下面,函數棧幀的地址),就會引發SIGSEGV信號。

訪問進程環境變量environ時的一個坑