linux内核中的const成员是否可以修改?
本文的基础知识:由于前半部分内容是转的,且不知道原文出处,没法给出原文地址,大家自行百度
const的实现机制
const究竟是如何实现的呢?对于声明为const的内置类型,例如int,short,long等等,编译器会如何实现const的本意?那么对于非内置类型是否也是与内置数据类型一样处理呢,例如对于结构体类型则会怎样处理呢?下面通过几个小例子来说明这些问题:
C语言const示例:
const int i=10;
int *p=(int *)(&i);
*p=20;
printf("i=%d *p=%d \n",i,*p);
猜一猜输出结果是什么? i=20 *p=20
C++语言const示例1:
const int i=10;
int *p=const_cast<int *>(&i);
*p=20;
cout<<"i="<<i<<"*p="<<*p<<endl;
输出结果是 i=10 *p=20
C++语言const示例2:
struct test{
int j;
char tmp;
test()
{
j=30;
tmp='a';
}
};
int main(int argc, char* argv[])
{
const struct test t1;
int *q=(int *)(&t1.j);
*q=40;
cout<<"j="<<t1.j<<"*q="<<*q<<endl;
return 0;
}
输出结果是 j=40 *q=40
示例结果分析
看到上面三组输出结果,我们可以分析两个问题:
问题1:C语言和C++语言中的const究竟表示什么?
问题2:const的实现机制究竟是怎样的?
问题1,对于const int类型的变量i,C语言中通过指针p修改了值后,i变成了20;而在C++中,通过指针p修改了值后,i仍然是10。
问题2,C++语言中 const struct test的元素j通过指针q被改变了,为何const int 与 const struct test的反应机制不同?
针对问题1,我们知道C语言中const表示只读的变量,既然把const看成是变量,那么其在内存中就会有存储他的空间,并且可以通过指针间接的改变该内存空间的值,当通过指针p改变该内存中的值后,再获取i的值的时候,会访问该空间,得到的是被改变后的值。而C++把const看做常量,编译器会使用常数直接替换掉对i的引用,例如cout<<i; 会理解成cout<<10; 并不会去访问i的内存地址去取数据,这里有点像是C语言里的宏#define i 10。因此C++里i会输出10,而*p会输出20.
针对问题2,C++语言中只是对于内置数据类型做常数替换,而对于像结构体这样的非内置数据类型则不会。因为结构体类型不是内置数据类型,编译器不知道如何直接替换,因此必须要访问内存去取数据,而访问内存去取数据必然会取到被指针q改变后的值,因此会造成与C++中const int类型完全不一样的处理模式。
-------------------------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------------------
有了这个背景知识,我们来看一下如何替换一个内核函数:
p_xfs_file_operations =(struct file_operations*)kallsyms_lookup_name("xfs_file_operations"); pte_xfs_splice = lookup_address((unsigned long)p_xfs_file_operations, &level); // change PTE to allow writing set_pte_atomic(pte_xfs_splice, pte_mkwrite(*pte_xfs_splice)); orig_xfs_file_splice_read = p_xfs_file_operations->splice_read; p_xfs_file_operations->splice_read = caq_xfs_file_splice_read; set_pte_atomic(pte_xfs_splice, pte_clear_flags(*pte_xfs_splice, _PAGE_RW));
如果如果没有 set_pte_atomic(pte_xfs_splice, pte_mkwrite(*pte_xfs_splice)); 这行代码会怎么样?
答案是,如果当时这个页没有写权限,肯定会出crash,如果有的话,这行代码就没用,但是
set_pte_atomic(pte_xfs_splice, pte_clear_flags(*pte_xfs_splice, _PAGE_RW));
这行代码就会有问题,因为后面可能有人去写这个页的时候就会crash。出现crash,打印一般如下:
<1>[51178.495137] BUG: unable to handle kernel paging request at ffffffffa0671ab8 <1>[51178.495150] IP: [<ffffffffa06cb312>] replace_sendfile+0x2c2/0x300 [newsendfile] <4>[51178.495163] PGD 1a0b067 PUD 1a0f063 PMD 391bf00067 PTE 800000391bea7161 <0>[51178.495173] Oops: 0003 [#1] SMP <4>[51178.495179] CPU 0 <4>[51178.495181] Modules linked in: newsendfile(EN+) datalink(EN) xfs w83627dhg(EN) tipc(EX) ossmod(EN) witdriver(EN) bonding ip6table_filter ip6_tables iptable_filter ip_tables ebtable_nat ebtables x_tables af_packet ipmi_devintf ipmi_si ipmi_msghandler edd cpufreq_conservative cpufreq_userspace cpufreq_powersave acpi_cpufreq mperf fuse loop dm_mod vhost_net macvtap macvlan ipv6 ipv6_lib tun kvm_intel kvm pcspkr ses enclosure usbhid hid i40e(EX) sg igb i2c_i801 iTCO_wdt iTCO_vendor_support dca mei mptctl ptp mptbase pps_core rtc_cmos acpi_power_meter container button ext3 jbd mbcache ttm drm_kms_helper drm i2c_algo_bit sysimgblt sysfillrect i2c_core syscopyarea ehci_hcd usbcore usb_common sd_mod crc_t10dif processor thermal_sys hwmon scsi_dh_hp_sw scsi_dh_alua scsi_dh_rdac scsi_dh_emc scsi_dh mpt3sas(EX) configfs scsi_transport_sas raid_class scsi_mod <4>[51178.495291] Supported: No, Unsupported modules are loaded <4>[51178.495295] <4>[51178.495300] Pid: 24329, comm: insmod Tainted: G ENX 3.0.101-0.47.90-default #1 ZTE Grantley/S1008 <4>[51178.495309] RIP: 0010:[<ffffffffa06cb312>] [<ffffffffa06cb312>] replace_sendfile+0x2c2/0x300 [newsendfile] <4>[51178.495321] RSP: 0018:ffff88190f2b7ee8 EFLAGS: 00010286 <4>[51178.495325] RAX: ffffffffa0661b10 RBX: ffffffff81bd5160 RCX: ffff880001a0bff0 <4>[51178.495330] RDX: ffffffffa0671a00 RSI: ffffffffa06cf4d4 RDI: ffffffffa06cd150 <4>[51178.495335] RBP: ffffffffa069e880 R08: 000000391bf00000 R09: ffff880000000000 <4>[51178.495340] R10: 00000000000926bc R11: 00000000ffffffff R12: ffffffffa014c000 <4>[51178.495345] R13: 0000000000000000 R14: 00007ffc45684787 R15: 00007fcc25041010 <4>[51178.495351] FS: 00007fcc25104700(0000) GS:ffff88207fc00000(0000) knlGS:0000000000000000 <4>[51178.495357] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 <4>[51178.495361] CR2: ffffffffa0671ab8 CR3: 0000001f177bc000 CR4: 00000000001407f0 <4>[51178.495366] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 <4>[51178.495371] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 <4>[51178.495377] Process insmod (pid: 24329, threadinfo ffff88190f2b6000, task ffff881d217ac540) <0>[51178.495381] Stack: <4>[51178.495384] 0000000000000001 0000000000002db3 ffffffff81da1ac0 0000000000000000 <4>[51178.495396] 00000000000720e1 ffffffffa014c29a 00000000000720e1 ffffffffa06cf1a0 <4>[51178.495405] 00000000000720e1 ffffffff810001cb 00007fcc25041010 ffffffffa06cf1a0 <0>[51178.495414] Call Trace: <4>[51178.495444] [<ffffffffa014c29a>] newsendfile_init+0x29a/0x1000 [newsendfile] <4>[51178.495458] [<ffffffff810001cb>] do_one_initcall+0x3b/0x180 <4>[51178.495471] [<ffffffff810a303f>] sys_init_module+0xcf/0x240 <4>[51178.495482] [<ffffffff8146f5f2>] system_call_fastpath+0x16/0x1b <4>[51178.495495] [<00007fcc24c4ad7a>] 0x7fcc24c4ad79
根据crash文件,我们获取以下对应函数的起始地址:
objdump -D /home/caq/newsendfile.ko |grep replace_sendfile |head -10 0000000000000050 <replace_sendfile>: 64: e8 00 00 00 00 callq 69 <replace_sendfile+0x19> 70: e8 00 00 00 00 callq 75 <replace_sendfile+0x25> 7c: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # 83 <replace_sendfile+0x33> 83: e8 00 00 00 00 callq 88 <replace_sendfile+0x38> 8f: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # 96 <replace_sendfile+0x46> 96: e8 00 00 00 00 callq 9b <replace_sendfile+0x4b> a2: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # a9 <replace_sendfile+0x59> a9: e8 00 00 00 00 callq ae <replace_sendfile+0x5e> b5: 48 89 05 00 00 00 00 mov %rax,0x0(%rip) # bc <replace_sendfile+0x6c>
(0x50+0x2c2)=0x312,
addr2line -e /home/caq/newsendfile.ko 0x312 /home/caq/newsendfile.c:3007
3001 p_xfs_file_operations =(struct file_operations*)kallsyms_lookup_name("xfs_file_operations"); 3002 pte_xfs_splice = lookup_address((unsigned long)p_xfs_file_operations, &level); 3003 // change PTE to allow writing 3004 //set_pte_atomic(pte_xfs_splice, pte_mkwrite(*pte_xfs_splice)); 3005 3006 orig_xfs_file_splice_read = p_xfs_file_operations->splice_read; 3007 p_xfs_file_operations->splice_read = caq_xfs_file_splice_read;
而对应的行号3007的就是: p_xfs_file_operations->splice_read = caq_xfs_file_splice_read;没有写权限的时候,去写这个page,就出crash了。可以明显看到,3006行是去读这个page,没事。