1 year ago

#342595

test-img

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

Accepted video resources