IOCTL在Xeon E3-1270 v5上锁定内核

时间:2016-07-26 14:50:27

标签: c linux linux-kernel linux-device-driver

在Intel Xeon E3-1270 CPU上使用带IOCTL的IOWR时,有没有人遇到系统锁定问题?我在使用上述CPU的3个不同的戴尔盒子上有3个不同的发行版(Centos 7.2,Ubuntu 14.04,Ubuntu 16.04),每次我使用带有IOWR的IOCTL时,只要我访问arg变量中发送的内存,系统就会锁定ioctl电话。

这是一个示例代码:

内核模块标题:

#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)

内核模块来源:

#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);

测试代码:

#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);
}

在内核模块中,我将(bufferTest *)转换为arg参数,一旦我尝试打印“test1”或“test2”,系统就会锁定。在我没有使用此特定CPU的任何其他系统上都不会发生这种情况。

如果我使用copy_to_user和copy_from_user将arg参数复制到内核模块中的本地结构,那么一切正常,但我认为使用IOWR定义我可以直接访问内存。看起来Xeon E3-1270处理器出现了某种问题,我无法弄清楚它是什么。还有其他人遇到过这个问题吗?

1 个答案:

答案 0 :(得分:2)

  

如果我使用copy_to_user和copy_from_user将arg参数复制到内核模块中的本地结构,那么一切正常,但我认为使用IOWR定义我可以直接访问内存。

不,与ioctl请求的类型无关,您需要以常见方式访问用户空间内存。除copy_from_user之外,还有__get_user宏用于从用户空间读取简单类型:

    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内核不了解ioctl类型及其含义。它们的用法只是一种约定。