Linux内核驱动模块内存释放问题:kfree放置及rmmod报错解决
Linux内核模块内存释放问题
我正在编写一个Linux内核模块,需要分配内存存储数据。因为要获取用户应用传入的buffer大小,内存分配(kmalloc)只能在VFS API的write函数里执行,无法在该函数外完成。现在搞不清kfree()该放置在何处:若放在cleanup函数中,执行rmmod卸载模块时会触发内核错误,注释掉kfree则错误消失。
write函数代码
ssize_t hcsr04_write(struct file *filp, const char *buffer, size_t length, loff_t * offset) { if (pulsecount < (5)){ pulseptr[pulsecount] = kmalloc(sizeof(buffer),GFP_ATOMIC); sprintf (pulseptr[pulsecount],"%s",buffer); pulsecount++; } else{ int j = 0; while (j<4){ sprintf (pulseptr[j], "%s", (pulseptr[j+1]) ); // [5 , 20 , 30 , 70 , 50] ===> [20 , 30 , 70 , 50 , 50] j++; } sprintf (pulseptr[4],"%s",buffer); } }
cleanup函数代码
static void __exit hcsr04_module_cleanup(void) { //if (pulseptr!= {NULL,NULL,NULL,NULL,NULL}){ kfree(pulseptr); printk(KERN_INFO "Dynamic memory freed successfully."); //} //pulseptr = {NULL,NULL,NULL,NULL,NULL}; gpio_free( GPIO_OUT ); gpio_free( GPIO_IN ); hcsr04_lock = 0; cdev_del(&hcsr04_cdev); unregister_chrdev_region( hcsr04_dev, 1 ); kobject_put( hcsr04_kobject ); }
rmmod执行时的错误信息
[ 93.294821] 8<--- cut here --- [ 93.297928] Unable to handle kernel paging request at virtual address bcf03574 [ 93.305253] pgd = 3cdbb3d3 [ 93.307993] [bcf03574] *pgd=00000000 [ 93.311621] Internal error: Oops: 5 [#1] SMP ARM [ 93.316301] Modules linked in: hcsr04(O-) nfc bnep bluetooth ecdh_generic ecc ipv6 hello(PO) g_serial libcomposite udc_core brcmfmac brcmutil sha256_generic libsha256 vc4 cfg80211 bcm2835_codec(C) rfkill bcm2835_isp(C) bcm2835_v4l2(C) v4l2_mem2mem cec bcm2835_mmal_vchiq(C) videobuf2_dma_contig snd_soc_core videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 snd_compress snd_pcm_dmaengine videobuf2_common snd_pcm videodev snd_timer mc snd raspberrypi_hwmon vc_sm_cma(C) uio_pdrv_genirq uio fixed [ 93.360563] CPU: 0 PID: 478 Comm: rmmod Tainted: P C O 5.4.72-v7 #1 [ 93.368060] Hardware name: BCM2835 [ 93.371516] PC is at kfree+0x48/0x2bc [ 93.375235] LR is at hcsr04_module_cleanup+0x18/0xcac [hcsr04] [ 93.381148] pc : [<802fd004>] lr : [<7f17a36c>] psr: 20010013 [ 93.387501] sp : b80abf08 ip : b80abf38 fp : b80abf34 [ 93.392797] r10: 00000081 r9 : b80aa000 r8 : 801011c4 [ 93.398095] r7 : 7f17a36c r6 : 7e92dc38 r5 : 7f17c000 r4 : bcf03570 [ 93.404713] r3 : bab24000 r2 : 00000024 r1 : 00000000 r0 : 7f17c000 [ 93.411333] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user [ 93.418568] Control: 10c5383d Table: 38d5806a DAC: 00000055 [ 93.424394] Process rmmod (pid: 478, stack limit = 0x274b04e4) [ 93.430307] Stack: (0xb80abf08 to 0xb80ac000) [ 93.434728] bf00: 801c8158 80989110 7f17c0c0 00000000 7e92dc38 00000081 [ 93.443026] bf20: 801011c4 b80aa000 b80abf4c b80abf38 7f17a36c 802fcfc8 7f17c0c0 00000000 [ 93.451324] bf40: b80abfa4 b80abf50 801ca940 7f17a360 72736368 00003430 00000000 80da6b10 [ 93.459621] bf60: 80101068 7e92cbe8 00000000 801011c4 b80aa000 00000006 b80abfac b80abf88 [ 93.467918] bf80: 80228294 8022938c 00000000 80e05f88 00000000 7e92de2c 00000000 b80abfa8 [ 93.476216] bfa0: 80101000 801ca7e0 00000000 7e92de2c 01677694 00000800 6a0f1b00 7e92dbe4 [ 93.484513] bfc0: 00000000 7e92de2c 7e92dc38 00000081 7e92df1f 7e92dc34 01677190 00000001 [ 93.492811] bfe0: 004e1f70 7e92dbec 004c866f 76f37218 60010030 01677694 00000000 00000000 [ 93.501100] Backtrace: [ 93.503593] [<802fcfbc>] (kfree) from [<7f17a36c>] (hcsr04_module_cleanup+0x18/0xcac [hcsr04]) [ 93.512331] r9:b80aa000 r8:801011c4 r7:00000081 r6:7e92dc38 r5:00000000 r4:7f17c0c0 [ 93.520195] [<7f17a354>] (hcsr04_module_cleanup [hcsr04]) from [<801ca940>] (sys_delete_module+0x16c/0x244) [ 93.530073] r5:00000000 r4:7f17c0c0 [ 93.533704] [<801ca7d4>] (sys_delete_module) from [<80101000>] (ret_fast_syscall+0x0/0x28) [ 93.542084] Exception stack(0xb80abfa8 to 0xb80abff0) [ 93.547209] bfa0: 00000000 7e92de2c 01677694 00000800 6a0f1b00 7e92dbe4 [ 93.555507] bfc0: 00000000 7e92de2c 7e92dc38 00000081 7e92df1f 7e92dc34 01677190 00000001 [ 93.563801] bfe0: 004e1f70 7e92dbec 004c866f 76f37218 [ 93.568922] r5:7e92de2c r4:00000000 [ 93.572549] Code: e3a02024 e5933000 e1a04624 e0243492 (e5943004) [ 93.578730] ---[ end trace cff8773499967501 ]---
问题分析与解决方法
核心问题
- 错误释放非动态分配内存:
pulseptr是指针数组,若为全局/静态数组,它本身并非kmalloc分配的内存,直接kfree(pulseptr)会触发内核页错误。 - 内存分配逻辑错误:
kmalloc(sizeof(buffer))仅分配了指针长度的内存(ARM平台为4字节),无法容纳用户传入的完整字符串;且直接用sprintf访问用户空间地址,会触发非法内存访问。
修正步骤
修正write函数的内存分配与数据拷贝:
使用copy_from_user安全拷贝用户空间数据,分配足够内存存储字符串,并检查内存分配结果:static char *pulseptr[5]; static int pulsecount = 0; ssize_t hcsr04_write(struct file *filp, const char *buffer, size_t length, loff_t * offset) { char *tmp_buf = kmalloc(length + 1, GFP_ATOMIC); if (!tmp_buf) { return -ENOMEM; } // 安全拷贝用户空间数据到内核 if (copy_from_user(tmp_buf, buffer, length)) { kfree(tmp_buf); return -EFAULT; } tmp_buf[length] = '\0'; // 确保字符串以结束符收尾 if (pulsecount < 5){ pulseptr[pulsecount] = tmp_buf; pulsecount++; } else{ int j = 0; // 替换前先释放旧内存,避免泄漏 while (j<4){ kfree(pulseptr[j]); pulseptr[j] = pulseptr[j+1]; j++; } kfree(pulseptr[4]); pulseptr[4] = tmp_buf; } return length; // 返回实际写入字节数,符合VFS规范 }修正cleanup函数的内存释放逻辑:
遍历指针数组,逐个释放动态分配的元素,而非数组本身:static void __exit hcsr04_module_cleanup(void) { int i; // 释放每个动态分配的内存块 for (i = 0; i < 5; i++) { if (pulseptr[i]) { kfree(pulseptr[i]); pulseptr[i] = NULL; } } printk(KERN_INFO "Dynamic memory freed successfully."); gpio_free( GPIO_OUT ); gpio_free( GPIO_IN ); hcsr04_lock = 0; cdev_del(&hcsr04_cdev); unregister_chrdev_region( hcsr04_dev, 1 ); kobject_put( hcsr04_kobject ); }补充初始化逻辑:
在模块初始化函数中,初始化指针数组元素为NULL:static int __init hcsr04_module_init(void) { int i; for (i = 0; i < 5; i++) { pulseptr[i] = NULL; } pulsecount = 0; // ...其他初始化代码 return 0; }
额外注意事项
- 内核态操作用户空间数据必须使用
copy_from_user/copy_to_user,禁止直接访问用户地址。 - 替换数组元素时,务必先释放旧内存,避免内存泄漏。
- VFS的write函数必须返回实际写入的字节数,否则用户态程序会判定写入失败。
内容的提问来源于stack exchange,提问作者user13024886
相关产品推荐
相关产品推荐

