SignoEleven
SignoEleven

Reputation: 21

IOCTL locking kernel on Xeon E3-1270 v5

Has anyone experienced an issue with a system lock up when using IOWR with IOCTL on an Intel Xeon E3-1270 CPU? I have 3 different distributions (Centos 7.2, Ubuntu 14.04, Ubuntu 16.04) on 3 different Dell boxes using the above CPU and every time I use IOCTL with an IOWR the system locks up as soon as I access the memory sent up in the arg variable of the ioctl call.

Here's is an example code:

Kernel Module Header:

#include <linux/ioctl.h>

#define IOC_MAGIC 'k'

typedef struct
{
    int test1;
    int test2;
} bufferTest;

#define IOCTL_HELLO _IO(IOC_MAGIC,0)
#define IOCTL_BUFFER_TEST  _IOWR(IOC_MAGIC,1, bufferTest)

Kernel Module Source:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h> // required for various structures related to files liked fops.
#include <linux/semaphore.h>
#include <linux/cdev.h>
#include "ioctl_basic.h"    //ioctl header file
#include <linux/version.h>
#include <asm/uaccess.h>

int open(struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "Inside open \n");
    return 0;
}

int release(struct inode *inode, struct file *filp)
{
    printk (KERN_INFO "Inside close \n");
     return 0;
}

long ioctl_funcs(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret=0;
    int index=0;
    bufferTest *bufferPtr = NULL;
    void *userPtr = NULL;

    printk(KERN_INFO "ioctl_func: %u\n", cmd);

    switch(cmd) {

    case IOCTL_HELLO:
        printk(KERN_INFO "Hello ioctl world\n");
        ret = 1;
        break;
    case IOCTL_BUFFER_TEST:
        bufferPtr = (bufferTest *)arg;
        userPtr = (void *)arg;

        if (access_ok(VERIFY_WRITE, userPtr, sizeof(bufferTest)))
        {
            printk(KERN_INFO "Success verify memory write\n");
            printk(KERN_INFO "test1: %d, test2: %d\n", bufferPtr->test1, bufferPtr->test2);
        }
        else
            printk(KERN_INFO "Failed to verify memory write\n");
        break;
    default:
        printk(KERN_INFO "Unknown command: %d\n", cmd);
        break;
     }

    return ret;
}

struct file_operations fops = {
 .open =   open,
 .unlocked_ioctl =  ioctl_funcs,
 .release = release
};


struct cdev *kernel_cdev;


int char_arr_init (void) {
    int ret;
    dev_t dev_no,dev;

    kernel_cdev = cdev_alloc();
    kernel_cdev->ops = &fops;
    kernel_cdev->owner = THIS_MODULE;
    printk (" Inside init module\n");
    ret = alloc_chrdev_region( &dev_no , 0, 1,"char_arr_dev");
    if (ret < 0) {
        printk("Major number allocation is failed\n");
    return ret;
    }

    Major = MAJOR(dev_no);
    dev = MKDEV(Major,0);

    printk (" The major number for your device is %d\n", Major);
    ret = cdev_add( kernel_cdev,dev,1);
    if(ret < 0 )
    {
        printk(KERN_INFO "Unable to allocate cdev");
        return ret;
    }

    return 0;
}

void char_arr_cleanup(void) {
    printk(KERN_INFO " Inside cleanup_module\n");
    cdev_del(kernel_cdev);
    unregister_chrdev_region(Major, 1);
}

MODULE_LICENSE("GPL");
module_init(char_arr_init);
module_exit(char_arr_cleanup);

Test Code:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include "ioctl_basic.h"  //ioctl header file

main ( ) {
    int fd;
    bufferTest buffer;

    fd = open("/dev/temp", O_RDWR);

    if (fd == -1)
    {
        printf("Error in opening file \n");
        return;
    }

    ioctl(fd,IOCTL_HELLO);  //ioctl call

    buffer.test1 = 5;
    buffer.test2 = 10;

    printf("buffer ptr: %p, test1: %d, test2: %d\n", &buffer, buffer.test1, buffer.test2);

    ioctl(fd,IOCTL_BUFFER_TEST,&buffer);

    close(fd);
}

In the kernel module I cast (bufferTest *) to the arg parameter, as soon as I try to print "test1" or "test2" the system will lock up. This does not happen on any of my other systems not using this particular CPU.

If I use the copy_to_user and copy_from_user to copy the arg parameter to a local structure in the kernel module then things work fine, but I thought using the IOWR define I can access the memory directly. It seems like the Xeon E3-1270 processor is having some kind of issue, and I can't figure out what it is. Has anyone else experienced this issue?

Upvotes: 2

Views: 316

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 65938

If I use the copy_to_user and copy_from_user to copy the arg parameter to a local structure in the kernel module then things work fine, but I thought using the IOWR define I can access the memory directly.

No, irrespectively to the type of ioctl request you need to access user space memory in a common way. Aside from copy_from_user there is __get_user macro for read simple types from user space:

    if (access_ok(VERIFY_WRITE, userPtr, sizeof(bufferTest)))
    {
        int test1, test2;
        printk(KERN_INFO "Success verify memory write\n");
        __get_user(test1, &buffer->test1); // Read value of 'buffer->test1' into test1 
        __get_user(test2, &buffer->test2); // Read value of 'buffer->test2' into test2
        printk(KERN_INFO "test1: %d, test2: %d\n", test1, test2);
    }

Linux kernel doesn't aware of ioctl types and their meaning. Their usage is just a convention.

Upvotes: 2

Related Questions