[問題] kernel module 區域變數記憶體

看板LinuxDev作者 (flawless)時間5年前 (2019/03/27 03:29), 5年前編輯推噓6(6051)
留言57則, 5人參與, 5年前最新討論串1/1
大家好,想請問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

03/27 16:17, 5年前 , 1F
array 是 struct xxx XXX[N] 宣告? copy_to_user 呼叫方法呢
03/27 16:17, 1F

03/27 16:18, 5年前 , 2F
要問 code 就要把 code 貼出來,不要請人隔空抓藥...
03/27 16:18, 2F

03/27 20:41, 5年前 , 3F
抱歉 原本想說只是個很直觀的array of struct跟struct
03/27 20:41, 3F

03/27 20:41, 5年前 , 4F
的配置差別 所以就沒貼上來,等等補上!
03/27 20:41, 4F
※ 編輯: dces4212 (140.128.72.6), 03/27/2019 20:54:24

03/28 00:17, 5年前 , 5F
不太相干的事情: 不要用 VLA, 很容易爆 stack
03/28 00:17, 5F

03/28 00:28, 5年前 , 6F
有爆過了哈哈 慘死 要做完整應該會根據資料結構算個上限
03/28 00:28, 6F

03/28 00:29, 5年前 , 7F
16 KB真的不小心就爆掉..,只是現在遇到這問題實在不解.
03/28 00:29, 7F

03/28 00:35, 5年前 , 8F
你出現錯誤的時候 g 是多少? 我覺得可能是你 stack 爆了去踩
03/28 00:35, 8F

03/28 00:35, 5年前 , 9F
到 text section ...
03/28 00:35, 9F

03/28 00:49, 5年前 , 10F
看 mm/usercopy.c 應該是沒大到踩到 text section 不然錯誤
03/28 00:49, 10F

03/28 00:50, 5年前 , 11F
訊息不太一樣,而且中間踩到 unmapped page 應該會先炸
03/28 00:50, 11F

03/28 00:51, 5年前 , 12F
看起來像是 x86 上超出 stack frame 之類的
03/28 00:51, 12F

03/28 00:51, 5年前 , 13F
乖乖用 kmalloc 吧
03/28 00:51, 13F
只要用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

03/28 10:52, 5年前 , 14F
對吼... 應該要請你附 backtrace 跟解析過的行數才對XD
03/28 10:52, 14F

03/28 10:52, 5年前 , 15F
arch_within_stack_frames 好像 x86 才有實作
03/28 10:52, 15F

03/28 13:24, 5年前 , 16F
backtrace 是指 call stack 跟dump出的register那些嗎
03/28 13:24, 16F

03/28 13:32, 5年前 , 17F
應該是只有x86有這實作 arch/下只看到x86
03/28 13:32, 17F

