C program for passing kernel address to system call in Kernel space

        As you move code to the kernel, the needing to construct file names in a kernel buffer and then wanting to pass the address of this buffer to another system call.  This will fail because the system calls verify that any addresses passed to them are from user space.

        The following example is intended to show how kernel address space can be passed to a system call within the kernel itself.  Checks are made by many of the system calls to ensure that the passed address is really in user space.
Through the use of get_fs(), get_ds() and set_fs(), you can pass kernel addresses to system calls.

         In the following example, the system call foobar() is created.  This call is passed a value and a size.  The system call simply creates a file called "foobar" and writes the value to the file.  The key point of the example is the variable "buf", which is in kernel space, but is successfully passed to sys_open() after the address limit for the current process is extended to allow access to kernel addresses.  Remember that for Linux the kernel address space occupies the high addresses within the process address space.
Note, this is not a good idea in general and the routine resets the address limit after it is done.

/*****************************************/
/* /usr/src/linux/include/linux/foobar.h */
/*****************************************/
#ifndef __LINUX_FOOBAR_H
#define __LINUX_FOOBAR_H

#include <linux/unistd.h>
#include <linux/linkage.h>

_syscall2(int, foobar, char*, value, int, size);

#endif


/******************************/
/* /usr/src/linux/fs/foobar.c */
/******************************/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/foobar.h>
#include <asm/uaccess.h>

extern size_t sys_write(int, const void*, size_t);

asmlinkage int sys_foobar(char *value, int size)
{
    int fd;
    char buf[256];

    /* save addr_limit of the current process */
    mm_segment_t cur_fs = get_fs();

    sprintf(buf, "foobar");

    /* set addr_limit of the current process to that of kernel */
    set_fs(get_ds());
    fd = sys_open(buf, O_CREAT|O_WRONLY, 0644);
    /* set addr_limit of the current process back to its own */
    set_fs(cur_fs);

    if (fd < 0){
        printk("SYS_FOOBAR: error in opening foobar\n");
        return(-1);
    }

    /* set addr_limit of the current process to that of kernel */
    set_fs(get_ds());
    sys_write(fd, value, size);
    /* set addr_limit of the current process back to its own */
    set_fs(cur_fs);

    sys_close(fd);

    return(0);

}