0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux Tracing System以及實(shí)例解決eBPF程序中遇到的問(wèn)題

Linux閱碼場(chǎng) ? 來(lái)源:Linux閱碼場(chǎng) ? 作者:Linux閱碼場(chǎng) ? 2022-06-13 11:45 ? 次閱讀

一、講座內(nèi)容簡(jiǎn)要描述

本次講座內(nèi)容分為兩部分

1)Linux Tracing System淺析

當(dāng)初學(xué)者接觸到Linux平臺(tái)的tracing系統(tǒng)時(shí),常常被各種詞語(yǔ)弄得暈頭轉(zhuǎn)向:比如 Kprobe,Tracepoint,Linux Auditing subsystem(auditd),systemtap,LTTng,perf,trace-cmd,eBPF,bpftrace,BCC等等。初學(xué)者往往會(huì)有以下疑問(wèn):這些專業(yè)詞語(yǔ)是什么意思?它們之間有什么關(guān)系?每種tracing技術(shù)的優(yōu)缺點(diǎn)是什么?應(yīng)該選擇哪種技術(shù)?為什么eBPF從中脫穎而出,近年來(lái)得到廣泛關(guān)注?

本次講座嘗試從統(tǒng)一的視角來(lái)梳理和對(duì)比這些技術(shù)的異同點(diǎn),并嘗試回答這些問(wèn)題。

2)eBPF開(kāi)發(fā)經(jīng)驗(yàn)分享

eBPF目前正在高速發(fā)展,很多坑和解決辦法缺乏官方文檔。本次講座主要介紹主講人在eBPF開(kāi)發(fā)實(shí)踐中經(jīng)常遇到的問(wèn)題,包括開(kāi)發(fā)框架的選擇,多內(nèi)核版本兼容性問(wèn)題,如何為低版本內(nèi)核生成BTF文件,eBPF驗(yàn)證機(jī)制與編譯器優(yōu)化機(jī)制的不一致問(wèn)題,eBPF在ARM架構(gòu)遇到的問(wèn)題等等。

二、Linux Tracing System淺析

對(duì)于Linux Tracing System尤其是目前最火的eBPF技術(shù)來(lái)說(shuō),主要是通過(guò)探針技術(shù),實(shí)現(xiàn)特定事件的追蹤和采樣,達(dá)到增強(qiáng)內(nèi)核行為可觀測(cè)性、優(yōu)化系統(tǒng)性能、動(dòng)態(tài)監(jiān)測(cè)網(wǎng)絡(luò)和加固系統(tǒng)安全的目的。如下圖所示,我將Linux Tracing System細(xì)分為三個(gè)維度,包括1)數(shù)據(jù)源(內(nèi)核態(tài)),負(fù)責(zé)提供數(shù)據(jù)地來(lái)源;2)Tracing框架(內(nèi)核態(tài)),負(fù)責(zé)對(duì)接數(shù)據(jù)源,采集解析發(fā)送數(shù)據(jù),并對(duì)用戶態(tài)提供接口;3)以及前端工具/庫(kù)(用戶態(tài)),對(duì)接Tracing內(nèi)核框架,直接與用戶交互,負(fù)責(zé)采集配置和數(shù)據(jù)分析。

下面將從這三個(gè)維度自下而上地對(duì)Linux Tracing System進(jìn)行梳理和分析。

7f07b7fe-dd51-11ec-ba43-dac502259ad0.png

1.1.數(shù)據(jù)源(內(nèi)核態(tài))介紹

如下圖所示,從數(shù)據(jù)提供方的角度來(lái)看,數(shù)據(jù)源可以分成硬件探針、軟件探針(又分為動(dòng)態(tài)探針以及靜態(tài)探針),也就是獲取底層數(shù)據(jù)源的方式和手段。顧名思義,硬件探針技術(shù)就是通過(guò)在硬件設(shè)備上(比如芯片)插入探針,捕獲硬件層次行為;而軟件探針技術(shù)則是通過(guò)軟件的方式插入探針,捕獲軟件層次的行為。這些探針技術(shù)負(fù)責(zé)提供數(shù)據(jù),上層的Tracing工具和框架則基于這些探針技術(shù)來(lái)采集數(shù)據(jù),并對(duì)數(shù)據(jù)進(jìn)一步整理、分析、和展現(xiàn)給用戶。

7f3327f4-dd51-11ec-ba43-dac502259ad0.png

硬件探針

HPC: Hardware Performance Counter是CPU硬件提供的一種常見(jiàn)的數(shù)據(jù)源,如下圖所示,它能夠監(jiān)控CPU級(jí)別的事件,比如執(zhí)行的 指令數(shù),跳轉(zhuǎn)指令數(shù),Cache Miss等等,被廣泛應(yīng)用于性能調(diào)試(Vtune, Perf)、攻擊檢測(cè)等等

