2
votes

I'm trying to write a Linux kernel module. In that I've registered the open, read, write, release functions as below:

static struct file_operations fops =
{
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_release,
};

The driver creates a character device as /dev/hvacchar. This part seems to be working fine.

To test, I write to the driver as:

cat > /dev/hvacchar
asdf1234

Calling process is immediately Killed.

I've removed everything from my dev_write and left with just one printk(). Here is the code:

static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
   printk(KERN_INFO "HVACCHAR: Entered dev_write()\n");
   printk(KERN_INFO "HVACChar: b4 sprintf(): buffer=%c\n", buffer[0]);
   return len;
}

tail -f /var/log/kern.log shows the first printk statement, but not the second. I'm guessing it crashes when it tries to access buffer variable. As per my understanding, it is being supplied by the system and it should have a valid memory allocation. What am I doing wrong?

Here is the entire dump from kern.log

Aug 31 15:17:28 app-ThinkCentre-M900 kernel: [873016.888579] HVACChar: Device has been opened 1 time(s)
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.627170] HVACCHAR: Entered dev_write()
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.627203] BUG: unable to handle kernel paging request at 00000000015c9004
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.630465] IP: [<ffffffffc08f7070>] dev_write+0x20/0x40 [hvacchar]
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.633714] PGD 51364067 PUD 374d3067 PMD 374f6067 PTE 80000000ac7de867
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.637075] Oops: 0001 [#36] SMP
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.640346] Modules linked in: hvacchar(OE) binfmt_misc btrfs xor raid6_pq ufs qnx4 hfsplus hfs minix ntfs msdos jfs xfs libcrc32c uas usb_storage ebbchar(OE) rfcomm bnep intel_rapl x86_pkg_temp_thermal input_leds intel_powerclamp coretemp snd_hda_codec_hdmi snd_hda_codec_realtek snd_hda_codec_generic snd_hda_intel snd_hda_codec i915_bpo snd_hda_core snd_hwdep snd_pcm kvm_intel snd_seq_midi kvm snd_seq_midi_event snd_rawmidi snd_seq hci_uart irqbypass btbcm btqca snd_seq_device btintel snd_timer crct10dif_pclmul intel_ips drm_kms_helper bluetooth drm crc32_pclmul ghash_clmulni_intel aesni_intel snd aes_x86_64 lrw gf128mul mei_me glue_helper ablk_helper serio_raw cryptd i2c_algo_bit fb_sys_fops syscopyarea sysfillrect sysimgblt soundcore mei video acpi_pad 8250_fintek wmi intel_lpss_acpi intel_lpss pinctrl_sunrisepoint pinctrl_intel acpi_als kfifo_buf i2c_hid industrialio mac_hid parport_pc ppdev lp parport hid_generic usbhid hid e1000e psmouse ptp ahci pps_core libahci fjes [last unloaded: hvacchar]
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.660535] CPU: 1 PID: 31522 Comm: cat Tainted: G      D    OE   4.4.0-92-generic #115~14.04.1-Ubuntu
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.662854] Hardware name: LENOVO 10FLS13501/30D0, BIOS FWKT5AA   09/23/2016
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.664735] task: ffff88010759e900 ti: ffff8800374e0000 task.ti: ffff8800374e0000
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.666473] RIP: 0010:[<ffffffffc08f7070>]  [<ffffffffc08f7070>] dev_write+0x20/0x40 [hvacchar]
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.668233] RSP: 0018:ffff8800374e3ea8  EFLAGS: 00010282
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.669703] RAX: 000000000000001d RBX: 00000000015c9000 RCX: 0000000000000000
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.671127] RDX: 0000000000000001 RSI: ffff88023dc8dd98 RDI: ffff88023dc8dd98
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.672547] RBP: ffff8800374e3eb8 R08: 000000000000000a R09: 0000000000000000
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.673890] R10: 0000000000000000 R11: 00000000000009bc R12: 0000000000000009
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.675080] R13: 0000000000000009 R14: ffff8800374e3f20 R15: 0000000000000009
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.676237] FS:  00007f92c016d740(0000) GS:ffff88023dc80000(0000) knlGS:0000000000000000
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.677373] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.678484] CR2: 00000000015c9004 CR3: 000000008fbd5000 CR4: 00000000003406e0
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.679420] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.680331] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.681209] Stack:
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.682065]  ffff880139bf6300 00000000015c9000 ffff8800374e3ec8 ffffffff812010c8
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.682936]  ffff8800374e3f08 ffffffff812016e2 ffffffff8120161c ffff880139bf6300
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.683789]  ffff880139bf6300 00000000015c9000 0000000000000009 0000000000000009
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.684640] Call Trace:
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.685436]  [<ffffffff812010c8>] __vfs_write+0x18/0x40
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.686232]  [<ffffffff812016e2>] vfs_write+0xa2/0x1a0
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.687028]  [<ffffffff8120161c>] ? vfs_read+0x10c/0x130
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.687818]  [<ffffffff81202406>] SyS_write+0x46/0xa0
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.688604]  [<ffffffff8180cd36>] entry_SYSCALL_64_fastpath+0x16/0x75
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.689391] Code: c0 31 c0 5d c3 66 0f 1f 44 00 00 0f 1f 44 00 00 55 48 c7 c7 80 80 8f c0 31 c0 48 89 e5 41 54 49 89 d4 53 48 89 f3 e8 ff cb 88 c0 <0f> be 53 04 0f be 33 48 c7 c7 a8 80 8f c0 31 c0 e8 ea cb 88 c0
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.691080] RIP  [<ffffffffc08f7070>] dev_write+0x20/0x40 [hvacchar]
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.691905]  RSP <ffff8800374e3ea8>
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.692728] CR2: 00000000015c9004
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.693552] ---[ end trace eee0478c7200f93f ]---
Aug 31 15:17:31 app-ThinkCentre-M900 kernel: [873020.693962] HVACChar: Device successfully closed

