看板 LinuxDev 關於我們 聯絡資訊
大家好,想請問kernel module的function中array of struct與struct的記憶體配置方式 是不是不一樣(變數為函數中直接宣告,未使用kmalloc)?會這樣問是因為最近在寫作業時 遇到使用copy_to_user複製一段記憶體內容到userspace時只要複製的內容是array of struct就會panic,log如下: usercopy: kernel memory exposure attempt detected from 00000000e7ee16e5 (<process stack>) (16 bytes) 但只要把原本要複製的內容放到同個資料結構的struct中就可以正常copy...,以下是複 時用到的資料結構: struct U64 { unsigned long long msl; unsigned long long lsl; }; 然後餵給copy_to_user的arg(size)都一樣是16 bytes。目前推測array of struct配置的 成員記憶體是不連續的,可是kernelspace的virtual address讓我在debug時看到的記憶 體都是不連續的(array of struct與struct),所以不確定這樣推測是否正確。 不知道各位前輩有什麼看法,謝謝大家! **更新**(補上程式碼),以下為可以正常運作的程式碼,原本有問題的版本是使用fib(ar ra of struct)做複製(copy_to_user(buf, &fib[g - 1], size)),另外,size一直都是16 bytes: static long long fib_sequence(long long g, char *buf, size_t size) { unsigned long long a; a = 10000000000000000000; struct U64 fib[g + 1], tmp = {0}; memset(fib, 0, sizeof(struct U64) * (g + 1)); int k; fib[0].lsl = 1; fib[1].lsl = 1; for (k = 2; k <= g; k++) { fib[k].lsl = fib[k - 1].lsl + fib[k - 2].lsl; fib[k].msl = fib[k - 1].msl + fib[k - 2].msl; if (fib[k].lsl > a) { fib[k].lsl = fib[k].lsl - a; fib[k].msl = fib[k].msl + 1; } } tmp = fib[g - 1]; copy_to_user(buf, &tmp, size); return 1; } -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 49.213.161.228 ※ 文章網址: https://www.ptt.cc/bbs/LinuxDev/M.1553628544.A.D7C.html ※ 編輯: dces4212 (49.213.161.228), 03/27/2019 03:40:15
wens: array 是 struct xxx XXX[N] 宣告? copy_to_user 呼叫方法呢03/27 16:17
wens: 要問 code 就要把 code 貼出來,不要請人隔空抓藥...03/27 16:18
dces4212: 抱歉 原本想說只是個很直觀的array of struct跟struct03/27 20:41
dces4212: 的配置差別 所以就沒貼上來,等等補上!03/27 20:41
※ 編輯: dces4212 (140.128.72.6), 03/27/2019 20:54:24
wens: 不太相干的事情: 不要用 VLA, 很容易爆 stack03/28 00:17
dces4212: 有爆過了哈哈 慘死 要做完整應該會根據資料結構算個上限03/28 00:28
dces4212: 16 KB真的不小心就爆掉..,只是現在遇到這問題實在不解.03/28 00:29
wens: 你出現錯誤的時候 g 是多少? 我覺得可能是你 stack 爆了去踩03/28 00:35
wens: 到 text section ...03/28 00:35
wens: 看 mm/usercopy.c 應該是沒大到踩到 text section 不然錯誤03/28 00:49
wens: 訊息不太一樣,而且中間踩到 unmapped page 應該會先炸03/28 00:50
wens: 看起來像是 x86 上超出 stack frame 之類的03/28 00:51
wens: 乖乖用 kmalloc 吧03/28 00:51
只要用array of struct當copy_to_user()的arg就一次都沒成功過(g範圍是0~100),但只 要單用struct就不會出問題。會考慮kmalloc的,感謝。目前推測觸發BUG()的地方是這裡 (因為就連g很小的時候都有問題,就不太可能是#L50的檢查了) (https://elixir.bootlin.com/linux/v4.15.18/source/mm/usercopy.c#L54),#L54做的 檢查其實看不太懂,#L50已經檢查過是否要複製的範圍在stack內,不知道這個是不是檢 查是否為stack內可存取的記憶體,註解寫的if object is safely感覺又不太像這意思, 不知道大大有啥看法。 ※ 編輯: dces4212 (49.213.161.228), 03/28/2019 01:13:03
wens: 對吼... 應該要請你附 backtrace 跟解析過的行數才對XD03/28 10:52
wens: arch_within_stack_frames 好像 x86 才有實作03/28 10:52
dces4212: backtrace 是指 call stack 跟dump出的register那些嗎03/28 13:24
dces4212: 應該是只有x86有這實作 arch/下只看到x8603/28 13:32
wens: 對啊 # backtrace 是指 call stack 跟dump出的register03/28 15:56
了解! [ 4167.013170] usercopy: kernel memory exposure attempt detected from 00000000e7ee16e5 (<process stack>) (16 bytes) [ 4167.013177] ------------[ cut here ]------------ [ 4167.013178] kernel BUG at /build/linux-7kdHqT/linux-4.15.0/mm/usercopy.c:72! [ 4167.013183] invalid opcode: 0000 [#1] SMP PTI [ 4167.013184] Modules linked in: fibdrv(OE)....(已省略) [ 4167.013262] CPU: 1 PID: 16325 Comm: client Tainted: G W OE 4.15.0-46-generic #49-Ubuntu [ 4167.013263] Hardware name: ASUSTeK COMPUTER INC. UX430UN/UX430UN, BIOS UX430UN.302 11/28/2017 [ 4167.013268] RIP: 0010:__check_object_size+0x123/0x1b0 [ 4167.013269] RSP: 0018:ffffbc64858f7e08 EFLAGS: 00010286 [ 4167.013271] RAX: 0000000000000064 RBX: 0000000000000010 RCX: 0000000000000006 [ 4167.013273] RDX: 0000000000000000 RSI: 0000000000000092 RDI: ffff9ac0eec96490 [ 4167.013274] RBP: ffffbc64858f7e28 R08: 0000000000000000 R09: 0000000000001831 [ 4167.013275] R10: 0000000000000000 R11: ffffffffaff5380d R12: 0000000000000001 [ 4167.013276] R13: ffffbc64858f7e38 R14: ffffbc64858f7e28 R15: 1ffff78c90b1efc7 [ 4167.013278] FS: 00007ff1e9d3d500(0000) GS:ffff9ac0eec80000(0000) knlGS:0000000000000000 [ 4167.013279] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 4167.013281] CR2: 00007fffc3d96080 CR3: 00000003d28fe006 CR4: 00000000003606e0 [ 4167.013282] Call Trace: [ 4167.013288] fib_read+0x159/0x197 [fibdrv] [ 4167.013290] ? fib_read+0x34/0x197 [fibdrv] [ 4167.013293] __vfs_read+0x1b/0x40 [ 4167.013294] vfs_read+0x8e/0x130 [ 4167.013296] SyS_read+0x55/0xc0 [ 4167.013300] do_syscall_64+0x73/0x130 [ 4167.013303] entry_SYSCALL_64_after_hwframe+0x3d/0xa2 [ 4167.013305] RIP: 0033:0x7ff1e9861081 [ 4167.013306] RSP: 002b:00007fffc3d51638 EFLAGS: 00000246 ORIG_RAX: 0000000000000000 [ 4167.013308] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff1e9861081 [ 4167.013309] RDX: 0000000000000010 RSI: 00007fffc3d51650 RDI: 0000000000000003 [ 4167.013310] RBP: 00007fffc3d516a0 R08: 00007ff1e9b3dd80 R09: 00007ff1e9b3dd80 [ 4167.013311] R10: 00007fffc3d51620 R11: 0000000000000246 R12: 000055f6d8e947c0 [ 4167.013313] R13: 00007fffc3d51780 R14: 0000000000000000 R15: 0000000000000000 [ 4167.013314] Code: 48 0f 45 d1 48 c7 c6 53 f2 6d af 48 c7 c1 ef ff 6e af 48 0f 45 f1 49 89 d9 49 89 c0 4c 89 f1 48 c7 c7 f8 ff 6e af e8 fd dd e7 ff <0f> 0b f3 c3 48 8b 3d 82 18 1a 01 48 8b 0d 13 99 1d 01 be 00 00 [ 4167.013369] RIP: __check_object_size+0x123/0x1b0 RSP: ffffbc64858f7e08 ※ 編輯: dces4212 (49.213.161.228), 03/28/2019 21:31:09
dces4212: 補充一下,copy_to_user只有在fib__sequence用到03/28 21:32
xam: struct U64 fib[g + 1] 為什麼你這樣寫編譯會過?03/31 04:31
yvb: 樓上: google: VLA c9903/31 10:04
yvb: 回原PO: 你確認過 wens 在 8 樓的問題了嗎?03/31 10:06
yvb: g=0 時, fib[0-1] => fib[-1] 是該被 usercopy 報 BUG(),03/31 10:09
yvb: 但你確定 g=1 到 g=100 也報 BUG() ? 03/31 10:10
xam: 看起來kernel對vla的支援還有點問題03/31 10:11
yvb: 另外, 既然for(...k<=g...)算到 fib[g], 為何是回 fib[g-1]?03/31 10:14
yvb: @xam: 嗯,要看原PO用的是gcc還是clang.若是clang也許有問題.03/31 10:33
yvb: 又, 原PO用的是4.15, 要4.20才有設-Wvla對VLA給warning.03/31 10:38
yvb: @xam: 又看了一下那篇,指的是struct的member用到VLA有問題,03/31 10:44
yvb: 而原PO的VLA是C99-style,所以clang支援.03/31 10:47
yvb: 至於第1點, 只是 overhead 問題; 但第3點就不大明暸了...03/31 10:54
yvb: 猜測是要做陣列大小檢查(還是直接改用kmalloc乾脆XD).03/31 11:01
xam: 用了 kmalloc 就是避用 vla 啊03/31 11:03
yvb: 是說原PO的 fib[g+1] 其實大小只要 fib[3], 撘配 % 運算即可.03/31 11:03
xam: 然後我猜直接 struct U64 fib[101] 應該也會正常.... 03/31 11:05
發現有意思的地方了...,g=0的時候只要用tmp=fib[-1]再把tmp餵給copy_to_user就不會 觸發BUG(),但假如直接餵fib[-1]給copy_to_user就會panic。推測先餵給tmp這邊沒有檢 查是否非法存取所以沒事(目前在userspace測試只能往後拿到6KB左右的資料,之後就被 seg fault了,而kernel space是比較有趣的地方,只要我不把往後拿拿到的資料餵給 copy_to_user,我往後-20000 * 16 byte (struct大小為16bytes)都可以拿到,但假如要 傳到userspace我只能在stack frame(16KB)內偷資料,只要超過就會被panic,這邊很怪 的 地方是kernel怎知道我這tmp裡面偷拿了甚至超過stack frame(16KB)的資料,目前猜測是 kernel 自己有個trap之類的機制隨時在監測是否有access violation)。 另外我是用GCC編譯的。 感謝兩位大大,讓我知道根本不是array of struct跟struct記憶體配置差別,是我自己 在copy_to_user面前非法存取了哈哈。 ※ 編輯: dces4212 (49.213.161.228), 03/31/2019 21:25:14
dces4212: 另外我發現我在g=0 時,fib[1].lsl = 1;這段expression03/31 21:27
dces4212: 也非法存取了.. 只是沒有panic03/31 21:28
忘記補充一點,剛剛測試發現當g=0的時候我對fib[0]賦值後再使用copy_to_user會發生 copy失敗的問題(copy_to_user回傳了16 bytes),蠻怪的..,g>0後都可以正常複製。 ※ 編輯: dces4212 (49.213.161.228), 03/31/2019 22:05:31
yvb: 觸發BUG()就是因為arch_within_stack_frames()回傳BAD_STACK.04/04 12:11
yvb: 超過16KB被panic: google "虛擬記憶體" "MMU" "分頁表" 幾項.04/04 12:12
yvb: fib[1].lsl = 1 可能寫到其它變數(a,tmp,k), 或變數間有空區.04/04 12:12
yvb: 至於 fib[0]賦值後 copy失敗 ==> 程式碼是修改成怎樣? 04/04 12:13
程式碼幾乎一樣,一開始是發現g=0時userspace拿到的資料是0(應為1,fib[0]=1),這時 候覺得很奇怪所以就加個if(g=0) {printk(當下的fib[0].msl, lsl 還有copy_to_user的 ret val)},然後發現fib在kernelspace是有拿到1的,還有copy_to_user的ret是16(複製 失敗大小,成功應為0),差不多是這樣。手機排版,sorry. ※ 編輯: dces4212 (101.10.82.251), 04/05/2019 16:00:31
yvb: 所以 g==0 時, 依舊會執行 fib[1].lsl = 1; 的意思?04/09 13:07
yvb: 或許上述 assignment 恰巧改寫到 copy_to_user(buf, ...) 中 04/09 13:08
yvb: buf 的位址? 編譯器產生怎樣的 obj 不是光看 src 就可得知的.04/09 13:08
yvb: 若是已避掉非法存取, 似乎沒道理發生問題, 除非編譯器有bug?04/09 13:12
沒錯,fib[1].lsl=1;這段每次都會執行到。剛測試了一下,發現確實是這個assigment去 改到buf的內容,會確定的原因挺詭異的,我最先是在assigment前後放printk看buf的內 容,然後只要我保留那段assigment,我新加的printk就會導致panic,而一旦我把那段 assigment 移掉,printk就正常印出位置了...,看來是非法寫入後又嘗試讀取相關記憶 體導致的,這跟之前說tmp偷到的資料只要不copy_to_user就沒事有差不多的概念..,挺 好奇kernel是怎做這個檢查的..,因為這不是直接觸發BUG(),不知道y大有什麼看法? 感謝大大! ※ 編輯: dces4212 (49.213.161.228), 04/12/2019 03:10:28 ※ 編輯: dces4212 (49.213.161.228), 04/12/2019 03:11:12
yvb: 就如前幾句說的, obj code 不是光看 src code 就可得知的.04/16 20:26
yvb: 或許 objdump -S 搭配 panic 訊息可窺之一二 (也或許不能).04/16 20:27
yvb: 另外, 是用什麼參數印 buf? &fib[1].lsl 及 &buf 又各是多少?04/16 20:28
了解,感謝y大。 用的參數是%p, &fib[1].lsl 及 &buf 都落在0x0~0xffffffff間(多次測試)
zack2004: 想知道為什麼原PO要用VLA?目的是什麼?04/24 20:54
zack2004: Linux kernel目前已禁用VLA。且從需求來看,這函式不需04/24 20:55
zack2004: 要不定長度的暫存空間。04/24 20:56
zack2004: https://bit.ly/2IJDnyT 04/24 20:57
感謝z大提醒,這是作業,預期是會有不同項的費氏數列輸入,並且在計算過程中一一印 出每一項的結果,其實也可以不用VLA來實做的。 ※ 編輯: dces4212 (49.213.161.228), 04/26/2019 05:02:50 ※ 編輯: dces4212 (101.9.132.120), 04/26/2019 05:15:12
dces4212: 忽然想到好像不該用%p 04/26 05:23
dces4212: -formats.txt 04/26 05:23
dces4212: 用px pK可能比較妥 04/26 05:24