linux - 来自内核模块的调试堆栈

标签 linux memory linux-kernel stack

我编写内核模块来调试用户进程的堆栈。我找到了一种使用 task_struct 结构中的 mm 字段获取指向堆栈的指针的方法,但是当我尝试从堆栈地址读取值时,我的模块崩溃了。 代码(对于 pid 为 860 的现有进程):

  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/sched.h>
  #include <linux/pid.h>
  #include <linux/proc_fs.h>

  struct task_struct *ed_task;
  long int *val;
  void *stack_p;
  int i = 0;

  static int __init init(void)
  {
    ed_task = pid_task(find_vpid(860), PIDTYPE_PID);
    stack_p = (void *) ed_task->mm->start_stack;
    printk("stack %d: %p", i, stack_p);
    val = stack_p - sizeof(void *);
    printk("stack %d value: %ld", i, *val);
    printk(">");
    return 0;
  }

  static void __exit modex(void)
  {

  }
  module_init(init);
  module_exit(modex);
  MODULE_LICENSE("GPL");

错误:

[ 2714.296489] stack 0: 00007fffbd922e30
[ 2714.296495] BUG: unable to handle kernel paging request at 00007fffbd922e28
[ 2714.297844] IP: init+0x65/0x1000 [main3]
[ 2714.299017] PGD 10140d067 
[ 2714.299019] P4D 10140d067 
[ 2714.300188] PUD 0 

[ 2714.303364] Oops: 0000 [#4] PREEMPT SMP
[ 2714.306523] Modules linked in: main3(O+) main1(O+) main(O+) main2(O+) rndis_host cdc_ether usbnet mii fuse rfcomm bnep nls_iso8859_1 nls_cp437 vfat fat intel_rapl x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm hp_wmi irqbypass iTCO_wdt mousedev sparse_keymap iTCO_vendor_support ppdev joydev mei_wdt btusb crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel btrtl pcbc btbcm aesni_intel btintel bluetooth evdev input_leds aes_x86_64 uvcvideo crypto_simd glue_helper cryptd videobuf2_vmalloc intel_cstate intel_rapl_perf videobuf2_memops ecdh_generic pcspkr videobuf2_v4l2 videobuf2_core videodev media psmouse mac_hid hid_generic arc4 nouveau iwldvm mac80211 mxm_wmi iwlwifi ttm cfg80211 drm_kms_helper drm rfkill syscopyarea sysfillrect snd_hda_codec_hdmi sysimgblt snd_hda_codec_idt
[ 2714.312788]  snd_hda_codec_generic fb_sys_fops snd_hda_intel i2c_algo_bit snd_hda_codec snd_hda_core snd_hwdep snd_pcm snd_timer snd soundcore parport_pc thermal tpm_infineon hp_accel ac e1000e lis3lv02d parport wmi mei_me mei input_polldev battery video ptp pps_core button lpc_ich shpchp tpm_tis tpm_tis_core tpm sch_fq_codel vboxnetflt(O) vboxnetadp(O) pci_stub vboxpci(O) vboxdrv(O) sg ip_tables x_tables ext4 crc16 jbd2 fscrypto mbcache sr_mod cdrom sd_mod usbhid hid serio_raw atkbd libps2 ahci libahci libata scsi_mod firewire_ohci xhci_pci xhci_hcd sdhci_pci sdhci ehci_pci led_class ehci_hcd firewire_core mmc_core crc_itu_t usbcore usb_common i8042 serio [last unloaded: main]
[ 2714.318036] CPU: 3 PID: 6244 Comm: insmod Tainted: G      D    O    4.12.4-1-ARCH #1
[ 2714.319130] Hardware name: Hewlett-Packard HP EliteBook 8560w/1631, BIOS 68SVD Ver. F.60 03/12/2015
[ 2714.320246] task: ffff917ea5819c80 task.stack: ffff9f90455c8000
[ 2714.321406] RIP: 0010:init+0x65/0x1000 [main3]
[ 2714.322524] RSP: 0018:ffff9f90455cbc90 EFLAGS: 00010286
[ 2714.323684] RAX: 00007fffbd922e30 RBX: 0000000000000000 RCX: ffffffff8ba55a68
[ 2714.324813] RDX: 00007fffbd922e28 RSI: 0000000000000000 RDI: ffffffffc0f08031
[ 2714.325937] RBP: ffff9f90455cbc90 R08: 000000000000044b R09: ffffffff8bca0940
[ 2714.327040] R10: fffff2d0c2ecac40 R11: 0000000000000000 R12: ffffffffc0498000
[ 2714.328142] R13: ffff917e6ee4c480 R14: ffffffffc0f09050 R15: ffff917f06e24660
[ 2714.329279] FS:  00007f8999054b80(0000) GS:ffff917f3dcc0000(0000) knlGS:0000000000000000
[ 2714.330389] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2714.331516] CR2: 00007fffbd922e28 CR3: 000000010150f000 CR4: 00000000000406e0
[ 2714.332652] Call Trace:
[ 2714.333810]  do_one_initcall+0x50/0x190
[ 2714.334945]  ? do_init_module+0x27/0x1e6
[ 2714.336065]  do_init_module+0x5f/0x1e6
[ 2714.337197]  load_module+0x2610/0x2ab0
[ 2714.338349]  ? vfs_read+0x115/0x130
[ 2714.339469]  SYSC_finit_module+0xf6/0x110
[ 2714.340574]  ? SYSC_finit_module+0xf6/0x110
[ 2714.341675]  SyS_finit_module+0xe/0x10
[ 2714.342782]  entry_SYSCALL_64_fastpath+0x1a/0xa5
[ 2714.343899] RIP: 0033:0x7f8998765bb9
[ 2714.344988] RSP: 002b:00007ffc068a0528 EFLAGS: 00000206 ORIG_RAX: 0000000000000139
[ 2714.346111] RAX: ffffffffffffffda RBX: 00007f8998a26aa0 RCX: 00007f8998765bb9
[ 2714.347203] RDX: 0000000000000000 RSI: 000000000041aada RDI: 0000000000000003
[ 2714.348279] RBP: 00007f8998a26af8 R08: 0000000000000000 R09: 00007f8998a28e40
[ 2714.349368] R10: 0000000000000003 R11: 0000000000000206 R12: 0000000000001020
[ 2714.350441] R13: 0000000000001018 R14: 00007f8998a26af8 R15: 0000000000000001
[ 2714.351524] Code: 48 89 15 07 13 a7 00 e8 8d 11 cf ca 48 8b 05 fb 12 a7 00 8b 35 ed 12 a7 00 48 c7 c7 31 80 f0 c0 48 8d 50 f8 48 89 15 eb 12 a7 00 <48> 8b 50 f8 e8 65 11 cf ca 48 c7 c7 45 80 f0 c0 e8 59 11 cf ca 
[ 2714.353786] RIP: init+0x65/0x1000 [main3] RSP: ffff9f90455cbc90
[ 2714.354914] CR2: 00007fffbd922e28
[ 2714.356130] ---[ end trace a150fd8aba7bd1e3 ]---

