linux系統核心空間保護

來源:互聯網
上載者:User
        /* Arrange for userspace references to be interpreted as kernel
         * pointers.  That way we can pass a kernel pointer to a routine
         * that expects a __user pointer and it will work okay. */
        set_fs(get_ds());

#define KERNEL_DS       0x00000000
#define USER_DS         TASK_SIZE

#define get_ds()        (KERNEL_DS)
#define get_fs()        (current_thread_info()->addr_limit)

static inline void set_fs (mm_segment_t fs)
{
        current_thread_info()->addr_limit = fs;
        modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
}

zz

http://www.enet.com.cn/article/2004/0729/A20040729328800.shtml

看 了LINUX代碼,感覺其對核心記憶體的保護做得不是很好,還有感覺大家有些地方理解不對(主要是LINUX的代碼看起來的樣子和實際的樣子不太一樣),所 以談談我對LINUX系統核心空間的保護和使用者空間與系統空間資料傳遞的代碼看法.注意我說的都是I386體繫結構,別的體繫結構可以看相應的代碼,不敢 保證結果是否是如我所說.
LINUX建立進程的時候建立了兩套段描述符,在檔案Segment.h有說明.

#ifndef _ASM_SEGMENT_H
#define _ASM_SEGMENT_H

#define __KERNEL_CS 0x10
#define __KERNEL_DS 0x18

#define __USER_CS 0x23
#define __USER_DS 0x2B

#endif

一個用於核心代碼,一個用於使用者代碼.運行核心代碼的時候用核心的段描述符號就可以直接存取使用者空間,但運行使用者代碼的時候使用者段描述符不能訪問核心空間,這是用的保護模式一些機制,具體代碼不再介紹.不懂的就得看看介紹保護模式的一些書籍了.
在 使用者代碼調用系統函數的時候,程式進入了系統核心代碼,描述符也已經切換到了核心的描述符,這時可以直接存取使用者空間或者核心空間,兩者的參數資料傳遞也 很簡單,可以直接拷貝等。但看了LINUX代碼的都知道,系統函數代碼裡面的使用者空間與核心空間參數傳遞是沒有這麼直接拷貝的,那是為什麼呢?大家想一 想,使用者調用的一些指標參數等,可以指向核心空間,如果不加以檢測直接拷貝,那麼使用者空間代碼就可以通過系統調用讀寫核心空間了,這顯然是不準許的。所以 核心代碼裡面就採用了統一的一些函數:
copy_from_user/copy_to_user和__generic_copy_from_user/__gerneric_copy_to_user等
, 而在這些函數裡面實現使用者調用傳遞的指標合法性檢測,使用者參數提供的指標等不能指向系統空間,這樣編寫核心代碼的時候只要調用這些函數就能實現了對核心空 間的保護,編寫也比較方便。這就提醒大家自己編寫核心代碼的時候,千萬不要圖方便直接使用者空間與核心空間的參數拷貝,其實那些COPY函數並不是說使用者空 間與核心空間要怎麼切換才能拷貝,這點我看很多人都沒有真正的理解。
我們再仔細看看那些COPY函數是怎麼實現的核心空間保護呢。原來是在每 個進程的進程資料結構裡面儲存了一個使用者空間範圍, current->addr_limit,因為核心空間在使用者空間上面,所以只要簡單檢測使用者傳遞參數訪問的空間是不是小於等於這個範圍就是了。下 面是相關的幾個檔案的相關內容:

檔案 uaccess.h:
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })

#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF)
#define USER_DS MAKE_MM_SEG(PAGE_OFFSET)

#define get_ds() (KERNEL_DS)
// 取得核心空間範圍 YRG
#define get_fs() (current->addr_limit)
// 取得使用者空間範圍 YRG
#define set_fs(x) (current->addr_limit = (x))
// 設定使用者空間範圍 YRG

檔案 processor.h :
typedef struct {
unsigned long seg;
} mm_segment_t;

檔案 page.h :
include

#define __PAGE_OFFSET (PAGE_OFFSET_RAW)

#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET)

