web-dev-qa-db-ja.com

Linuxカーネルのcopy_from_userは内部でどのように機能しますか?

copy_from_user()関数は内部でどの程度正確に機能しますか?カーネルがユーザーのメモリスペースにアクセスする特権を持っているという事実を考慮して、バッファを使用しますか、それともメモリマッピングが行われますか?

20
Santi1986

copy_from_user()の実装は、アーキテクチャに大きく依存しています。

X86およびx86-64では、SMAP(Supervisor Mode Access Prevention)が構成されている場合は一時的に無効にしながら、ユーザースペースアドレスから直接読み取り、カーネルスペースアドレスに書き込むだけです。トリッキーな部分は、copy_from_user()コードが特別な領域に配置され、ページフォールトハンドラーがその中でフォールトが発生したことを認識できるようにすることです。 copy_from_user()で発生するメモリ保護障害は、他のプロセスコンテキストコードによってトリガーされた場合のようにプロセスを強制終了したり、割り込みコンテキストで発生した場合のようにカーネルをパニックにしたりしません。 -EFAULTを呼び出し元に返すコードパスで実行を再開するだけです。

22
caf

「カーネルがカーネル空間アドレスを渡しているので、copy_to_userはどのように動作するのか、ユーザー空間プロセスはどのようにそれにアクセスできるのか」について

ユーザースペースプロセスは、任意のアドレスへのアクセスを試みることができます。ただし、アドレスがそのプロセスのユーザースペース(つまり、そのプロセスのページテーブル)にマップされていない場合、または読み取り専用の場所への書き込み試行などのアクセスに問題がある場合は、ページフォールトが生成されます。少なくともx86では、すべてのプロセスのすべてのカーネル空間がそのプロセスの仮想アドレス空間の下位1ギガバイトにマップされ、4GBの合計アドレス空間の上位3ギガバイトがマッピングされていることに注意してください(ここでは32ビットクラシックを使用しています)ケース)は、プロセステキスト(つまりコード)とデータに使用されます。ユーザースペースとの間のコピーは、プロセスに代わって実行されているカーネルコードによって実行され、実際には、コピー中に使用されているのは、そのプロセスのメモリマッピング(つまり、ページテーブル)です。これは、実行がカーネルモード(つまり、x86言語の特権/スーパーバイザーモード)で行われているときに行われます。ユーザースペースコードが正当なターゲットロケーション(つまり、そのプロセスアドレススペースに適切にマップされたアドレス)を渡して、カーネルコンテキストから実行されるcopy_to_userにデータをコピーすると仮定すると、通常はそのアドレス/領域に書き込むことができます。問題が発生し、コントロールがユーザーに戻った後、ユーザースペースは、プロセス自体によってこの場所の設定から最初に読み取ることもできます。さらに興味深い詳細は、Daniel P. Bovet、Marco Cesatiによる「Linuxカーネルの理解、第3版」の第9章と第10章にあります。特に、access_ok()は必要ですが、十分な妥当性チェックではありません。ユーザーは、プロセスアドレス空間に属していないアドレスを渡すことができます。この場合、カーネルコードがコピーを実行しているときに、ページフォールト例外が発生します。最も興味深い部分は、カーネルページフォールトハンドラーが、そのような場合のページフォールトがカーネルコードのバグによるものではなく、ユーザーからの不正なアドレスによるものであると判断する方法です(特に問題のカーネルコードがカーネルモジュールからのものである場合)ロード済み)。

8
aas029

最良の答えには何か問題があります。copy_(from | to)ユーザーは割り込みコンテキストでは使用できません。スリープする可能性があります。コピー(from | to)ユーザー関数のみを使用できます。プロセスのコンテキストでは、プロセスのページテーブルには、カーネルがアクセスするために必要なすべての情報が含まれているため、アドレス指定されたページがメモリ内にあることを確認できれば、カーネルはユーザースペースアドレスに直接アクセスできます。copy(from | to)を使用してください。 _user関数。ユーザーがチェックできるため、ユーザースペースのアドレス指定されたページが常駐していない場合は、直接修正されます。

1
Wang YanQing

copy_from_user()システムコールの実装は、異なるアドレス空間からの2つのバッファを使用して行われます。

  • ユーザースペースバッファユーザー仮想アドレススペース。
  • kernel-space bufferカーネル仮想アドレス空間。

copy_from_user()システムコールが呼び出されると、データはユーザーバッファからカーネルバッファにコピーされます。

copy_from_user()を使用した文字デバイスドライバコードの一部(書き込み操作)を以下に示します。

ssize_t cdev_fops_write(struct file *flip, const char __user *ubuf,
                        size_t count, loff_t *f_pos) 
{     
    unsigned int *kbuf;
    copy_from_user(kbuf, ubuf, count);
    printk(KERN_INFO "Data: %d",*kbuf); 
}
0
Praveen Felix