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);
}