1 year ago
#342595
Garrett Chestnut
Unable to write struct to user from kernel with custom module
I am currently extending a character device driver (as a kernel module), and I'm attempting to add a new IOCTL command. All the command should do is print out the contents of a struct containing information about the task currently running in user space. As you can see in the top level scull.c
file, I attempt to take some of the fields from the kernel's current
macro, and place it into a custom struct called TASKSTRUCT
(this is the alias, the original name is taskstruct
). The output should resemble the following when running ./scull i
from the src
directory:
Device (/dev/scull) opened
Return value: 0
state 0, stack 0xffffc90002200000, cpu 1, prio 120, sprio 120, nprio 120,
rtprio 0, pid 47924, tgid 47924, nv 2, niv 5
Device (/dev/scull) closed
This is the current output, obviously indicating that something has gone wrong:
Device (/dev/scull) opened
Return value: -1
Device (/dev/scull) closed
When I run GDB, I find no evidence of a segmentation fault, just the above output. Below, I have included the files necessary to reproduce this problem.
/driver/scull.c
/*
* main.c -- the bare scull char module
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/cdev.h>
#include <linux/uaccess.h> /* copy_*_user */
#include <linux/sched.h>
#include <asm/current.h>
#include "scull.h" /* local definitions */
#include "access_ok_version.h"
#include "taskstruct.h"
/*
* Our parameters which can be set at load time.
*/
static int scull_major = SCULL_MAJOR;
static int scull_minor = 0;
static int scull_quantum = SCULL_QUANTUM;
module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
MODULE_AUTHOR("Author");
MODULE_LICENSE("Dual BSD/GPL");
static struct cdev scull_cdev; /* Char device structure */
typedef struct TASKSTRUCT TASKSTRUCT;
/*
* Open and close
*/
static int scull_open(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "scull open\n");
return 0; /* success */
}
static int scull_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "scull close\n");
return 0;
}
/*
* The ioctl() implementation
*/
static long scull_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int err = 0, tmp;
int retval = 0;
TASKSTRUCT info;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok_wrapper(VERIFY_WRITE, (void __user *)arg,
_IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok_wrapper(VERIFY_READ, (void __user *)arg,
_IOC_SIZE(cmd));
if (err) return -EFAULT;
switch(cmd) {
case SCULL_IOCIQUANTUM:
info.state = current->state;
info.stack = current->stack;
info.cpu = current->nr_cpus_allowed;
info.prio = current->prio;
info.static_prio = current->static_prio;
info.normal_prio = current->normal_prio;
info.rt_priority = current->rt_priority;
info.pid = current->pid;
info.tgid = current->tgid;
info.nvcsw = current->nvcsw;
info.nivcsw = current->nivcsw;
retval = copy_to_user((int __user *)arg,&info,sizeof(info));
//printk(KERN_INFO "copy_to_user: %d\n",retval);
if(retval == 0) {
printk(KERN_INFO "copy_to_user worked!");
}}
printk(KERN_INFO "size of info: %ld\n",sizeof(info));
break;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return retval;
}
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
/*
* Finally, the module stuff
*/
/*
* The cleanup function is used to handle initialization failures as well.
* Thefore, it must be careful to work correctly even if some of the items
* have not been initialized
*/
void scull_cleanup_module(void)
{
dev_t devno = MKDEV(scull_major, scull_minor);
/* Get rid of the char dev entry */
cdev_del(&scull_cdev);
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, 1);
}
int scull_init_module(void)
{
int result;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, 1, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, 1, "scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
cdev_init(&scull_cdev, &scull_fops);
scull_cdev.owner = THIS_MODULE;
result = cdev_add (&scull_cdev, dev, 1);
/* Fail gracefully if need be */
if (result) {
printk(KERN_NOTICE "Error %d adding scull character device", result);
goto fail;
}
return 0; /* succeed */
fail:
scull_cleanup_module();
return result;
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
/driver/scull.h
/*
* scull.h -- definitions for the char module
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
* $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $
*/
#ifndef _SCULL_H_
#define _SCULL_H_
#include <linux/ioctl.h> /* needed for the _IOW etc stuff used later */
#include "taskstruct.h"
#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0 /* dynamic major by default */
#endif
/*
* SCULL_QUANTUM
*/
#ifndef SCULL_QUANTUM
#define SCULL_QUANTUM 4000
#endif
/*
* Ioctl definitions
*/
/* Use 'k' as magic number */
#define SCULL_IOC_MAGIC 'k'
#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)
/*
* I (new) means "Info": returns task_struct of task currently running in user space
*/
#define SCULL_IOCIQUANTUM _IOR(SCULL_IOC_MAGIC, 1, struct TASKSTRUCT)
/* ... more to come */
#define SCULL_IOC_MAXNR 1
#endif /* _SCULL_H_ */
/driver/Makefile (driver
is the folder containing the code for the module)
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O0 -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif
EXTRA_CFLAGS += $(DEBFLAGS)
ifneq ($(KERNELRELEASE),)
# call from kernel build system
obj-m := scull.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers
depend .depend dep:
$(CC) $(EXTRA_CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif
/driver/taskstruct.h
#ifndef TASKSTRUCT_H
#define TASKSTRUCT_H
typedef struct TASKSTRUCT {
long state;
void *stack;
unsigned int cpu;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
pid_t pid;
pid_t tgid;
unsigned long nvcsw;
unsigned long nivcsw;
};
#endif
/src/scull.c (src
contains the code to test the module)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "scull.h"
#include "taskstruct.h"
#define CDEV_NAME "/dev/scull"
/* Quantum command line option */
static int g_quantum;
static void usage(const char *cmd)
{
printf("Usage");
}
typedef int cmd_t;
static cmd_t parse_arguments(int argc, const char **argv)
{
cmd_t cmd;
if (argc < 2) {
fprintf(stderr, "%s: Invalid number of arguments\n", argv[0]);
cmd = -1;
goto ret;
}
/* Parse command and optional int argument */
cmd = argv[1][0];
switch (cmd) {
case 'i':
break;
default:
fprintf(stderr, "%s: Invalid command\n", argv[0]);
cmd = -1;
}
ret:
if (cmd < 0 || cmd == 'h') {
usage(argv[0]);
exit((cmd == 'h')? EXIT_SUCCESS : EXIT_FAILURE);
}
return cmd;
}
static int do_op(int fd, cmd_t cmd)
{
int ret, q;
char *s;
//task_struct info;
switch (cmd) {
case 'i':
ret = ioctl(fd, SCULL_IOCIQUANTUM, &s);
printf("Return value: %d\n",ret);
if(ret == 0)
printf("%s\n",s);
ret = 0;
break;
default:
/* Should never occur */
abort();
ret = -1; /* Keep the compiler happy */
}
if (ret != 0)
perror("ioctl");
return ret;
}
int main(int argc, const char **argv)
{
int fd, ret;
cmd_t cmd;
cmd = parse_arguments(argc, argv);
fd = open(CDEV_NAME, O_RDONLY);
if (fd < 0) {
perror("cdev open");
return EXIT_FAILURE;
}
printf("Device (%s) opened\n", CDEV_NAME);
ret = do_op(fd, cmd);
if (close(fd) != 0) {
perror("cdev close");
return EXIT_FAILURE;
}
printf("Device (%s) closed\n", CDEV_NAME);
return (ret != 0)? EXIT_FAILURE : EXIT_SUCCESS;
}
/src/Makefile
CFLAGS=-O2 -Wall -I../driver
TGT=scull
.PHONY: clean
all: $(TGT)
clean:
rm -f *.o $(TGT)
(src
also contains a copy of taskstruct.h
)
Any help would be greatly appreciated.
c
linux-kernel
linux-device-driver
ioctl
0 Answers
Your Answer