2012-09-26 9 views
15

私は最近、カスタムLinuxカーネル(2.6.31.5、x86)ドライバで、copy_to_userが周期的にユーザー領域にバイトをコピーしないという問題が発生しました。渡されたバイト数を返します。何もコピーしていないことを示します。コード検査の後、私たちはコードが割り込みを無効にしているのに対し、copy_to_userはその契約に違反しています。これを修正した後、問題は停止しました。問題はあまり頻繁に起こっていないので、割り込みを無効にすると問題が発生したことを証明する必要があります。mov命令でx86で割り込みが無効になっているページフォルトが発生するとどうなりますか?

以下のコードスニペットをarch/x86/lib/usercopy_32.c repから見てください。 movslは単語をCXのカウントでuserspaceにコピーします。終了時にサイズがCXで更新されます。 movslが正しく実行されると、CXは0になります。 CXはゼロではないので、movs? copy_to_userの定義と観察された動作に適合させるために、命令を実行してはいけません。私がしているしている

/* Generic arbitrary sized copy. */ 
#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) 

2つのアイデア:

  1. 割り込みが無効になっている場合は、ページフォルトが発生し、その後 担当しません。 movs?何もせずにスキップされます。戻り値 は、 定義が指定したとおり、ユーザー・スペースにコピーされなかった量、および観察された動作がCXになります。
  2. ページフォールトは発生しますが、割り込みが無効になっているためLinuxが処理できないため、ページフォールトハンドラはどのようにページフォールトハンドラがこれを行うのか分かりませんが、ページフォルトハンドラは命令をスキップします。この場合も、この場合CXは変更されずに戻り値が正しいことになります。

この動作を指定するIntelのマニュアルのセクションを指摘できますか、または参考にできる追加のLinuxソースを教えてください。

+0

あなたは "コードが割り込みを無効にしていた"と言います。あなたはどんな割り込みとどのように詳しく説明できますか?... – TheCodeArtist

+0

@TheCodeArtist:write_lock_bh();私の理解によってソフトウェア割り込みを無効にする。 – Edward

+0

@TheCodeArtist:ありがとう!あなたのコメントはwrite_lock_bh()をもっと詳しく見て、私に方法を示しました! – Edward

答えて

7

を参照してください。私の#2の提案は正しかったし、メカニズムは私の顔の前にあった。ページフォールトは発生しますが、fixup_exceptionメカニズムは例外/継続メカニズムを提供するために使用されます。このセクションでは、例外ハンドラテーブルにエントリを追加します。

".section __ex_table,\"a\"\n"    \ 
    " .align 4\n"     \ 
    " .long 4b,5b\n"     \ 
    " .long 0b,3b\n"     \ 
    " .long 1b,6b\n"     \ 
    ".previous"      \ 

これは言う:IPアドレスが最初のエントリで、例外がフォルトハンドラに遭遇した場合は、2番目のアドレスにIPアドレスを設定して続行します。

「4:」で例外が発生した場合は、「5:」にジャンプします。 "0:"で例外が発生した場合は "3:"にジャンプし、 "1:"で例外が発生した場合は "6:"にジャンプします。

欠落している部分がアーチ/ x86の本/ mm/fault.c中)(do_page_faultである:我々はwrite_lock_bh()ロックであるため

/* 
* If we're in an interrupt, have no user context or are running 
* in an atomic region then we must not take the fault: 
*/ 
if (unlikely(in_atomic() || !mm)) { 
    bad_area_nosemaphore(regs, error_code, address); 
    return; 
} 

in_atomicはtrueを返し! bad_area_nosemaphoreは最終的にフィックスアップを行います。

作業領域の概念のためにpage_faultが発生する可能性が低い場合、関数呼び出しは失敗し、プリエンプションが無効になっているため、コピーされていないバイトをsizeに設定して__copy_userマクロから飛び出します。

4

ページフォールトはマスク可能な割り込みではありません。実際には、技術的には割り込みではなく、むしろ例外ではありますが、違いはより意味があると私は同意します。

割り込みが無効なアトミック・コンテキストでcopy_to_userがコールされたときに、そのコードが明示的にチェックされているためです。

は、私は答えを見つけたhttp://lxr.free-electrons.com/source/arch/x86/lib/usercopy_32.c#L575

+0

あなたの答えをありがとう。その呼び出しはほとんどの場合動作しました。非常にまれにしか失敗しませんでした。それが原子的な文脈のせいであったなら、私はそれがいつも失敗すると思います。とにかくペンティアムでその状態を実行すべきではありません。 、[Linusによると](http://answers.softpicks.net/answers/topic/-BUG-__copy_to_user_inatomic-broken-on-non-Pentium-machines-2056019-1.htm)boot_cpu_data.wp_works_okは== 0にする必要がありますすべてが386より大きい – Edward

関連する問題