核心中訪問空指標(基於kernel-4.9)
在C語言中,我們定義了NULL來表示空指標,空指標是一個特殊的指標,它其實就是0指標,*p = NULL和*p=0是等價的寫法。空指標是一個未賦值的指標,毫無意義的指標,如果訪問到該地址,那麼程式會出錯。
如果在Linux應用程式中訪問NULL指標:
會收到Segmentation Fault訊號,一般行為是該使用者程序會殺死自己,程式當然也可以捕獲對應的訊號自行處理,這種使用者態的錯誤是不會導致系統crash的。
如果是在核心中訪問到NULL指標分多種情況:
(1)如果是在核心態的程序上下文中訪問,那麼會執行oops動作,殺死當前程序,並列印相關資訊。
(2)如果是在核心態的其他上下文中(比如中斷上下文),那麼系統會執行panic動作。
(3)如果核心中有配置panic_on_oops,那麼上面發生oops的場景也會發生panic。
接下來我們來看一下程式碼中是如何執行的,當我們訪問一個頁表中不存在的地址時,那麼CPU首先會觸發一個異常,缺頁異常由硬體自動觸發。
首先我們需要看中斷向量表,當發生data abort異常時,ARM64會首先去執行entry.S中定義的:
el1_da:
......
mov x2, sp // struct pt_regs
bl do_mem_abort //主處理函式,C程式碼
......
el0_da:
/*
* Data abort handling
*/
mrs x26, far_el1
// enable interrupts before calling the main handler
enable_dbg_and_irq
ct_user_exit
clear_address_tag x0, x26
mov x1, x25
mov x2, sp
bl do_mem_abort //主處理函式,C程式碼
b ret_to_user
下面我們以此跟蹤下去,可以看到會執行到arch/arm64/mm/fault.c:
/*
* Dispatch a data abort to the relevant handler.
*/
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
const struct fault_info *inf = esr_to_fault_info(esr); //根據esr找到對應的fault_info陣列成員
struct siginfo info;
if (!inf->fn(addr, esr, regs))
return;
pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",
inf->name, esr, addr);
info.si_signo = inf->sig;
info.si_errno = 0;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
arm64_notify_die("", regs, &info, esr);
}
static inline const struct fault_info *esr_to_fault_info(unsigned int esr)
{
return fault_info + (esr & 63);
}
下面來看fault info陣列的定義:
static const struct fault_info fault_info[] = {
{ do_bad, SIGBUS, 0, "ttbr address size fault" },
{ do_bad, SIGBUS, 0, "level 1 address size fault" },
{ do_bad, SIGBUS, 0, "level 2 address size fault" },
{ do_bad, SIGBUS, 0, "level 3 address size fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
{ do_bad, SIGBUS, 0, "unknown 8" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" },
{ do_bad, SIGBUS, 0, "unknown 12" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" },
{ do_bad, SIGBUS, 0, "synchronous external abort" },
{ do_bad, SIGBUS, 0, "unknown 17" },
{ do_bad, SIGBUS, 0, "unknown 18" },
{ do_bad, SIGBUS, 0, "unknown 19" },
{ do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous parity error" },
{ do_bad, SIGBUS, 0, "unknown 25" },
{ do_bad, SIGBUS, 0, "unknown 26" },
{ do_bad, SIGBUS, 0, "unknown 27" },
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
{ do_bad, SIGBUS, 0, "unknown 32" },
{ do_alignment_fault, SIGBUS, BUS_ADRALN, "alignment fault" },
{ do_bad, SIGBUS, 0, "unknown 34" },
{ do_bad, SIGBUS, 0, "unknown 35" },
{ do_bad, SIGBUS, 0, "unknown 36" },
{ do_bad, SIGBUS, 0, "unknown 37" },
{ do_bad, SIGBUS, 0, "unknown 38" },
{ do_bad, SIGBUS, 0, "unknown 39" },
{ do_bad, SIGBUS, 0, "unknown 40" },
{ do_bad, SIGBUS, 0, "unknown 41" },
{ do_bad, SIGBUS, 0, "unknown 42" },
{ do_bad, SIGBUS, 0, "unknown 43" },
{ do_bad, SIGBUS, 0, "unknown 44" },
{ do_bad, SIGBUS, 0, "unknown 45" },
{ do_bad, SIGBUS, 0, "unknown 46" },
{ do_bad, SIGBUS, 0, "unknown 47" },
{ do_tlb_conf_fault, SIGBUS, 0, "TLB conflict abort" },
{ do_bad, SIGBUS, 0, "unknown 49" },
{ do_bad, SIGBUS, 0, "unknown 50" },
{ do_bad, SIGBUS, 0, "unknown 51" },
{ do_bad, SIGBUS, 0, "implementation fault (lockdown abort)" },
{ do_bad, SIGBUS, 0, "implementation fault (unsupported exclusive)" },
{ do_bad, SIGBUS, 0, "unknown 54" },
{ do_bad, SIGBUS, 0, "unknown 55" },
{ do_bad, SIGBUS, 0, "unknown 56" },
{ do_bad, SIGBUS, 0, "unknown 57" },
{ do_bad, SIGBUS, 0, "unknown 58" },
{ do_bad, SIGBUS, 0, "unknown 59" },
{ do_bad, SIGBUS, 0, "unknown 60" },
{ do_bad, SIGBUS, 0, "section domain fault" },
{ do_bad, SIGBUS, 0, "page domain fault" },
{ do_bad, SIGBUS, 0, "unknown 63" },
};
上面定義的fault info,主要關鍵的處理函式就如下幾個,分別是:
do_bad/do_alignment_fault/do_tlb_conf_fault/do_translation_fault/do_page_fault。
回到最初的問題,如果我們想要訪問的是指標0地址,那麼硬體會執行到do_page_fault這個函式中來進行處理。我們來繼續跟蹤一下,看看這個函式具體做了什麼吧。
static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
struct task_struct *tsk;
struct mm_struct *mm;
int fault, sig, code;
unsigned long vm_flags = VM_READ | VM_WRITE;
unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
if (notify_page_fault(regs, esr))
return 0;
tsk = current;
mm = tsk->mm;
/*
* If we're in an interrupt or have no user context, we must not take
* the fault.
*/
if (faulthandler_disabled() || !mm) //faulthandler_disabled()函式判斷是否faulthandler被disabled了或者是否處於中斷上下文
//!mm用來判斷是否沒有使用者程序的上下文,如果都處於中斷上下文或者沒有使用者上下文,那麼直接跳轉
goto no_context;
if (user_mode(regs))
mm_flags |= FAULT_FLAG_USER;
if (is_el0_instruction_abort(esr)) {
vm_flags = VM_EXEC;
} else if (((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) ||
((esr & ESR_ELx_CM) && !(mm_flags & FAULT_FLAG_USER))) {
vm_flags = VM_WRITE;
mm_flags |= FAULT_FLAG_WRITE;
}
if (addr < USER_DS && is_permission_fault(esr, regs)) {
/* regs->orig_addr_limit may be 0 if we entered from EL0 */
if (regs->orig_addr_limit == KERNEL_DS)
die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
if (is_el1_instruction_abort(esr))
die("Attempting to execute userspace memory", regs, esr);
if (!search_exception_tables(regs->pc))
die("Accessing user space memory outside uaccess.h routines", regs, esr);
}
/*
* As per x86, we may deadlock here. However, since the kernel only
* validly references user space from well defined areas of the code,
* we can bug out early if this is from code which shouldn't.
*/
if (!down_read_trylock(&mm->mmap_sem)) {
if (!user_mode(regs) && !search_exception_tables(regs->pc))
goto no_context;
retry:
down_read(&mm->mmap_sem);
} else {
/*
* The above down_read_trylock() might have succeeded in which
* case, we'll have missed the might_sleep() from down_read().
*/
might_sleep();
#ifdef CONFIG_DEBUG_VM
if (!user_mode(regs) && !search_exception_tables(regs->pc))
goto no_context;
#endif
}
fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
/*
* If we need to retry but a fatal signal is pending, handle the
* signal first. We do not need to release the mmap_sem because it
* would already be released in __lock_page_or_retry in mm/filemap.c.
*/
if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) {
if (!user_mode(regs))
goto no_context;
return 0;
}
/*
* Major/minor page fault accounting is only done on the initial
* attempt. If we go through a retry, it is extremely likely that the
* page will be found in page cache at that point.
*/
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
if (fault & VM_FAULT_MAJOR) {
tsk->maj_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs,
addr);
} else {
tsk->min_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs,
addr);
}
if (fault & VM_FAULT_RETRY) {
/*
* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk of
* starvation.
*/
mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
mm_flags |= FAULT_FLAG_TRIED;
goto retry;
}
}
up_read(&mm->mmap_sem);
/*
* Handle the "normal" case first - VM_FAULT_MAJOR
*/
if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP |
VM_FAULT_BADACCESS))))
return 0;
/*
* If we are in kernel mode at this point, we have no context to
* handle this fault with.
*/
if (!user_mode(regs))
goto no_context;
if (fault & VM_FAULT_OOM) {
/*
* We ran out of memory, call the OOM killer, and return to
* userspace (which will retry the fault, or kill us if we got
* oom-killed).
*/
pagefault_out_of_memory();
return 0;
}
if (fault & VM_FAULT_SIGBUS) {
/*
* We had some memory, but were unable to successfully fix up
* this page fault.
*/
sig = SIGBUS;
code = BUS_ADRERR;
} else {
/*
* Something tried to access memory that isn't in our memory
* map.
*/
sig = SIGSEGV;
code = fault == VM_FAULT_BADACCESS ?
SEGV_ACCERR : SEGV_MAPERR;
}
__do_user_fault(tsk, addr, esr, sig, code, regs); //在使用者程序上下文中發現地址無法被對映,比如地址0,那麼就會執行到此處,do user fault
return 0;
no_context: //沒有程序上下文的情況會直接跳到此執行do kernel fault
__do_kernel_fault(mm, addr, esr, regs);
return 0;
}
此函式的中間部分都是進行缺頁異常的處理,比如重新建立頁表,從夥伴系統分配記憶體等等,我們暫且不去進一步分析。只看最後出現異常的情況,異常分兩種,一種是有使用者上下文,一種沒有使用者上下文,分別會去執行__do_user_fault和__do_kernel_fault。這不就是和我們前面的描述,兩種訪問0地址的方式場景匹配了嘛。
/*
* Something tried to access memory that isn't in our memory map. User mode
* accesses just cause a SIGSEGV
*/
static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
unsigned int esr, unsigned int sig, int code,
struct pt_regs *regs)
{
struct siginfo si;
const struct fault_info *inf;
trace_user_fault(tsk, addr, esr);
if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) {
inf = esr_to_fault_info(esr);
pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
tsk->comm, task_pid_nr(tsk), inf->name, sig,
addr, esr);
show_pte(tsk->mm, addr);
show_regs(regs);
}
tsk->thread.fault_address = addr;
tsk->thread.fault_code = esr;
si.si_signo = sig;
si.si_errno = 0;
si.si_code = code;
si.si_addr = (void __user *)addr;
force_sig_info(sig, &si, tsk);
}
/*
* The kernel tried to access some page that wasn't present.
*/
static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
unsigned int esr, struct pt_regs *regs)
{
/*
* Are we prepared to handle this kernel fault?
* We are almost certainly not prepared to handle instruction faults.
*/
if (!is_el1_instruction_abort(esr) && fixup_exception(regs))
return;
/*
* No handler, we'll have to terminate things with extreme prejudice.
*/
bust_spinlocks(1);
pr_alert("Unable to handle kernel %s at virtual address %08lx\n",
(addr < PAGE_SIZE) ? "NULL pointer dereference" :
"paging request", addr);
show_pte(mm, addr);
die("Oops", regs, esr);
bust_spinlocks(0);
do_exit(SIGKILL);
}
相關推薦
核心中訪問空指標(基於kernel-4.9)
在C語言中,我們定義了NULL來表示空指標,空指標是一個特殊的指標,它其實就是0指標,*p = NULL和*p=0是等價的寫法。空指標是一個未賦值的指標,毫無意義的指標,如果訪問到該地址,那麼程式會出錯。 如果在Linux應用程式中訪問NULL指標: 會收到
ARM64核心系統呼叫詳解(基於kernel-4.9)
本文以ARM64為例,介紹如何新增系統呼叫,首先來介紹一些程式碼執行流程: 首先來看異常向量表的配置,核心在arch/arm64/kernel/entry.S彙編程式碼中設定了異常向量表。 /* * Exception vectors. */
用pandas或numpy處理資料中的空值(np.isnan()/pd.isnull())
最近在做資料處理的時候,遇到個讓我欲仙欲死的問題,那就是資料中的空值該如何獲取。 我的目的本來是獲取資料中的所有非零且非空值,然後再計算獲得到的所有資料計算均值,再用均值把0和空值填上。這個操作讓我意識到了i is None/np.isnan(i)/i.isnull()之間的差別,再此
Python爬取百度貼吧回帖中的微訊號(基於簡單http請求)
作者:草小誠 轉載請注原文地址:https://blog.csdn.net/cxcjoker7894/article/details/85685115 前些日子媳婦兒有個需求,想要一個任意貼吧近期主題帖的所有回帖中的微訊號,用來做一些微商的操作,你懂的。因為有些貼吧專門就是
樹莓派3B+ 原始碼方式安裝opencv3(基於3.4.1)
身邊有朋友在樹莓派上安裝不上去opencv3,因此在這裡記錄了一下自己安裝opencv3的過程。 這位前輩的安裝步驟非常非常詳細,下邊所有過程都是參考此經驗,只不過添加了自己安裝過程的圖片,看著好理解一點。 非常感謝,謝謝謝謝謝謝! 安裝過程 更換軟體源的時候建議
Opencv庫組成以及主要檔案作用(版本2.4.9)
opencv主要資料夾構成(版本2.4.
機器學習算法中的評價指標(準確率、召回率、F值、ROC、AUC等)
html eight inf 曲線 mba cor 方法 指標 pan 參考鏈接:https://www.cnblogs.com/Zhi-Z/p/8728168.html 具體更詳細的可以查閱周誌華的西瓜書第二章,寫的非常詳細~ 一、機器學習性能評估指標 1.準確率(A
機器學習演算法中的評價指標(準確率、召回率、F值、ROC、AUC等)
參考連結:https://www.cnblogs.com/Zhi-Z/p/8728168.html 具體更詳細的可以查閱周志華的西瓜書第二章,寫的非常詳細~ 一、機器學習效能評估指標 1.準確率(Accurary) 準確率是我們最常見的評價指標,而且很容易理解,就是被分對的樣本
Linux核心--01(基於armA9tiny4412開發板)
1、如何編譯核心 tar -Jxvf linux-3.5-20170929.tar.xz -C /~ 解壓核心壓縮包至自己的家目錄 建議刪除arch目錄與我們無關的其他架構的資料夾 通過./config生成Makefile,但是因為我們初學核心,不懂得用 m
java 中的空指標,不為空,的理解
一、null是代表不確定的物件 Java中,null是一個關鍵字,用來標識一個不確定的物件。因此可以將null賦給引用型別變數,但不可以將null賦給基本型別變數。 比如:int a = null;是錯誤的。Ojbect o = null是正確的。 Java中,變數的
擁抱PBO(基於專案的組織)聚焦核心價值創造
近年來,PBO(Project-Based Organizations)作為一種新興的整合各類專業智力資源和專業知識的組織結構,受到越來越多的關注,第五版PMBOK出現的新詞彙,三種組織(職能型、矩陣型、專案型)都可以建立PBO,以減輕“等級制度和衙門主義”對專案的不良影響
Quartz和Spring,Mybatis結合,讀資料庫空指標(NullPointerException)
專案中要用定時任務,採用的是Quartz,配置好了可以跑定時了,但是在讀資料庫的時候報空指標,注入的mapper介面類沒有獲取到,費了點時間找到了原因,在這裡記錄下。 我的Quartz是這麼配置的 &
關於微信小程式中圖示的引用(基於iconfont)
步驟如下: iconfont圖示程式碼下載 在該網站中尋找我們需要的圖示,加入購物車(注意是加入購物車哦。如圖) 【加入購物車】 【檢視購物車,點選下載程式碼】 【下載後的檔案,解壓】 關注解壓檔案 【iconfont.ttf】和【
c++中使用空指標呼叫成員函式的理解
使用空指標呼叫成員函式會如何? 舉個例子:base是基類,裡面有兩個函式:non-virtual func2 以及 virtual func1; derived是派生類,使用public繼承自base,裡面有四個函式:virtual func1,non-vi
C++中Reference與指標(Pointer)的使用對比
reference VS pointer 1、 定義: 與pointer 類似,一個reference是一個物件(object),可以用來間接指向另一個物件。 一個reference
SQL中的空值(null)對算術運算、比較運算、集合運算的影響
算術運算如果算術表示式的任一輸入為空,則該算術表示式(涉及諸如 +、-、* 或 / 的算術運算)結果為空。例:如果查詢中有一個表示式是r.A + 5, 並且對於關係中某個特定的元組, r.A為空,那麼對此元組來說,該表示式的結果也為空。比較運算SQL將涉及空值的任何比較運算的
java 程式設計中遇到空指標異常的可能原因java.lang.nullpointerexception
1.所謂的指標,就是java中的物件的引用。比如String s;這個s就是指標。 2.所謂的空指標,就是指標的內容為空,比如上面的s,如果令它指向null,就是空指標。 3.所謂的空指標異常,就是一個指標是空指標,你還要去操作它,既然它指向的是空物件,它就不能使用這個物件
資料結構中佇列的實現(基於順序表迴圈佇列)
在學習資料結構中,佇列也是一個重要的資料結構,我們今天來用基於順序表的佇列(Queue),在基於順序表佇列如果是不迴圈的順序表,則在出佇列時,時間複雜度是O(n),所以我們用迴圈佇列來實現,怎麼解釋基於迴圈順序表的佇列呢?我們上圖: 上圖是在不迴圈順序表中出隊。這樣不
JS去除陣列中的空字串(空格也清除)
$scope.gop.pictTypeArr = $.grep($scope.gop.pictTypeArr, function (x) { return $.trim(x).length
Python中關於URL的處理(基於Python2.7版本)
參考官方文件:https://docs.python.org/3/library/urllib.html點選開啟連結1、 完整的url語法格式: 協議://使用者名稱@密碼:子域名.域名.頂級域名:埠