Nagavi Bindzuriani Nagavi Bindzuriani - 1 month ago 16
Linux Question

Linux kernel module - opening created char device results in a bug

I've just started reading Ldd3 and tried to implement a simple module, more or less following the examples in the book. Module adds char device with custom fops (empty functions) to the kernel. Module loads without a problem, but when I try to open the device, process gets killed and dmesg prints out debug info. My kernel is 4.8.0. x86-64. Here's my code

#include <linux/init.h>
#include <linux/module.h>

#include <linux/types.h> // for dev_t
#include <linux/kdev_t.h> // for MAJOR, MINOR macros etc..
#include <linux/fs.h> // for alloc_chrdev_region etc ..
#include <linux/cdev.h> // to register char devic

MODULE_LICENSE("Dual BSD/GPL");

dev_t hello_num;
struct cdev *my_cdev;

loff_t hello_llseek(struct file *filepo, loff_t offset, int a){
return 0;
}

int hello_open(struct inode *inode, struct file *flip){
printk(KERN_ALERT "you opened this device");
return 0;
}

ssize_t hello_read(struct file *filep, char __user *ptr, size_t sizee, loff_t *gela){
return 0;
}

ssize_t hello_write(struct file *filep, const char __user *ptr, size_t sizee, loff_t *gela){
return 0;
}

int hello_release(struct inode *inode, struct file *filep){
return 0;
}

static int hello_init(void)
{
int cdev_add_return = 999; //DEBUG
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.llseek = hello_llseek,
.read = hello_read,
.write = hello_write,
.open = hello_open,
.release = hello_release,
};

alloc_chrdev_region(&hello_num, 0, 3, "hello");
my_cdev = cdev_alloc();
if(my_cdev == 0){
printk(KERN_DEBUG "my_cdev is NULL");
}

printk(KERN_DEBUG "mc_cdev address: %p", my_cdev); // DEBUG

my_cdev->ops = &hello_fops;

if((cdev_add_return = cdev_add(my_cdev, hello_num, 3)) != 0){
printk(KERN_DEBUG "cdev_add errrored \n");
}
printk(KERN_DEBUG "cdev_add returned: %d", cdev_add_return); // DEBUG

printk(KERN_INFO "Hello World: major num: %ld \n", (long) MAJOR(hello_num));
return 0;
}

static void hello_exit(void)
{
cdev_del(my_cdev);
unregister_chrdev_region(hello_num, 3);
printk(KERN_INFO "Goodbye\n");
}

module_init(hello_init);
module_exit(hello_exit);


Here's the debug info:

