kernel_read関数
Rev.1を表示中。最新版はこちら。
vfs関連ファイル操作の関数を、LKMから使用する場合、そのbuf引数がユーザ空間である必要がある。処理の前にaccess_okマクロで、bufがユーザ空間ならエラーとするため。LKMからのファイル出力でlkmからの入出力で、do_mmap_pgoff()で、無理やりユーザ空間のバッファを確保し、それでもっての強引な処理。カーネルを眺めていると、kernel_read()と言う関数にぶち当たった。カーネル空間のbufから、ファイルの読み込みを行うもので、カレントプロセスのセグメント値を、カーネルセグメントに差し替える事で実現する。
#define get_fs() (current_thread_info()->addr_limit)
#define set_fs(x) (current_thread_info()->addr_limit = (x))
#define get_ds() (KERNEL_DS)
int kernel_read(struct file *file, loff_t offset,
char *addr, unsigned long count)
{
mm_segment_t old_fs;
loff_t pos = offset;
int result;
old_fs = get_fs();
set_fs(get_ds());
result = vfs_read(file, (void __user *)addr, count, &pos);
set_fs(old_fs);
return result;
}
ユザー/カーネル空間のチェックは、そのセグメント値によって判断される。__range_not_okマクロは、インラインで詳細は分からないが、雰囲気チェックとするセグメントを、current_thread_info()->addr_limit.segをそのチェックしていることが、なんとなく推測できる。
#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))
#define __range_not_ok(addr, size)
({
unsigned long flag, roksum;
__chk_user_ptr(addr);
asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"
: "=&r" (flag), "=r" (roksum)
: "1" (addr), "g" ((long)(size)),
"rm" (current_thread_info()->addr_limit.seg));
flag;
}
以下はLKMからのファイル出力の処理を、この手法にて書き直した物。
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/pagemap.h>
#include <linux/memcontrol.h>
#include <linux/module.h>
MODULE_DESCRIPTION("tty read/write");
MODULE_AUTHOR("y.kitamura");
MODULE_LICENSE("GPL");
static char* tty = "";
module_param(tty, charp, S_IRUGO);
static int tty_init(void)
{
struct file* file;
char buf[256], msg[256];
mm_segment_t old_fs;
ssize_t res;
if (tty == NULL) {
printk("insmod pre_rmmod.ko tty=/dev/ttydrv\n");
return -1;
}
file = filp_open(tty, O_RDWR, 0);
if (IS_ERR(file)) {
sprintf(msg, "err:%ld\n", PTR_ERR(file));
printk(msg);
return -1;
}
old_fs = get_fs();
set_fs(get_ds());
strcpy(msg, "[from lkm]# ");
res = vfs_write(file, (const char __user *)msg, strlen(msg), &file->f_pos);
memset(buf, 0, sizeof(buf));
res = vfs_read(file, (const char __user *)buf, sizeof(buf), &file->f_pos);
sprintf(msg, "input data in LKM is %s", buf);
res = vfs_write(file, (const char __user *)msg, strlen(msg), &file->f_pos);
set_fs(old_fs);
return -1;
}
module_init(tty_init);
ttyコマンドで、現在の端末を確認。そのデバイス名で上記モジュール(file.ko)をinsmodする。LKMからのプロンプトとして、[from lkm]#が表示され、何か入力すると、input data in LKM isとして、それが表示される。なお、最後のエラーメッセージは、rmmodする手間を省くため、tty_init()でreturn -1としているから。[root@localhost lkm]# tty /dev/pts/0 [root@localhost lkm]# insmod file.ko tty=/dev/pts/0 [from lkm]# abcdefg input data in LKM is abcdefg insmod: error inserting 'file.ko': -1 Operation not permitted [root@localhost lkm]#





