kernel_read関数


vfs関連ファイル操作の関数を、LKMから使用する場合、そのbuf引数がユーザ空間である必要がある。処理の前にaccess_okマクロで、bufがカーネル空間ならエラーとするため。LKMからのファイル出力でlkmからの入出力で、do_mmap_pgoff()で、無理やりユーザ空間のバッファを確保し、それでもっての強引な処理。

カーネルを眺めていると、kernel_read()と言う関数にぶち当たった。カーネル空間のbufから、ファイルの読み込みを行うもので、カレントプロセスのDSセグメント値を、カーネルDSセグメントに差し替える事で実現する。
追記
うそでした。セグメントその物を差し替えるのではありませんでした。
#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;
}
ユーザ/カーネル空間のチェックは、current_thread_info()->addr_limit.segにメモリ空間サイズが設定されていて、そのサイズ値によって判断される。__range_not_okマクロは、インラインで詳細は分からないが、雰囲気チェックするaddr+sizeを、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 file.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]# 

補足

kernel_readと同様にkernel_writeというのがあった。読み込みが、書き込みに変わっただけで、処理は同じ。しかし、kernel_readはEXPORTとされていたが、kernel_writeはされていなかった。


最終更新 2012/07/28 17:32:21 - north
(2012/07/10 20:12:29 作成)


検索

アクセス数
3689218
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。