[ 3504.756954] mc_cdev address: ffff8800340ed280
[ 3504.756967] cdev_add returned: 0
[ 3504.756975] Hello World: major num: 245
[ 3512.356621] BUG: unable to handle kernel paging request at ffffffff810b3d1d
[ 3512.359676] IP: [<ffffffff811029f1>] try_module_get+0x41/0x110
[ 3512.359676] PGD 1809067 PUD 180a063 PMD 10001e1
[ 3512.359676] Oops: 0003 [#2] PREEMPT SMP
[ 3512.359676] Modules linked in: hello(O) joydev mousedev arc4 ath9k ath9k_common ath9k_hw ath snd_hda_codec_realtek input_leds psmouse mac_hid uvcvideo mac80211 snd_hda_codec_generic evdev serio_raw iTCO_wdt atkbd iTCO_vendor_support libps2 videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_core asus_wmi snd_hda_intel videodev snd_hda_codec media i915 coretemp lpc_ich i2c_i801 pcspkr i2c_smbus i8042 drm_kms_helper atl1c cfg80211 snd_hda_core drm sparse_keymap wmi led_class rfkill serio snd_hwdep snd_pcm syscopyarea thermal sysfillrect snd_timer fjes sysimgblt ac fb_sys_fops shpchp snd intel_agp intel_gtt battery i2c_algo_bit soundcore video button acpi_cpufreq tpm_tis tpm_tis_core tpm squashfs loop sch_fq_codel ip_tables x_tables ext4 crc16 jbd2 mbcache uas usb_storage sd_mod uhci_hcd ahci
[ 3512.359676] libahci libata scsi_mod ehci_pci ehci_hcd usbcore usb_common [last unloaded: hello]
[ 3512.359676] CPU: 1 PID: 2438 Comm: checkout Tainted: G R D O 4.8.0-ARCH #1
[ 3512.359676] Hardware name: ASUSTeK Computer INC. 1005P/1005P, BIOS 0706 01/04/2010
[ 3512.359676] task: ffff880034035b00 task.stack: ffff8800341a8000
[ 3512.359676] RIP: 0010:[<ffffffff811029f1>] [<ffffffff811029f1>] try_module_get+0x41/0x110
[ 3512.359676] RSP: 0018:ffff8800341abc30 EFLAGS: 00010206
[ 3512.359676] RAX: 0000000048000009 RBX: ffff8800340ed280 RCX: 0000000048000009
[ 3512.359676] RDX: 000000004800000a RSI: ffffffff810b3d1d RDI: ffffffff810b39e5
[ 3512.359676] RBP: ffff8800341abc48 R08: 0000000000000000 R09: 0000000000000000
[ 3512.359676] R10: 0000000000000000 R11: ffff88002d65df38 R12: ffff880034064320
[ 3512.469705] R13: ffff88003834f600 R14: ffff8800340ed280 R15: 0000000000000000
[ 3512.469705] FS: 00007f5bd10a9400(0000) GS:ffff88003f500000(0000) knlGS:0000000000000000
[ 3512.469705] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 3512.469705] CR2: ffffffff810b3d1d CR3: 000000003d315000 CR4: 00000000000006e0
[ 3512.469705] Stack:
[ 3512.469705] ffff8800340ed280 ffff880034064320 ffff88003834f600 ffff8800341abc98
[ 3512.469705] ffffffff8120c99d ffff880034064320 0000000000008000 0000000064a8303a
[ 3512.469705] ffff88003834f600 ffff8800341afcc0 ffff880034064320 0000000000000000
[ 3512.469705] Call Trace:
[ 3512.469705] [<ffffffff8120c99d>] chrdev_open+0x7d/0x1e0
[ 3512.469705] [<ffffffff812052c5>] do_dentry_open+0x205/0x2e0
[ 3512.469705] [<ffffffff8120c920>] ? cdev_put+0x30/0x30
[ 3512.469705] [<ffffffff8120637c>] vfs_open+0x4c/0x70
[ 3512.469705] [<ffffffff81213fbb>] ? may_open+0x9b/0x100
[ 3512.469705] [<ffffffff81216332>] path_openat+0x282/0x1170
[ 3512.469705] [<ffffffff811f32b9>] ? unlock_page_memcg+0x29/0x60
[ 3512.469705] [<ffffffff811bdbcb>] ? page_add_file_rmap+0x5b/0x140
[ 3512.469705] [<ffffffff81176de3>] ? filemap_map_pages+0x233/0x410
[ 3512.469705] [<ffffffff812183f1>] do_filp_open+0x91/0x100
[ 3512.469705] [<ffffffff81226129>] ? __alloc_fd+0xc9/0x180
[ 3512.469705] [<ffffffff812067a7>] do_sys_open+0x147/0x210
[ 3512.469705] [<ffffffff8120688e>] SyS_open+0x1e/0x20
[ 3512.469705] [<ffffffff815f7072>] entry_SYSCALL_64_fastpath+0x1a/0xa4
[ 3512.469705] Code: 53 65 ff 05 c2 a8 f0 7e 83 3f 02 0f 84 bd 00 00 00 8b 8f 38 03 00 00 85 c9 0f 84 af 00 00 00 8d 51 01 48 8d b7 38 03 00 00 89 c8 <f0> 0f b1 97 38 03 00 00 39 c8 89 c2 75 7e 4c 8b 6d 08 0f 1f 44
[ 3512.469705] RIP [<ffffffff811029f1>] try_module_get+0x41/0x110
[ 3512.469705] RSP <ffff8800341abc30>
[ 3512.469705] CR2: ffffffff810b3d1d
[ 3512.469705] ---[ end trace c8094481edecca95 ]---


Makefile:

obj-m += hello.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


I create device node manually. Can anyone point where the problem is ?
thanks in advance

Answer

Your struct with file operations is declared locally to the function. So, when hello_init is finished, pointer to that structure is no longer valid.

When you open the character device, kernel tries to look into that structure, but it is not accessible.

Common practics is declaring structure with file operations statically:

static struct file_operations hello_fops = {...};
Comments