kernel crashing

I wrote a character driver
Code:
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>

d_open_t     open;
d_close_t    close;
d_read_t     read;
d_write_t    write;

static struct cdevsw char_dev = {
    .d_version =     D_VERSION,
    .d_open =        open,
    .d_close =       close,
    .d_read  =       read,
    .d_write =       write,
};

static char buf[512+1];
static size_t len;

int
open(struct cdev *dev, int flag, int otyp, struct thread *td)
{
    /* Initialize character buffer  */
    memset(&buf, '\0', 512 + 1 );
    len = 0;

    return (0);
}

int close(struct cdev *dev, int flag, int otyp, struct thread *td)
{
    return(0);
}

int
write(struct cdev *dev, struct uio *uio, int ioflag)
{
    int error = 0;

    error = copyinstr(uio->uio_iov->iov_base, &buf, 512, &len);

    if (error != 0)
        uprintf("Write to \"character device\" failed.\n" );

    return(error);
}

int
read(struct cdev *dev, struct uio *uio, int ioflag)
{
    int error = 0;

    if(len <= 0)
        error = -1;
    else
        copystr(&buf, uio->uio_iov->iov_base, 512+1, &len);

    return(error);
}

static struct cdev *sdev;

static int
load(struct module *module, int cmd, void *arg)
{
    int error = 0;

    switch (cmd){
    case MOD_LOAD:
        sdev = make_dev(&char_dev, 0, UID_ROOT, GID_WHEEL,
                        0600, "char_dev");
        uprintf("Character device loaded.\n");
        break;

    case MOD_UNLOAD:
        destroy_dev(sdev);
        uprintf("Character device unloaded.\n");
        break;

    default:
        error = EOPNOTSUPP;
        break;
    }
    return (error);
}
DEV_MODULE(char_dev, load, NULL);

and an interface to interact with it

Code:
#include <stdio.h>
#include <fcntl.h>
#include <paths.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

static char buf[512+1];

int
main(int argc, char *argv[])
{
    int kernel_fd;
    int len;

    if(argc != 2){
        printf("Usage:\n%s <string>\n", argv[0]);
        exit(0);
    }

    if((kernel_fd = open("/dev/char_dev", O_RDWR)) == -1){
        perror("/dev/char_dev");
        exit(1);
    }

    if((len = strlen(argv[1])+1) > 512){
        printf("ERROR: String is too long\n");
        exit(0);
    }

    if (write(kernel_fd, argv[1], len)  == -1)
        perror("write()");
    else

        printf("Wrote \"%s\" to device /dev/char_dev""\n", argv[1]);


    if(read(kernel_fd, buf, len) == -1)
        perror("read()");
    else
        printf("Read \"%s\" from device /dev/char_dev""\n", buf);


    if((close(kernel_fd)) == -1){
        perror("close()");
        exit(1);
    }

        close(kernel_fd);

    exit(0);
}

When I run ./test_my_cdev "Hello, Kernel"
the kernel crashes and the computer reboots.

the crash dump has this info :

Code:
Fatal trap 12: page fault while in kernel mode
cpuid = 1; apic id = 01
fault virtual address   = 0x601220
fault code              = supervisor write data, protection violation
instruction pointer     = 0x20:0xffffffff810722d1
stack pointer           = 0x28:0xfffffe004b268860
frame pointer           = 0x28:0xfffffe004b268860
code segment            = base 0x0, limit 0xfffff, type 0x1b
                        = DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags        = interrupt enabled, resume, IOPL = 0
current process         = 9976 (test_my_cdev)
trap number             = 12

Does anyone know why this is happening and/or how to solve it?
 
Without knowing even a little in FreeBSD kernel programming, my instinct says:

Maybe you made false assumption on what your write() function receives. You think you will have "Hello, Kernel\0" in the uio->uio_iov->iov_base buffer. But what if you get just the character 'H' without \0 after?

I just read this: https://www.freebsd.org/doc/en/books/arch-handbook/driverbasics-char.html
And you can see that the use of uio is more complex than you did. Also notice the uiomove() usage.
 
Without knowing even a little in FreeBSD kernel programming, my instinct says:

Maybe you made false assumption on what your write() function receives. You think you will have "Hello, Kernel\0" in the uio->uio_iov->iov_base buffer. But what if you get just the character 'H' without \0 after?

I just read this: https://www.freebsd.org/doc/en/books/arch-handbook/driverbasics-char.html
And you can see that the use of uio is more complex than you did. Also notice the uiomove() usage.
I ran that same code on a different machine and it worked as expected; however, i'm going through the kernel debugging section in handbook so eventually i'll find the culprit.

Thanks again.
 
Back
Top