最近、カスタムLinuxカーネル(2.6.31.5、x86)ドライバーで、copy_to_userが定期的にバイトをユーザースペースにコピーしないという問題が発生しました。渡されたバイト数を返し、何もコピーしていないことを示します。コードを調べたところ、copy_to_userを呼び出しているときに、コードが割り込みを無効にしていて、コントラクトに違反していることがわかりました。これを修正した後、問題は発生しなくなりました。問題が発生する頻度は非常に低いため、割り込みを無効にすると問題が発生したことを証明する必要があります。
arch / x86 / lib /usercopy_32.crepから以下のコードスニペットを見ると; movslは、CXのカウントによって単語をユーザースペースにコピーします。サイズは終了時に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つのアイデアは次のとおりです。
- 割り込みが無効になっている場合、ページフォールトは発生せず、その後繰り返します。movs?何もせずにスキップされます。その場合、戻り値は、定義で指定され、観察された動作に応じて、CX、つまりユーザースペースにコピーされなかった量になります。
- ページフォールトは発生しますが、割り込みが無効になっているためLinuxはそれを処理できないため、ページフォールトハンドラーがこれをどのように行うかはわかりませんが、ページフォールトハンドラーは命令をスキップします。この場合も、CXは変更されずに残り、戻り値は正しくなります。
誰かが私にこの動作を指定するIntelマニュアルのセクションを指摘したり、役立つ可能性のある追加のLinuxソースを指摘したりできますか?