檔案page_offset.h:
#include
#ifdef CONFIG_1GB
#define PAGE_OFFSET_RAW 0xC0000000
#elif defined(CONFIG_2GB)
#define PAGE_OFFSET_RAW 0x80000000
#elif defined(CONFIG_3GB)
#define PAGE_OFFSET_RAW 0x40000000
#endif
// 這個顯然可以配置使用者空間與系統空間大小 YRG

大家看那get_ds()、get_fs()、set_fs()等函數可能不是你剛看到時想的那麼一回事吧?他們看來好象是訪問或者設定段,其實只是訪問或者設定一個進程變數罷了。

你 看那些COPY函數使得傳遞的一些參數只能是指向使用者空間,那麼核心代碼對系統一些函數的調用怎麼辦呢,因為那時的參數都在核心空間裡面呀。你仔細看看上 面不是有個set_fs(x)調用嗎,那就是設定這個使用者空間限制的調用,只要臨時設定使用者空間限制為核心空間的範圍,調用完了過後恢複就是了。你再看下 面代碼就對那幾個set_fs()的作用清楚了吧。

->filename is in our kernel space

unsigned long old_fs_value = get_fs();

set_fs(get_ds()); /* after this we can access the user space data */
open(filename, O_CREAT O_RDWR o_EXCL, 0640);
set_fs(old_fs_value); /* restore fs... */

好了,原理講完了,代碼大家也看明白了,我們再看看這種編程好不好呢?其實這種編程顯然不好。 缺點如下:

1、還沒有形成統一的保護方式,對核心空間的保護不好。核心代碼編程不注意的話就可能使得使用者程式突破核心空間的保護。這點編寫核心代碼的同志一定得注意使用者空間與系統空間的拷貝一定得用那些COPY函數,不要自己簡單的直接拷貝。
2、核心代碼對系統函數調用時設定使用者空間限制不好。這點如果設定了哪個進程的使用者空間限制還沒有設定回去的時候如果進程使用者代碼得到運行(應該不太可能吧)那麼就突破了核心空間限制。還有如果核心代碼不注意忘了恢複使用者空間限制那麼也使得核心空間保護失效。

其 實有好的辦法,實現也很簡單。就是用一個核心全域變數儲存使用者空間範圍,這個啟動的時候根據配置計算好,核心調用的時候可以很方便的引用這個變數對使用者代 碼的調用參數實現檢測,用另一個核心全域變數來區分程式是在核心態還是使用者態運行,如果在核心態再調用系統函數就可以不用檢測系統調用的參數是在核心空間 還是使用者空間。這樣就可以避免修改那個全域變數的麻煩。

核心學習FAQ大集錦   zz

http://www.discuz.net/viewthread.php?tid=26370

4.3 在核心中怎麼開啟並操作一個檔案?
a. 直接用open()、read()等系統調用,見問題4.2。
b. 用filp_open()函數開啟檔案,得到struct file *的指標fp。
使用指標fp進行相應操作,如讀檔案可以用fp->f_ops->read。
最後用filp_close()函數關閉檔案。
filp_open()、filp_close()函數在fs/open.c定義,在include/linux/fs.h中
聲明。
c. 自己寫封裝函數,可參照檔案fs/exec.c中的open_exec()和kernel_read()函數。
在http://www.linuxforum.net/forum/ ... t=&Board=linuxK
&Number=363455&page=&view=&sb=&o=&vc=1上有些代碼可以參照。

4.4 在核心中讀寫檔案時為什麼會出現EFAULT錯誤?
a. 核心檔案系統提供的read()和write()之類的函數,期望是對使用者態程式服務的,
所以它會驗證讀寫緩衝區不超過使用者空間的上限即0xC000 0000。但現在核心中
要讀寫檔案,緩衝區在核心中即地址會超過0xC000 0000。
b. 在讀寫檔案前先得到當前fs:mm_segment_t old_fs=get_fs();
並設定當前fs為核心fs:set_fs(KERNEL_DS);
在讀寫檔案後再恢複原先fs: set_fs(old_fs);
set_fs()、get_fs()等相關宏在檔案include/asm/uaccess.h中定義。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.