I'm new to linux and device drivers. Please be gentle :)

1
first check where buffer is pointing to with printk("%p\n", (void*)buffer) - Serve Laurijssen
How is what you pass as buffer defined and initialised? - alk
@Brouwrijssen Here is the output for the printk you suggested HVACCHAR: buffer = 0000000002172408 alk, I'm not calling the dev_write function. It is being called by the kernel. Hence, I am assuming the kernel should ensure that proper memory is allocated to that parameter. - ameyazing
and what is the value of len? - Serve Laurijssen
printk("%zu\n", len); gives 5 in the log file, when tested as echo "asdf" > /dev/hvacchar - ameyazing

1 Answers

3
votes

Well, the reason is simple. Your code is executed in kernel context whilst the buffer with data comes from the userspace. One of the biggest goals of any kernel is to provide memory protection, so different memory regions are used for the kernel and the userspace. If you need to get some data from the userspace, copy it to the kernel memory using copy_from_user() function. If you don't follow the scheme and simply try to access the data directly, this will lead to a memory access violation, and the kernel will complain about it (like in your log output): "unable to handle kernel paging request".

So, a proper solution (based on your code) would look somewhat like this:

#include <linux/slab.h>

static ssize_t dev_write(struct file *filep, const char __user *buffer, size_t len, loff_t *offset) {
    char *buf_internal;

    buf_internal = kmalloc(len, GFP_KERNEL);
    if (buf_internal == NULL)
        return -ENOMEM;

    if (copy_from_user(buf_internal, buffer, len)) {
        kfree(buf_internal);
        return -EFAULT;
    }

    buf_internal[len - 1] = '\0';

    printk(KERN_INFO "HVACCHAR: Entered dev_write()\n");
    printk(KERN_INFO "HVACChar: b4 sprintf(): buffer=%c\n", buf_internal[0]);

    kfree(buf_internal);

    return len;
}

Please find the __user qualifier added to the function prototype in order to highlight the nature of the buffer. I didn't test the code, but I hope the idea as pretty clear :)