Reputation: 21
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
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