1)HPC事件列表

7fa68f82-dd51-11ec-ba43-dac502259ad0.png

2)HPC數(shù)據(jù)案例

對(duì)于此類硬件數(shù)據(jù),我們通常使用用戶態(tài)工具perf來(lái)進(jìn)行采集,下圖展示了一個(gè)具體的案例。

7ff3fe2a-dd51-11ec-ba43-dac502259ad0.png

LBR: Last Branch RecordCPU硬件提供的另一種特性,它能夠記錄每條分支(跳轉(zhuǎn))指令的源地址和目的地址?;贚BR硬件特性,可實(shí)現(xiàn)調(diào)用棧信息的記錄

在系統(tǒng)性能優(yōu)化領(lǐng)域以及調(diào)試程序時(shí)經(jīng)常使用的性能分析利器:火焰圖(Flame Graph)也可以基于LBR的數(shù)據(jù)生成,使用命令perf record -F 99 -a --call-graph lbr即可得到完整直觀的火焰圖數(shù)據(jù)。

802848ce-dd51-11ec-ba43-dac502259ad0.png

火焰圖示例

軟件探針(靜態(tài)探針):

靜態(tài)內(nèi)核探針指的內(nèi)核運(yùn)行之前,在內(nèi)核源代碼或者二進(jìn)制中插入預(yù)先設(shè)置好的鉤子函數(shù),內(nèi)核運(yùn)行時(shí)觸發(fā)生效的探針?lè)桨浮?/p>

Tracepoint:Tracepoint是一種典型的靜態(tài)探針。它通過(guò)在內(nèi)核源代碼中插入預(yù)先定義的靜態(tài)鉤子函數(shù)來(lái)實(shí)現(xiàn)內(nèi)核行為的監(jiān)控。簡(jiǎn)單地來(lái)看,大家可以把Tracepoint的原理等同于調(diào)試程序時(shí)加入的printf函數(shù)。

下圖展示了2012年,內(nèi)核引入sched_process_execTracepoint時(shí)的commit,可以看到,首先用TRACE_EVENT宏定義了新增Tracepoint的名字和參數(shù)等信息,然后在內(nèi)核函數(shù)exec_binprm的源代碼中加入了鉤子函數(shù)trace_sched_process_exec。每當(dāng)程序執(zhí)行二進(jìn)制時(shí),都會(huì)觸發(fā)exec_binprm函數(shù),繼而觸發(fā)trace_sched_process_exec鉤子函數(shù)。Tracing工具和框架將自定義的函數(shù)掛載到該鉤子函數(shù)上,來(lái)采集程序執(zhí)行行為日志。

8061da94-dd51-11ec-ba43-dac502259ad0.png

靜態(tài)探針的優(yōu)點(diǎn):

穩(wěn)定(內(nèi)核開(kāi)發(fā)者會(huì)負(fù)責(zé)維護(hù)該函數(shù)的穩(wěn)定性)

性能好

靜態(tài)探針的缺點(diǎn)

需要修改內(nèi)核代碼來(lái)添加新的靜態(tài)探針

內(nèi)核支持的靜態(tài)探針數(shù)量有限

軟件探針(動(dòng)態(tài)探針):

有了靜態(tài)探針,為什么還需要?jiǎng)討B(tài)探針呢?主要原因是靜態(tài)探針都是人工添加的,支持的數(shù)量有限,而動(dòng)態(tài)探針就是為了解決這個(gè)問(wèn)題,它能夠支持Hook幾乎所有的內(nèi)核函數(shù)。

KprobesKprobe是一個(gè)典型的動(dòng)態(tài)探針,如下圖所示,在內(nèi)核運(yùn)行時(shí),Kprobe技術(shù)將需要監(jiān)控的內(nèi)核函數(shù)的指令動(dòng)態(tài)替換,使得該函數(shù)的控制流跳轉(zhuǎn)到用戶自定義的處理函數(shù)上。當(dāng)內(nèi)核執(zhí)行到該監(jiān)控函數(shù)時(shí),相應(yīng)的用戶自定義處理函數(shù)被執(zhí)行,然后繼續(xù)執(zhí)行正常的代碼路徑。

80a00df0-dd51-11ec-ba43-dac502259ad0.png

動(dòng)態(tài)探針的優(yōu)點(diǎn):

可以Hook幾乎所有的內(nèi)核函數(shù)

動(dòng)態(tài)探針的缺點(diǎn)

不穩(wěn)定(函數(shù)的變更、編譯器的優(yōu)化等都可能導(dǎo)致采集程序的失效)

性能相對(duì)較差

軟件探針(動(dòng)/靜態(tài)探針):

靜態(tài)探針性能好,但支持的數(shù)量有限,動(dòng)態(tài)探針支持的數(shù)量多,但不穩(wěn)定、性能相對(duì)較差,那么是否存在一種技術(shù),能同時(shí)兼顧靜態(tài)和動(dòng)態(tài)的優(yōu)勢(shì)呢?答案是動(dòng)靜態(tài)結(jié)合的探針?lè)桨浮?/p>

Function Hooks(Ftrace): Function HooksFtrace引入一種動(dòng)靜態(tài)結(jié)合的探針方案。如下圖所示,靜態(tài)指的是它通過(guò)gcc編譯器,在內(nèi)核編譯階段,在內(nèi)核函數(shù)的入口處插入了預(yù)留的特定指令,當(dāng)內(nèi)核運(yùn)行時(shí),它會(huì)將預(yù)留的特定指令替換為跳轉(zhuǎn)指令(callftrace_caller),使得內(nèi)核函數(shù)的控制流跳轉(zhuǎn)到用戶自定義函數(shù)上,達(dá)到數(shù)據(jù)監(jiān)控的目的。

80f54bda-dd51-11ec-ba43-dac502259ad0.png

Ftrace和Function Tracer

Function Hooks(Ftrace)的優(yōu)點(diǎn)

相比于Tracepoint和Kprobe,F(xiàn)unction Hooks最顯著的功能性特點(diǎn)是它能夠方便地監(jiān)控內(nèi)核函數(shù)的調(diào)用關(guān)系,如下圖所示,監(jiān)控了內(nèi)核函數(shù)exec_binprm的所有子函數(shù)調(diào)用關(guān)系。

814a3906-dd51-11ec-ba43-dac502259ad0.png

上面分析完各種動(dòng)態(tài)和靜態(tài)探針的方案和優(yōu)缺點(diǎn)后,從開(kāi)發(fā)者代碼多功能可控的角度出發(fā),建議優(yōu)先使用靜態(tài)探針?lè)桨浮?/p>

1.2.Linux Tracing System 發(fā)展歷程

? 2004年4月,Linux Auditing subsystem(auditd)被引入內(nèi)核2.6.6-rc1

? 2005年4月,Kprobe被引入內(nèi)核2.6.11.7

? 2006年,LTTng發(fā)布(至今沒(méi)有合入內(nèi)核)

? 2008年10月 ,Kernel Tracepoint 被引入內(nèi)核(v2.6.28)。

? 2008年,F(xiàn)trace被引入內(nèi)核(包括compile time function hooks)。

? 2009年,perf被引入內(nèi)核

? 2009年,SystemTap發(fā)布(至今沒(méi)有合入內(nèi)核)

? 2014年,Alexei Starovoitov將eBPF引入內(nèi)核

1.3.Linux Tracing 框架方案對(duì)比

818c89c8-dd51-11ec-ba43-dac502259ad0.png

eBPF的優(yōu)勢(shì)對(duì)比:

穩(wěn)定:通過(guò)驗(yàn)證器,防止用戶編寫的程序?qū)е聝?nèi)核崩潰

免安裝:eBPF內(nèi)置于linux內(nèi)核,無(wú)需安裝額外以來(lái)

內(nèi)核編程:支持開(kāi)發(fā)者插入自定義的代碼邏輯(包括數(shù)據(jù)采集、分析和過(guò)濾)到內(nèi)核中運(yùn)行

2.eBPF框架開(kāi)發(fā)分析

2.1 eBPF基礎(chǔ)架構(gòu)

eBPF程序分為兩部分: 用戶態(tài)和內(nèi)核態(tài)代碼。

eBPF內(nèi)核代碼:

這個(gè)代碼首先需要經(jīng)過(guò)編譯器(比如LLVM)編譯成eBPF字節(jié)碼,然后字節(jié)碼會(huì)被加載到內(nèi)核執(zhí)行。所以 這部分代碼理論上用什么語(yǔ)言編寫都可以,只要編譯器支持將該語(yǔ)言編譯為eBPF字節(jié)碼即可。

目前絕大多數(shù)工具都是用的C語(yǔ)言來(lái)編寫eBPF內(nèi)核代碼,包括BCC。

bpftrace提供了一種易用的腳本語(yǔ)言來(lái)幫助用戶快速高效的使用eBPF功能,其背后的原理還是利用LLVM 將腳本轉(zhuǎn)為eBPF字節(jié)碼。

eBPF用戶態(tài)代碼:

