作者dces4212 (flawless)
看板LinuxDev
标题[问题] kernel module 区域变数记忆体
时间Wed Mar 27 03:28:57 2019
大家好,想请问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://webptt.com/cn.aspx?n=bbs/LinuxDev/M.1553628544.A.D7C.html
※ 编辑: dces4212 (49.213.161.228), 03/27/2019 03:40:15
1F:→ wens: array 是 struct xxx XXX[N] 宣告? copy_to_user 呼叫方法呢03/27 16:17
2F:→ wens: 要问 code 就要把 code 贴出来,不要请人隔空抓药...03/27 16:18
3F:→ dces4212: 抱歉 原本想说只是个很直观的array of struct跟struct03/27 20:41
4F:→ dces4212: 的配置差别 所以就没贴上来,等等补上!03/27 20:41
※ 编辑: dces4212 (140.128.72.6), 03/27/2019 20:54:24
5F:→ wens: 不太相干的事情: 不要用 VLA, 很容易爆 stack03/28 00:17
6F:→ dces4212: 有爆过了哈哈 惨死 要做完整应该会根据资料结构算个上限03/28 00:28
7F:→ dces4212: 16 KB真的不小心就爆掉..,只是现在遇到这问题实在不解.03/28 00:29
8F:→ wens: 你出现错误的时候 g 是多少? 我觉得可能是你 stack 爆了去踩03/28 00:35
9F:→ wens: 到 text section ...03/28 00:35
10F:→ wens: 看 mm/usercopy.c 应该是没大到踩到 text section 不然错误03/28 00:49
11F:→ wens: 讯息不太一样,而且中间踩到 unmapped page 应该会先炸03/28 00:50
12F:→ wens: 看起来像是 x86 上超出 stack frame 之类的03/28 00:51
13F:→ 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
14F:→ wens: 对吼... 应该要请你附 backtrace 跟解析过的行数才对XD03/28 10:52
15F:→ wens: arch_within_stack_frames 好像 x86 才有实作03/28 10:52
16F:→ dces4212: backtrace 是指 call stack 跟dump出的register那些吗03/28 13:24
17F:→ dces4212: 应该是只有x86有这实作 arch/下只看到x8603/28 13:32
18F:→ 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
19F:→ dces4212: 补充一下,copy_to_user只有在fib__sequence用到03/28 21:32
20F:→ xam: struct U64 fib[g + 1] 为什麽你这样写编译会过?03/31 04:31
21F:推 yvb: 楼上: google: VLA c9903/31 10:04
22F:→ yvb: 回原PO: 你确认过 wens 在 8 楼的问题了吗?03/31 10:06
23F:→ yvb: g=0 时, fib[0-1] => fib[-1] 是该被 usercopy 报 BUG(),03/31 10:09
24F:→ yvb: 但你确定 g=1 到 g=100 也报 BUG() ? 03/31 10:10
25F:推 xam: 看起来kernel对vla的支援还有点问题03/31 10:11
27F:→ yvb: 另外, 既然for(...k<=g...)算到 fib[g], 为何是回 fib[g-1]?03/31 10:14
28F:→ yvb: @xam: 嗯,要看原PO用的是gcc还是clang.若是clang也许有问题.03/31 10:33
29F:→ yvb: 又, 原PO用的是4.15, 要4.20才有设-Wvla对VLA给warning.03/31 10:38
30F:→ yvb: @xam: 又看了一下那篇,指的是struct的member用到VLA有问题,03/31 10:44
31F:→ yvb: 而原PO的VLA是C99-style,所以clang支援.03/31 10:47
32F:→ yvb: 至於第1点, 只是 overhead 问题; 但第3点就不大明了了...03/31 10:54
33F:→ yvb: 猜测是要做阵列大小检查(还是直接改用kmalloc乾脆XD).03/31 11:01
34F:→ xam: 用了 kmalloc 就是避用 vla 啊03/31 11:03
35F:→ yvb: 是说原PO的 fib[g+1] 其实大小只要 fib[3], 撘配 % 运算即可.03/31 11:03
36F:→ 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
37F:→ dces4212: 另外我发现我在g=0 时,fib[1].lsl = 1;这段expression03/31 21:27
38F:→ 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
39F:推 yvb: 触发BUG()就是因为arch_within_stack_frames()回传BAD_STACK.04/04 12:11
40F:→ yvb: 超过16KB被panic: google "虚拟记忆体" "MMU" "分页表" 几项.04/04 12:12
41F:→ yvb: fib[1].lsl = 1 可能写到其它变数(a,tmp,k), 或变数间有空区.04/04 12:12
42F:→ 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
43F:推 yvb: 所以 g==0 时, 依旧会执行 fib[1].lsl = 1; 的意思?04/09 13:07
44F:→ yvb: 或许上述 assignment 恰巧改写到 copy_to_user(buf, ...) 中 04/09 13:08
45F:→ yvb: buf 的位址? 编译器产生怎样的 obj 不是光看 src 就可得知的.04/09 13:08
46F:→ 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
47F:推 yvb: 就如前几句说的, obj code 不是光看 src code 就可得知的.04/16 20:26
48F:→ yvb: 或许 objdump -S 搭配 panic 讯息可窥之一二 (也或许不能).04/16 20:27
49F:→ yvb: 另外, 是用什麽参数印 buf? &fib[1].lsl 及 &buf 又各是多少?04/16 20:28
了解,感谢y大。
用的参数是%p, &fib[1].lsl 及 &buf 都落在0x0~0xffffffff间(多次测试)
50F:→ zack2004: 想知道为什麽原PO要用VLA?目的是什麽?04/24 20:54
51F:→ zack2004: Linux kernel目前已禁用VLA。且从需求来看,这函式不需04/24 20:55
52F:→ zack2004: 要不定长度的暂存空间。04/24 20:56
感谢z大提醒,这是作业,预期是会有不同项的费氏数列输入,并且在计算过程中一一印
出每一项的结果,其实也可以不用VLA来实做的。
※ 编辑: dces4212 (49.213.161.228), 04/26/2019 05:02:50
※ 编辑: dces4212 (101.9.132.120), 04/26/2019 05:15:12
54F:→ dces4212: 忽然想到好像不该用%p 04/26 05:23
56F:→ dces4212: -formats.txt 04/26 05:23
57F:→ dces4212: 用px pK可能比较妥 04/26 05:24