03/28 15:56, 5年前 , 18F
對啊 # backtrace 是指 call stack 跟dump出的register
03/28 15:56, 18F
了解! [ 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

03/28 21:32, 5年前 , 19F
補充一下,copy_to_user只有在fib__sequence用到
03/28 21:32, 19F

03/31 04:31, 5年前 , 20F
struct U64 fib[g + 1] 為什麼你這樣寫編譯會過?
03/31 04:31, 20F

03/31 10:04, 5年前 , 21F
樓上: google: VLA c99
03/31 10:04, 21F

03/31 10:06, 5年前 , 22F
回原PO: 你確認過 wens 在 8 樓的問題了嗎?
03/31 10:06, 22F

03/31 10:09, 5年前 , 23F
g=0 時, fib[0-1] => fib[-1] 是該被 usercopy 報 BUG(),
03/31 10:09, 23F

03/31 10:10, 5年前 , 24F
但你確定 g=1 到 g=100 也報 BUG() ?
03/31 10:10, 24F

03/31 10:11, 5年前 , 25F
看起來kernel對vla的支援還有點問題
03/31 10:11, 25F

03/31 10:13, 5年前 , 26F

03/31 10:14, 5年前 , 27F
另外, 既然for(...k<=g...)算到 fib[g], 為何是回 fib[g-1]?
03/31 10:14, 27F

03/31 10:33, 5年前 , 28F
@xam: 嗯,要看原PO用的是gcc還是clang.若是clang也許有問題.
03/31 10:33, 28F

03/31 10:38, 5年前 , 29F
又, 原PO用的是4.15, 要4.20才有設-Wvla對VLA給warning.
03/31 10:38, 29F

03/31 10:44, 5年前 , 30F
@xam: 又看了一下那篇,指的是struct的member用到VLA有問題,
03/31 10:44, 30F

03/31 10:47, 5年前 , 31F
而原PO的VLA是C99-style,所以clang支援.
03/31 10:47, 31F

03/31 10:54, 5年前 , 32F
至於第1點, 只是 overhead 問題; 但第3點就不大明暸了...
03/31 10:54, 32F

03/31 11:01, 5年前 , 33F
猜測是要做陣列大小檢查(還是直接改用kmalloc乾脆XD).
03/31 11:01, 33F

03/31 11:03, 5年前 , 34F
用了 kmalloc 就是避用 vla 啊
03/31 11:03, 34F

03/31 11:03, 5年前 , 35F
是說原PO的 fib[g+1] 其實大小只要 fib[3], 撘配 % 運算即可.
03/31 11:03, 35F

03/31 11:05, 5年前 , 36F
然後我猜直接 struct U64 fib[101] 應該也會正常....
03/31 11:05, 36F
發現有意思的地方了...,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

03/31 21:27, 5年前 , 37F
另外我發現我在g=0 時,fib[1].lsl = 1;這段expression
03/31 21:27, 37F

03/31 21:28, 5年前 , 38F
也非法存取了.. 只是沒有panic
03/31 21:28, 38F
忘記補充一點,剛剛測試發現當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

04/04 12:11, 5年前 , 39F
觸發BUG()就是因為arch_within_stack_frames()回傳BAD_STACK.
04/04 12:11, 39F

04/04 12:12, 5年前 , 40F
超過16KB被panic: google "虛擬記憶體" "MMU" "分頁表" 幾項.
04/04 12:12, 40F

04/04 12:12, 5年前 , 41F
fib[1].lsl = 1 可能寫到其它變數(a,tmp,k), 或變數間有空區.
04/04 12:12, 41F

04/04 12:13, 5年前 , 42F
至於 fib[0]賦值後 copy失敗 ==> 程式碼是修改成怎樣?
04/04 12:13, 42F
程式碼幾乎一樣,一開始是發現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

04/09 13:07, 5年前 , 43F
所以 g==0 時, 依舊會執行 fib[1].lsl = 1; 的意思?
04/09 13:07, 43F

04/09 13:08, 5年前 , 44F
或許上述 assignment 恰巧改寫到 copy_to_user(buf, ...) 中
04/09 13:08, 44F

04/09 13:08, 5年前 , 45F
buf 的位址? 編譯器產生怎樣的 obj 不是光看 src 就可得知的.
04/09 13:08, 45F

04/09 13:12, 5年前 , 46F
若是已避掉非法存取, 似乎沒道理發生問題, 除非編譯器有bug?
04/09 13:12, 46F
沒錯,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

04/16 20:26, 5年前 , 47F
就如前幾句說的, obj code 不是光看 src code 就可得知的.
04/16 20:26, 47F

04/16 20:27, 5年前 , 48F
或許 objdump -S 搭配 panic 訊息可窺之一二 (也或許不能).
04/16 20:27, 48F

04/16 20:28, 5年前 , 49F
另外, 是用什麼參數印 buf? &fib[1].lsl 及 &buf 又各是多少?
04/16 20:28, 49F
了解,感謝y大。 用的參數是%p, &fib[1].lsl 及 &buf 都落在0x0~0xffffffff間(多次測試)

04/24 20:54, 5年前 , 50F
想知道為什麼原PO要用VLA?目的是什麼?
04/24 20:54, 50F

04/24 20:55, 5年前 , 51F
Linux kernel目前已禁用VLA。且從需求來看,這函式不需
04/24 20:55, 51F

04/24 20:56, 5年前 , 52F
要不定長度的暫存空間。
04/24 20:56, 52F

04/24 20:57, 5年前 , 53F
04/24 20:57, 53F
感謝z大提醒,這是作業,預期是會有不同項的費氏數列輸入,並且在計算過程中一一印 出每一項的結果,其實也可以不用VLA來實做的。 ※ 編輯: dces4212 (49.213.161.228), 04/26/2019 05:02:50 ※ 編輯: dces4212 (101.9.132.120), 04/26/2019 05:15:12

04/26 05:23, 5年前 , 54F
忽然想到好像不該用%p
04/26 05:23, 54F

04/26 05:23, 5年前 , 55F

04/26 05:23, 5年前 , 56F
-formats.txt
04/26 05:23, 56F

04/26 05:24, 5年前 , 57F
用px pK可能比較妥
04/26 05:24, 57F
文章代碼(AID): #1Scds0ry (LinuxDev)
文章代碼(AID): #1Scds0ry (LinuxDev)