這部分代碼負(fù)責(zé)將eBPF內(nèi)核程序加載到內(nèi)核,與eBPF MAP交互,以及接收eBPF內(nèi)核程序發(fā)送出來(lái)的數(shù)據(jù)。這個(gè)功能的本質(zhì)上是通過(guò)Linux OS提供的syscall(bpf syscall + perf_event_open syscall)完成的,因此這 部分代碼你可以用任何語(yǔ)言實(shí)現(xiàn)。比如BCC使用python,libbpf使用c或者c++,TRACEE使用Go等等。

81bbd886-dd51-11ec-ba43-dac502259ad0.png

2.2 eBPF數(shù)據(jù)源

性能分析大師Brendan Gregg(Intel Fellow)總結(jié)的Linux BPF Tracing Tools上展示了豐富多彩的eBPF鉤子類型,這些鉤子類型提供了可以加載BPF程序的范圍。

fentry/fexit

Tracepoints

network devices (tc/xdp)

network routes

TCP congestion algorithms

sockets (data level)

kernel functions (kprobes)

userspace functions (uprobes)

system calls

82198580-dd51-11ec-ba43-dac502259ad0.png

2.3 eBPF框架的發(fā)展歷程

2014年9月 引入了bpf() syscall,將eBPF引入用戶態(tài)空間。

自帶迷你libbpf庫(kù),簡(jiǎn)單對(duì)bpf()進(jìn)行了封裝,功能是將eBPF字節(jié)碼加載到內(nèi)核。

2015年2月份 Kernel 3.19 引入bpf_load.c/h文件,對(duì)上述迷你libbpf庫(kù)再進(jìn)行封裝,功能是將eBPF elf二進(jìn)制文件加載到內(nèi)核(目前已過(guò)時(shí),不建議使用)。

2015年4月 BCC項(xiàng)目創(chuàng)建,提供了eBPF一站式編程

1.創(chuàng)建之初,基于上述迷你libbpf庫(kù)來(lái)加載eBPF字節(jié)碼。

2. 提供了Python接口。

2015年11月 Kernel 4.3 引入標(biāo)準(zhǔn)庫(kù) libbpf

1. 該標(biāo)準(zhǔn)庫(kù)由Huawei 2012 OS內(nèi)核實(shí)驗(yàn)室的王楠提交。

2018年 為解決BCC的缺陷,CO-RE(Compile Once, Run Everywhere)的想法被提出并實(shí)現(xiàn),最后達(dá)成共識(shí):libbpf + BTF + CO-RE代表了eBPF的未來(lái),BCC底層實(shí)現(xiàn)逐步轉(zhuǎn)向libbpf。

2.4 eBPF可移植性痛點(diǎn)和解決方案

技術(shù)痛點(diǎn)

在內(nèi)核版本A上編譯的eBPF程序,無(wú)法直接在另外一個(gè)內(nèi)核版本B上運(yùn)行。造成可以執(zhí)行差的根本原因在于eBPF程序訪問(wèn)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)(內(nèi)存空間)是不穩(wěn)定的,經(jīng)常隨內(nèi)核版本更迭而變化。

目前使用BCC的方案通過(guò)在部署機(jī)器上動(dòng)態(tài)編譯eBPF源代碼可以來(lái)解決移植性問(wèn)題。每一次eBPF程序運(yùn)行都需要進(jìn)行一次編譯,而且需要在部署機(jī)器上按照上百兆大小的依賴,如編譯器和頭文件Clang/LLVM + Linux headers等。同時(shí)在Clang/LLVM編譯過(guò)程中需要消耗大量的資源(CPU/內(nèi)存),對(duì)業(yè)務(wù)性能也會(huì)造成很大影響。

解決方案(CO-RE Compile Once,Run Everywhere):

1)BTF:將內(nèi)核數(shù)據(jù)結(jié)構(gòu)信息高效壓縮和存儲(chǔ)(相比于DWARF,可達(dá)到超過(guò)100倍的 壓縮比)

2)LLVM/Clang編譯器:編譯eBPF代碼的時(shí)候記錄下relocation相關(guān)的信息

3)Libbpf:基于BTF和編譯器提供的信息,動(dòng)態(tài)relocate數(shù)據(jù)結(jié)構(gòu)

其中BTF為重要組成部分,Linux Kernel 5.2及以上版本自帶BTF文件,低版本需要手動(dòng)移植。

通過(guò)分析內(nèi)核源碼,可以發(fā)現(xiàn)BTF文件的生成并不需要改動(dòng)內(nèi)核,只依賴:

帶有debug info的vmlinux image

pahole

LLVM

這意味著,我們可以自己為低版本內(nèi)核生產(chǎn)BTF文件,以此讓低內(nèi)核版本支持CORE。

