溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Linux內核怎么判斷地址是否位于用戶空間

發(fā)布時間:2021-07-14 11:52:09 來源:億速云 閱讀:246 作者:chen 欄目:系統(tǒng)運維

這篇文章主要介紹“Linux內核怎么判斷地址是否位于用戶空間”,在日常操作中,相信很多人在Linux內核怎么判斷地址是否位于用戶空間問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Linux內核怎么判斷地址是否位于用戶空間”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

一、 問題描述

access_ok函數(shù)是什么原理?

Linux內核怎么判斷地址是否位于用戶空間

問題

二、問題分析

我們在內核空間和用戶空間進行數(shù)據(jù)拷貝的時候必須判斷用戶空間地址是否合法。主要通過偶函數(shù)access_ok來判斷。

1. Linux用戶空間與內核地址空間

Linux  操作系統(tǒng)和驅動程序運行在內核空間,應用程序運行在用戶空間,兩者不能簡單地使用指針傳遞數(shù)據(jù),因為Linux使用的虛擬內存機制,用戶空間的數(shù)據(jù)可能被換出,當內核空間使用用戶空間指針時,對應的數(shù)據(jù)可能不在內存中。

通常32位Linux內核地址空間劃分0~3G為用戶空間,3~4G為內核空間。注意這里是32位內核地址空間劃分,64位內核地址空間劃分是不同的。

Linux內核怎么判斷地址是否位于用戶空間
  • 進程尋址空間0~4G

  • 進程在用戶態(tài)只能訪問0~3G,只有進入內核態(tài)才能訪問3G~4G

  • 進程通過系統(tǒng)調用進入內核態(tài)

  • 每個進程虛擬空間的3G~4G部分是相同的

  • 進程從用戶態(tài)進入內核態(tài)不會引起CR3的改變但會引起堆棧的改變

2. access_ok詳解

原型:

access_ok ( type,addr,size);

功能:

  • access_ok — 檢查用戶空間指針是否有效  注意,根據(jù)體系結構的不同,這個函數(shù)可能只是檢查指針是否在用戶空間范圍內,在調用這個函數(shù)之后,內存訪問函數(shù)可能仍然返回 -EFAULT

參數(shù)說明:

  • typeType of access: VERIFY_READ or VERIFY_WRITE.  請注意,VERIFY_WRITE是VERIFY_READ的超集——如果寫入一個塊是安全的,那么從它讀取總是安全的。addr要檢查的塊的開始的用戶空間指針size要檢查的塊的大小

返回值:

  • 此函數(shù)檢查用戶空間中的內存塊是否可用。如果可用,則返回真(非0值),否則返回假 (0) 。

2. 源碼分析

#define access_ok(type, addr, size) (__range_ok(addr, size) == 0)
/* We use 33-bit arithmetic here... */ #define __range_ok(addr, size) ({ \  unsigned long flag, roksum; \  __chk_user_ptr(addr); \  __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \   : "=&r" (flag), "=&r" (roksum) \   : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \   : "cc"); \  flag; })
static inline void __chk_user_ptr(const volatile void *p, size_t size) {  assert(p >= __user_addr_min && p + size <= __user_addr_max); }

其中__range_ok詳解如下:參數(shù)對應:

flag   --------  %0 roksum --------  %1 addr   --------  %2 size   --------  %3

匯編指令詳解

adds %1, %2, %3

等價于:

rosum = addr + size

這個操作會影響狀態(tài)位(目的是影響是進位標志C)。

以下的兩個指令都帶有條件CC,也就是當C=0的時候才執(zhí)行;如果上面的加法指令進位了(C=1),則以下的指令都不執(zhí)行,flag就為初始值current_thread_info()->addr_limit(非0),并返回。如果沒有進位(C=0),就執(zhí)行下面的指令:

sbcccs %1, %1, %0

該指令等價于

rosum = rosum - flag - 1

也就是(addr + size) - (current_thread_info()->addr_limit) - 1,操作影響符號位。.

如果(addr + size) >= (current_thread_info()->addr_limit) - 1,則C=1 如果(addr  + size) < (current_thread_info()->addr_limit) - 1,則C=0  當C=0的時候執(zhí)行以下指令,否則跳過(flag非零)。

movcc %0, #0

等價于

flag = 0,給flag賦值0。

綜上所述:__range_ok宏等價于:

如果(addr + size) >= (current_thread_info()->addr_limit) - 1,返回非零值 如果(addr + size) < (current_thread_info()->addr_limit),返回零

而access_ok就是檢驗將要操作的用戶空間的地址范圍是否在當前進程的用戶地址空間限制中。這個宏的功能很簡單,完全可以用C實現(xiàn),不是必須使用匯編。  由于這兩個函數(shù)使用頻繁,就使用匯編來實現(xiàn)部分功能來增加效率。

3. 使用實例

我們在內核拷貝數(shù)據(jù)到用戶空間或者從用戶空間拷貝數(shù)據(jù)到內核空間,都需要判斷用戶空間地址是否在用戶空間。

static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) {  if (access_ok(VERIFY_READ, from, n))   n = __copy_from_user(to, from, n);  else /* security hole - plug it */   memset(to, 0, n);  return n; }  static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) {  if (access_ok(VERIFY_WRITE, to, n))   n = __copy_to_user(to, from, n);  return n; }

到此,關于“Linux內核怎么判斷地址是否位于用戶空間”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI