我正在撰寫一個檢查任務 UID 的 eBPF kprobe,即呼叫 execve 之間唯一允許的 UID 更改是 setuid()、seteuid() 和 setreuid() 呼叫所允許的那些。
由于探針檢查所有任務,它使用從 init_task 開始迭代的展開回圈,并且它必須使用最多 1024 或 8192 個分支,具體取決于內核版本。
我的問題是,如果存在非法更改,如何實作回傳非零的檢查,定義為:
(new_ruid != old_euid && new_ruid != old_ruid) ||
(new_euid != old_euid && new_euid != old_ruid && new_euid != old_suid)
但不使用分支(clang 使用跳轉來短路檢查之間的任何運算式&&是否計算為真)。
uj5u.com熱心網友回復:
您應該能夠使用按位或、異或、移位和整數乘法來做到這一點。我假設你的變數都是__i32or ,在繼續避免問題之前__u32將它們轉換為 (否則將下面乘法的每個運算元轉換為)。__u64__u64
顯然a != b可以成為a ^ b。&&有點棘手,但可以轉換為乘法(如果有任何運算元,則結果0為0)。然后,您的狀況的第一部分變為:
// (new_ruid != old_euid && new_ruid != old_ruid)
__u64 x = (new_ruid ^ old_euid) * (new_ruid ^ old_euid);
然而對于第二部分,我們有一個溢位問題,因為有 3 個條件。您可以通過將前兩位的結果“壓縮”到低 32 位來避免它,因為您并不真正關心乘法,只關心它的“真實性”:
// (new_euid != old_euid && new_euid != old_ruid && new_euid != old_suid)
__u64 y = (new_euid ^ old_euid) * (new_euid ^ old_ruid);
y = (y >> 32) | (y & 0xffffffff);
y *= (new_euid ^ old_suid);
最后只是 OR 兩個部分的結果。如果需要,也可以再次“壓縮”到低 32 位__u32:
__u64 res = x | y;
// or
__u64 tmp = x | y;
__u32 res = (tmp >> 32) | (tmp & 0xffffffff);
無論優化級別如何,上述所有組合編譯對我來說都沒有任何分支。
uj5u.com熱心網友回復:
在另一個答案之后,有比將高位折疊在低位上更好的縮減功能。
首先從原始問題開始,生成的代碼實際上并沒有那么糟糕。
bool func0(uint64_t new_ruid,uint64_t old_euid, uint64_t old_ruid,
uint64_t new_euid, uint64_t old_suid) {
return
(new_ruid != old_euid && new_ruid != old_ruid) ||
(new_euid != old_euid && new_euid != old_ruid && new_euid != old_suid);
}
_Z5func0mmmmm: # @_Z5func0mmmmm
cmpq %rsi, %rdi
je .LBB0_2
movb $1, %al
cmpq %rdx, %rdi
je .LBB0_2
retq
.LBB0_2:
cmpq %rsi, %rcx
setne %al
cmpq %rdx, %rcx
setne %dl
andb %al, %dl
cmpq %r8, %rcx
setne %al
andb %dl, %al
retq
只有第一部分包含條件分支,而后一部分完全展開到 set_conditionals。
因此,我們有大約三個減少的候選者:
uint32_t reduct1(uint64_t a, uint64_t b) {
a ^= b;
return (a >> 32) | (a & 0xffffffff);
}
movq %rdi, %rax
xorq %rsi, %rax
movq %rax, %rcx
shrq $32, %rcx
orl
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/437334.html
下一篇:C纖維在printf上崩潰