低版本內(nèi)核BTF文件

準(zhǔn)備工作

·安裝pahole軟件(1.16+)

·https://git.kernel.org/pub/scm/devel/pahole/pahole.git

·安裝LLVM(11+)

·獲取目標(biāo)低版本內(nèi)核的vmlinux文件(帶有debug info),文件保存在{vmlinux_file_path}

·通過(guò)源下載

·比如對(duì)于CentOS,通過(guò)yum install kernel-debuginfo可以下載vmlinux

·源碼編譯內(nèi)核,獲取vmlinux

生成BTF:

·利用pahole在vmlinux文件中生成BTF信息,執(zhí)行以下命令:

·pahole -J {vmlinux_file_path}

·將BTF信息單獨(dú)輸出到新文件{BTF_file_path},執(zhí)行以下命令:

·llvm-objcopy --only-section=.BTF --set-section-flags .BTF=alloc,readonly --strip- all {vmlinux_file_path} {BTF_file_path}

·去除非必要的符號(hào)信息,降低BTF文件的大小,得到最終的BTF文件(大小約2~3MB):

·strip -x {BTF_file_path}

2.5 eBPF程序?qū)嵗治觯ㄒ粋€(gè)Print引發(fā)的慘案)

eBPF程序會(huì)被LLVM編譯為eBPF字節(jié)碼,eBPF字節(jié)碼需要通過(guò)eBPF Verifier的(靜態(tài))驗(yàn)證后,才能真正運(yùn)行。邊界檢查是eBPF Verifier的重點(diǎn)工作,目的是為了防止eBPF程序內(nèi)存越界訪問(wèn)。接下來(lái)通過(guò)在eBPF程序中簡(jiǎn)單的增加、刪減print打印信息觸發(fā)不同原因的幾種邊界檢查異常導(dǎo)致驗(yàn)證失敗的例子,進(jìn)一步講解深層的原理。

程序?qū)嶒?yàn)環(huán)境:

1)LLVM 11

2)Linux Kernel 5.8

3)Libbpf commit @9c44c8a

邊界檢查案例:

1)內(nèi)存越界:

SEC("kprobe/do_unlinkat")int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name){  // 獲取一個(gè)數(shù)組指針array(數(shù)組MAX_SIZE為16個(gè)字節(jié))  u32 key = 0;  char *array = bpf_map_lookup_elem(&array_map, &key);  if (array == NULL)    return 0;  // 獲取當(dāng)前運(yùn)行程序的CPU編號(hào)(當(dāng)前機(jī)器的CPU有16個(gè)核)  unsigned int pos = bpf_get_smp_processor_id();       // 根據(jù)下表修改數(shù)組的值      array[pos] = 1;      return 0;}

上述代碼編譯運(yùn)行后,提示Verifier失敗,然后使用objdump命令來(lái)看一下具體的字節(jié)碼,通過(guò)以下字節(jié)碼程序,可以看到Verifier失敗的原因在于第14行R6寄存器(變量pos)沒(méi)有進(jìn)行邊界檢查導(dǎo)致。

Root Cause:

當(dāng)eBPF Verifier走到第14行的時(shí)候嘗試去訪問(wèn)array數(shù)組,但是此時(shí)數(shù)組的下標(biāo)pos是來(lái)自bpf_get_smp_processor_id獲取到的unsigned int 類型的動(dòng)態(tài)變量,此時(shí)Verifier無(wú)法判斷變量的具體數(shù)值,所以會(huì)保守認(rèn)為可能會(huì)達(dá)到最大值,這樣的話就會(huì)超出array數(shù)組的范圍,造成內(nèi)存越界。

0000000000000000 :;intBPF_KPROBE(do_unlinkat,intdfd,structfilename*name)0:r1=0;  u32 key = 0;1:  *(u32 *)(r10 - 4) = r12:  r2 = r103:  r2 += -4;      char *array = bpf_map_lookup_elem(&array_map, &key); 4:r1 = 0 ll6:  call 17:  r6 = r0;  if (array == NULL)8:  if r6 == 0 goto +6 ;      unsigned int pos = bpf_get_smp_processor_id();; 9:call 8;      array[pos] = 1; 10:r0 <<= 3211:  r0 >>= 3212:  r6 += r013:  r1 = 1;    array[pos] = 1;14:  *(u8 *)(r6 + 0) = r1

解決方案:

添加邊界檢查代碼

if (pos < MAX_SIZE)???if?r0?>15goto+3

2)Verifier驗(yàn)證機(jī)制和編譯器優(yōu)化機(jī)制不一致導(dǎo)致邊界檢查不通過(guò)

