copy_from_user等々とか
Rev.1を表示中。最新版はこちら。
copy_from_user等々の関数があります。ユーザプロセスからカーネルプロセスへの引数引渡しに使います。プロセスが違うから。と思っていましたが・・・
その答えはちゃんとLinuxカーネル解読室にかいてありました。解読室ではput_user_u64で説明されています
。要はマシン語レベルでエラー処理を関数自体に持たせるため。ということです。
ユーザランドから呼ばれるカーネルプロセスで、その引数がでたらめの場合、そのカーネルスレッド事態が
ストールしてしまうから・・・と思います。コトナルプロセス間での統合っしたエラー処理はできそうもな
い。と想像できます。
記述は、本文処理部+エラー処理部+エラーテーブルと言う構成で、エラー処理部は.fixup、エラーテーブ
ルは__ex_tableと宣言しておくようです。そうするとカーネルリンク時にそれらは合体されるわけで、たぶ
ん__ex_tableの先頭アドレスをcpuの特殊レジスターにでも設定するんじゃないでしょうか?
Linuxカーネル解読室はput_user_u64ですので、copy_form_userから呼び出される(copy_to_userからも呼び
出されている。)__copy_userを見てみようと思います。アセンブラですからアーキテクチャに依存していろ
んなファイルがありました。
#define __copy_user(to, from, size) \ do { \ int __d0, __d1, __d2; \ __asm__ __volatile__( \ " cmp $7,%0\n" \ " jbe 1f\n" \ " movl %1,%0\n" \ " negl %0\n" \ " andl $7,%0\n" \ " subl %0,%3\n" \ "4: rep; movsb\n" \ " movl %3,%0\n" \ " shrl $2,%0\n" \ " andl $3,%3\n" \ " .align 2,0x90\n" \ "0: rep; movsl\n" \ " movl %3,%0\n" \ "1: rep; movsb\n" \ "2:\n" \ ".section .fixup,\"ax\"\n" \ "5: addl %3,%0\n" \ " jmp 2b\n" \ "3: lea 0(%3,%0,4),%0\n" \ " jmp 2b\n" \ ".previous\n" \ ".section __ex_table,\"a\"\n" \ " .align 4\n" \ " .long 4b,5b\n" \ " .long 0b,3b\n" \ " .long 1b,2b\n" \ ".previous" \ : "=&c"(size), "=&D" (__d0), "=&S",(__d1), "=r"(__d2) \ : "3"(size), "0"(size), "1"(to), "2"(from) \ : "memory"); \ } while (0)
x86拡張インラインアセンブリっていうやつで「うん・・・」って感じです。でもラベルの4:,0:,1:のrep命
令のバッファー転送で、そこがエラー発生箇所となります。そこで、.section __ex_tableでこのエラー発生
が起こるかもしれないアドレスとそのジャンプ先のペアーでもってテーブルを作成していることが推測でき
ます。なんかサイズ値7で、rep処理を変えているようですが・・・。処理スピードあげるために、サイズ値
によってバイトオーダで転送させるか、ダブルワードオーダで転送させるかしているのかな?
copy_from_useがエラーハンドルの組み込みに起因するということで、プロセス空間の違いからというわけで
なさそうです。実際カーネルはユーザプロセスのmm_structを使います。ということは同じGDTで呼び出しユ
ーザプロセスのメモリー空間・・・。それならstrcpyでも動作するのでは?
#include <linux/kernel.h> #include <linux/sched.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #define PROC_NAME "hogehoge" static size_t proc_read( char* page, char** start, off_t offset, int count, int* eof, void* data
);
static struct proc_dir_entry *dirp; static int proc_init_module(void) { dirp = (struct proc_dir_entry *) create_proc_entry(PROC_NAME, 0666, (struct proc_dir_entry *) 0); if (dirp == 0) return(-EINVAL); dirp->read_proc = (write_proc_t *) proc_read; return 0; } static void proc_cleanup_module(void) { remove_proc_entry(PROC_NAME, (struct proc_dir_entry *) 0); } static size_t proc_write( struct file *filp, const char __user *buff, unsigned long len, void *data ) { char _buff[64];
strncpy(_buff, buff, len); _buff[len] = 0; printk("from user :%s\n", _buff); return len;:}
module_init(proc_init_module); module_exit(proc_cleanup_module);
ユーザテストプログラム
#include "stdio.h" main() { FILE* fp; char* buff="123456789";
fp = fopen("/proc/hogehoge", "w"); if (fp) { fwrite(buff, sizeof(char), strlen(buff), fp); fclose(fp); } } [root@localhost test]# ./a.out [root@localhost test]# dmesg 123456789strcpyでもちゃんと動作しているようです。
#include "stdio.h" main() { FILE* fp; char* buff[100];
fp = fopen("/proc/hogehoge", "r"); if (fp) { fgets(NULL, sizeof(buff), fp); printf("%s\n", buff); fclose(fp); } } [root@localhost test]# ./a.out セグメンテーション違反ですstrcpyを見てみました。これもいろいろあってその中の2つ程みてみました。ただ単にdesへsrcをコピーし
ているだけで、エラーハンドルに掛かる処理はありません。
char *strcpy(char *dest, const char *src) { char *tmp = dest;
while ((*dest++ = *src++) != '\0') /* nothing */; return tmp; } EXPORT_SYMBOL(strcpy);
char *strcpy(char *dest, const char *src) { int d0, d1, d2; asm volatile("1:\tlodsb\n\t" "stosb\n\t" "testb %%al,%%al\n\t" "jne 1b" : "=&S" (d0), "=&D" (d1), "=&a" (d2) :"0" (src), "1" (dest) : "memory"); return dest; } EXPORT_SYMBOL(strcpy);
strcpyはdes,srcのアドレスをそのまま参照して複写しています。ということは同じスタックと言えそうです。チェックの仕方がまずいのかもしれませんが、まあ、copy_from_usr等のいうものがなんとなく解ったということでよしとしました。