Linux開發--探討將標準輸入輸出及錯誤重定向到/dev/null

Q: 我使用瞭如下代碼將stdin、stdout、stderr重定向到/dev/null

   freopen( "/dev/null", "w", stdout );
   freopen( "/dev/null", "w", stderr );
   freopen( "/dev/null", "r", stdin ); 


這樣做正確嗎,是否使用"w+"或者"a"更正確一些。在很多代碼中是這樣完成重定向的:

   close( 0 );
   close( 1 );
   close( 2 );

   open( "/dev/null", O_RDWR );
   dup( 0 );
   dup( 0 );


這兩種方式中哪一種更好、更具可移植性。


A: Andrew Gierth <[email protected]>

  第一種方式不是總能達到目的。freopen()並不確保新的文件流描述符一定重用底層原有文件句柄。假如未能重用,向stderr流輸出的標準I/O函數最終輸出到/dev/null,但那些向STDERR_FILENO句柄輸出的標準I/O函數就沒這麼幸運了,可能輸出到一些不可預期的文件中去。換句話說,2號句柄此時不再是標準錯誤輸出了。比如:
write( 2, ... )


這樣的調用存在安全問題。第二種方式可以避免上述問題,然而存在競爭環境問題。

現在看下述代碼:
    int fd = open( "/dev/null", O_RDWR );
    /*
     * handle failure of open() somehow
     */
    dup2( fd, 0 );
    dup2( fd, 1 );
    dup2( fd, 2 );
    if ( fd > 2 )
    {
        close( fd );
    }


與第二種方式相比,這種代碼是線程安全的。

有人認爲對於後臺守護進程做此類重定向操作浪費資源,建議直接關閉0、1、2號句柄拉倒,這是非常不正確的。假設它們確實被關閉了,則一些普通數據文件句柄將等於0、1、2。以2號句柄爲例,某些庫函數失敗後會向2號句柄輸出錯誤信息,這將破壞原有數據。

D: 小四 <[email protected]> 2002-04-25 16:47


2號句柄的此類安全問題在2002年4月23日得到了實際印證,可參看<<x86/FreeBSD 4.5-RELEASE IO Smash及S/Key機制分析>>。

1987年,Henry Spencer在setuid(7)手冊頁中做了如下建議,一切標準I/O句柄都可能因關閉過而不再是真實的標準I/O句柄,在使用printf()一類的函數前,務必確認這些句柄是期待中的標準I/O句柄。1991年,在comp news上有人重貼了這份文檔。

內核補丁應該確保對於SUID、SGID進程而言,0、1、2號句柄不會被打開後指向一個普通文件。這有很多實現方式,比如使它們全部指向/dev/null。這種限制不應該在庫函數一級實現,可能有些SUID、SGID程序直接使用系統調用。

stdin、stdout、stderr中某一個被關閉,都可能潛在存在問題。

1992年W. Richard Stevens在<<Advanced Programming in the UNIX Environment>>中建議Daemon進程應該關閉所有不必要的文件句柄,並將stdin、stdout、stderr指向/dev/null。

自1998年以來,OpenBSD內核中execve()裏有一個檢查,如果句柄0、1、2是關閉的,就打開/dev/null,使之對應0、1、2號句柄。這樣就可以安全地執行setuid程序了。FreeBSD/NetBSD直至最近纔再次暴露出類似問題,而Linux在glibc中做了一些檢查。

但是,OpenBSD這個檢查存在一個問題,當falloc()失敗時,應該轉向錯誤處理,而
不是簡單地跳出循環。art在註釋中指出了這點,卻無人去修正它。

--------------------------------------------------------------------------
sys/kern/kern_exec.c 在一個循環中,內核試圖打開/dev/null,使之對應0-2號句柄
(...)
    if ( ( error = falloc( p, &fp, &indx ) ) != 0 )
    {
        break;
    }
(...)
--------------------------------------------------------------------------

於是本地用戶獲得一個內核文件表項相關的競爭環境,可以獲取root權限。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章