为什么它不起作用?堆栈是否有任何特殊的内存保护或我做错了什么?

最佳答案

您不能直接从内核空间访问用户空间内存。为此,您需要使用内核提供的 API 函数 copy_from_user()。

修改您的代码以使用此 API 应该可行:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/pid.h>
#include <linux/proc_fs.h>

#include <linux/uaccess.h> // Header file corresponding to copy_from_user()

struct task_struct *ed_task;
long int *valp;       // Pointer to destination kernel memory
long int val;         // Destination kernel memory
void *stack_p;
int i = 0;

static int __init init(void)
{
  ed_task = pid_task(find_vpid(2298), PIDTYPE_PID);
  stack_p = (void *) ed_task->mm->start_stack;
  printk("stack %d: %p\n", i, stack_p);
  valp = stack_p - sizeof(void *);

  copy_from_user(&val, valp, sizeof(val));

  printk("stack %d value: %ld", i, val);
  printk(">");
  return 0;
}

static void __exit modex(void)
{

}
module_init(init);
module_exit(modex);
MODULE_LICENSE("GPL");

关于linux - 来自内核模块的调试堆栈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48266365/

相关文章:

linux - matlab脚本编辑

linux - zbus 连接未显示在busctl 列表中

linux - 读取txt文件并将值解析为bash脚本

c - Linux内核中不同类型的未对齐访问

c - char 指针的段错误

c - 在c中打破二维指针数组的一行,我做错了什么?

java - 将一个对象的值分配给另一个对象 - 它们不能在内存中指向相同的地址

R在时间序列距离计算期间内存不足

linux - 从 Linux 日志文件中学习

linux - 从 sk_buff 的网络 header 确定数据包是 ipv4 还是 ipv6