①使用錯(cuò)誤寄存器做邊界檢查:

SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name){  // 獲取一個(gè)數(shù)組指針array(數(shù)組MAX_SIZE為16個(gè)字節(jié))  u32 key = 0;  char *array = bpf_map_lookup_elem(&array_map, &key); if (array == NULL)    return 0;  // 獲取當(dāng)前運(yùn)行程序的CPU編號(hào)(當(dāng)前機(jī)器的CPU有16個(gè)核)  unsigned int pos = bpf_get_smp_processor_id();;  // 修改數(shù)值  if (pos < MAX_SIZE){    array[pos] = 1;    pos += 1;  }  // debug代碼,輸出一些上下文信息  bpf_printk("debug %d %d %d
", bpf_get_current_pid_tgid() >> 32, bpf_get_current_pid_tgid(), array[1]);  // 修改數(shù)值  if (pos < MAX_SIZE)    array[pos] = 1;  return 0;}

編譯這個(gè)代碼后Verifier驗(yàn)證通過(guò),可以正常運(yùn)行。但是此時(shí)如果把bpf_printk打印信息刪掉,竟然提示Verifier驗(yàn)證失敗,原因是R0寄存器(變量pos)沒(méi)有通過(guò)邊界檢查,但是明明已經(jīng)加了邊界檢查代碼,怎么還會(huì)出現(xiàn)問(wèn)題,這么神奇!

Root Cause:

8275b88c-dd51-11ec-ba43-dac502259ad0.png

由于編譯器的優(yōu)化策略,導(dǎo)致刪減bpf_printk后編譯生成的eBPF字節(jié)碼使用寄存器r1(表示pos變量)來(lái)進(jìn)行邊界檢查,但是卻用r0+1(同樣表示pos變量)來(lái)訪問(wèn)數(shù)組array。

相比之下,從eBPF verifier的角度來(lái)看,由于在編譯過(guò)程中,r1和r0+1的關(guān)聯(lián)性丟失了,導(dǎo)致eBPF verifier無(wú)法知道pos變量已經(jīng)通過(guò)了檢查,因此錯(cuò)誤的認(rèn)為pos變量沒(méi)有進(jìn)行邊界檢查,不允許程序運(yùn)行。

②寄存器溢出或重新加載后,狀態(tài)丟失:

SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name){  //獲取一個(gè)數(shù)組指針array(數(shù)組MAX_SIZE為16個(gè)字節(jié))  u32 key = 0;  char *array = bpf_map_lookup_elem(&array_map, &key); if (array == NULL)    return 0;  // 獲取當(dāng)前運(yùn)行程序的CPU編號(hào)(當(dāng)前機(jī)器的CPU有16個(gè)核)  unsigned long pos = bpf_get_smp_processor_id();;  // 修改數(shù)值  if (pos < MAX_SIZE){    for (unsigned long i = 0; i < MAX_SIZE; i++)      bpf_printk("debug %d %d %d
", bpf_get_current_pid_tgid() >> 32,          bpf_get_current_pid_tgid(), array[i]);    array[pos] = 1;  }  return 0;}

在上述邊界檢查代碼中添加一段print調(diào)試打印信息后編譯驗(yàn)證又會(huì)出現(xiàn)Verifier失敗,通過(guò)排查發(fā)現(xiàn)不是已知的兩類問(wèn)題,依然使用objdump查看添加后的字節(jié)碼信息。

Root Cause:

82bcd4ba-dd51-11ec-ba43-dac502259ad0.png

加入bpf_printk后通過(guò)字節(jié)碼可以看到,代碼先使用R0(表示pos變量)進(jìn)行邊界檢查。由于當(dāng)前寄存器數(shù)量不足,編譯器決定將將R0臨時(shí)保存到棧上的空間(R10-16,在eBPF字節(jié)碼中,R10存儲(chǔ)存放著 eBPF ??臻g的棧幀指針的地址),這樣R0就可以空閑出來(lái),留給其他代碼使用,我們稱這種行為為寄存器溢出(register spill)。當(dāng)真正需要使用pos變量的時(shí)候,編譯器會(huì)從棧上(R10-16)將之前保存的內(nèi)容取出來(lái)賦給R1(也表示pos變量),然后使用R1對(duì)數(shù)組array進(jìn)行訪問(wèn)。但神奇的是,當(dāng)寄存器溢出發(fā)生時(shí),pos變量的狀態(tài)丟失了,eBPF忘記了該變量曾經(jīng)進(jìn)行了邊界檢查,導(dǎo)致程序無(wú)法通過(guò)驗(yàn)證。

解決方案:

在源碼中加入 &= 操作符,引導(dǎo)編譯器生成理想的eBPF字節(jié)碼

array[pos &= MAX_SIZE - 1] = 1;

如果上述方法失效,無(wú)法引導(dǎo)編譯器,那么針對(duì)出錯(cuò)的部分源代碼人工編寫eBPF字節(jié)碼,替代編譯器生成的字節(jié)碼

#defineSTR(s)#s#defineXSTR(s)STR(s)#defineasm_variable_bound_check(variable)({  asmvolatile(    "%[tmp]&="XSTR(MAX_SIZE-1)"
"    :[tmp]"+&r"(variable)  );})asm_check(pos);array[pos] = 1;

3.總結(jié)

本文總結(jié)了從動(dòng)靜態(tài)探針的角度梳理分析Linux Tracing System以及實(shí)例解決eBPF程序中遇到的問(wèn)題。eBPF目前正在高速發(fā)展,很多坑和解決辦法缺乏官方文檔。本文在以下幾點(diǎn)上做了自己的分析和分享,希望對(duì)大家更清晰的認(rèn)識(shí)Linux Tracing System和eBPF有所幫助。

1.自下而上的方式分析動(dòng)靜態(tài)探針

2.各種場(chǎng)景下動(dòng)靜態(tài)探針的選擇

3.BPF開(kāi)發(fā)框架的選擇

4.多內(nèi)核版本兼容性問(wèn)題

5.如何為低版本內(nèi)核生成BTF文件

6.eBPF邊界檢查問(wèn)題分析

7.eBPF Verifier驗(yàn)證機(jī)制與編譯器優(yōu)化機(jī)制不一致問(wèn)題

原文標(biāo)題:Linux Tracing System淺析和eBPF開(kāi)發(fā)經(jīng)驗(yàn)分享

文章出處:【微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10702

    瀏覽量

    209378
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8257

    瀏覽量

    149957
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207918
  • 硬件
    +關(guān)注

    關(guān)注

    11

    文章

    3113

    瀏覽量

    65850

原文標(biāo)題:Linux Tracing System淺析和eBPF開(kāi)發(fā)經(jīng)驗(yàn)分享

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    實(shí)戰(zhàn)eBPF kprobe函數(shù)插樁

    本文作者為團(tuán)隊(duì)小伙伴阿松,在Linux文件監(jiān)控領(lǐng)域?qū)崙?zhàn)經(jīng)驗(yàn)豐富。本次引入eBPF在文件監(jiān)控上應(yīng)用,提升文件變更的關(guān)聯(lián)進(jìn)程信息等。在實(shí)現(xiàn)過(guò)程,分享了eBPF kbproe時(shí),被插樁函數(shù)
    發(fā)表于 11-29 09:03 ?1876次閱讀

    Application Level Tracing概述

    Application Level Tracing概述API介紹頭文件函數(shù)原型原文地址概述是esp32提供的一種調(diào)試方案,通過(guò)JTAG接口可以在程序執(zhí)行時(shí)以很小的開(kāi)銷在主機(jī)和esp32間傳輸數(shù)據(jù)
    發(fā)表于 01-07 07:20

    關(guān)于 eBPF 安全可觀測(cè)性,你需要知道的那些事兒

    核來(lái)說(shuō)其存在感極低但觀測(cè)能力卻異常強(qiáng)大(藥效好,副作用?。?b class='flag-5'>程序沙箱化:通過(guò) eBPF 驗(yàn)證器保護(hù)內(nèi)核穩(wěn)定運(yùn)行。侵入性低:無(wú)須修改內(nèi)核代碼,且無(wú)須停止程序運(yùn)行。透明化:從內(nèi)核透明搜集
    發(fā)表于 09-08 15:31

    openEuler 倡議建立 eBPF 軟件發(fā)布標(biāo)準(zhǔn)

    生產(chǎn)環(huán)境大規(guī)模應(yīng)用。缺點(diǎn):應(yīng)用技術(shù)門檻高,且不具備可移植性(比如高內(nèi)核版本的 eBPF 程序無(wú)法移植至低內(nèi)核版本)。開(kāi)發(fā)態(tài)、運(yùn)行態(tài)融合(典型代表 BCC)優(yōu)點(diǎn):源碼形式發(fā)布天然具備可移植性;封裝抽象
    發(fā)表于 12-23 16:21

    UART應(yīng)用實(shí)例源代碼程序-UART In-Applic

    UART應(yīng)用實(shí)例源代碼程序:A UART code loader provides in-system reprogrammability of program code spac
    發(fā)表于 01-23 23:04 ?66次下載

    qq版原理linux實(shí)例程序代碼

    qq版原理linux實(shí)例程序代碼(對(duì)應(yīng)qq版原理圖):
    發(fā)表于 05-27 08:48 ?0次下載
    qq版原理<b class='flag-5'>linux</b>下<b class='flag-5'>實(shí)例</b>源<b class='flag-5'>程序</b>代碼

    The Linux Programming Interface - A Linux and UNIX System Programming Handbook

    The Linux Programming Interface - A Linux and UNIX System
    發(fā)表于 03-02 11:42 ?7次下載

    System Generator的設(shè)計(jì)實(shí)例

    Xilinx FPGA工程例子源碼:System Generator的設(shè)計(jì)實(shí)例
    發(fā)表于 06-07 14:41 ?22次下載

    eBPF是什么以及eBPF能干什么

    一、eBPF是什么 eBPF是extended BPF的縮寫,而BPF是Berkeley Packet Filter的縮寫。對(duì)linux網(wǎng)絡(luò)比較熟悉的伙伴對(duì)BPF應(yīng)該比較了解,它通過(guò)特定的語(yǔ)法
    的頭像 發(fā)表于 07-05 15:17 ?1.1w次閱讀
    <b class='flag-5'>eBPF</b>是什么<b class='flag-5'>以及</b><b class='flag-5'>eBPF</b>能干什么

    Linux 內(nèi)核:eBPF優(yōu)勢(shì)和eBPF潛力總結(jié)

    Express Data Path (XDP):網(wǎng)絡(luò)驅(qū)動(dòng)程序是最早可以附加 XDP BPF 鉤子的點(diǎn)。當(dāng)收到一個(gè)數(shù)據(jù)包時(shí),eBPF 程序就會(huì)被觸發(fā)運(yùn)行。
    發(fā)表于 01-10 11:37 ?2840次閱讀

    eBPF,何以稱得上是革命性的內(nèi)核技術(shù)?

    eBPF 的全稱是 extended Berkeley Packet Filter,它被稱之為 “革命性” 的內(nèi)核技術(shù),可以在 Linux 內(nèi)核運(yùn)行沙盒程序,而無(wú)需更改內(nèi)核源代碼或加
    的頭像 發(fā)表于 05-08 08:26 ?526次閱讀
    <b class='flag-5'>eBPF</b>,何以稱得上是革命性的內(nèi)核技術(shù)?

    eBPF的前世今生?eBPF在使用遇到的問(wèn)題有哪些?

    在介紹eBPF (Extended Berkeley Packet Filter)之前,我們先來(lái)了解一下它的前身-BPF (Berkeley Packet Filter)伯克利數(shù)據(jù)包過(guò)濾器。
    的頭像 發(fā)表于 08-12 15:10 ?1420次閱讀
    <b class='flag-5'>eBPF</b>的前世今生?<b class='flag-5'>eBPF</b>在使用<b class='flag-5'>中</b><b class='flag-5'>遇到</b>的問(wèn)題有哪些?

    基于ebpf的性能工具-bpftrace

    運(yùn)行情況對(duì)于診斷問(wèn)題、優(yōu)化性能以及進(jìn)行安全監(jiān)控至關(guān)重要。bpftrace作為一款強(qiáng)大的跟蹤工具,為開(kāi)發(fā)人員和系統(tǒng)管理員提供了一種獨(dú)特的方式來(lái)監(jiān)視和分析Linux系統(tǒng)的內(nèi)部運(yùn)行。本文描述bpftrace的原理和使用。 bpftrace 「bpftrace是基于
    的頭像 發(fā)表于 09-04 16:02 ?577次閱讀
    基于<b class='flag-5'>ebpf</b>的性能工具-bpftrace

    如何使用Tokio 和 Tracing模塊構(gòu)建異步的網(wǎng)絡(luò)應(yīng)用程序

    ,并在調(diào)試和故障排除時(shí)提供有用的信息。 在本教程,我們將介紹如何使用 Tokio 和 Tracing 模塊來(lái)構(gòu)建一個(gè)異步的網(wǎng)絡(luò)應(yīng)用程序,并使用 Tracing 來(lái)記錄應(yīng)用
    的頭像 發(fā)表于 09-19 15:29 ?550次閱讀

    ebpf的快速開(kāi)發(fā)工具--libbpf-bootstrap

    )和libbpf的程序eBPF是一種可以在Linux內(nèi)核運(yùn)行的程序,提供了強(qiáng)大的網(wǎng)絡(luò)過(guò)濾、系統(tǒng)調(diào)用監(jiān)控和性能分析等功能。libbpf是一
    的頭像 發(fā)表于 09-25 09:04 ?737次閱讀
    <b class='flag-5'>ebpf</b>的快速開(kāi)發(fā)工具--libbpf-bootstrap