安庆

导航

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类型完全不一样的处理模式。

 

 

-------------------------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------------------

有了这个背景知识,我们来看一下如何替换一个内核函数:

const struct file_operations xfs_file_operations = {
    .llseek     = xfs_file_llseek,
    .read       = do_sync_read,
    .write      = do_sync_write,
    .aio_read   = xfs_file_aio_read,
    .aio_write  = xfs_file_aio_write,
    .splice_read    = xfs_file_splice_read,
    .splice_write   = xfs_file_splice_write,
    .unlocked_ioctl = xfs_file_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = xfs_file_compat_ioctl,
#endif
    .mmap       = xfs_file_mmap,
    .open       = xfs_file_open,
    .release    = xfs_file_release,
    .fsync      = xfs_file_fsync,
    .fallocate  = xfs_file_fallocate,
};
 
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));
该变量是一个const变量,假设我们要替换splice_read    成员,如果直接修改,肯定不行,转成指针,就ok了,这样,我就把splice_read  成功地改为了自己的函数。

 

如果如果没有 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,没事。

posted on 2018-07-19 20:07  _备忘录  阅读(377)  评论(0编辑  收藏  举报