8

私はいくつかの調査を行ってきましたが、このマクロについて少し混乱しています。うまくいけば、誰かが私にいくつかのガイダンスを与えることができます. 私はいくつかのioctlコード(私が継承したもので、書いたものではありません)を持っておりaccess_ok()、ユーザー空間からデータをコピーする前にチェックするかどうかを最初に確認します:

#define __lddk_copy_from_user(a,b,c) copy_from_user(a,b,c)
#define __lddk_copy_to_user(a,b,c) copy_to_user(a,b,c)

long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
  switch(cmd) {
    case COMMAND:
      if(! access_ok(VERIFY_READ, (void *)arg, sizeof(Message_par_t)))
        return(retval); 

      if(! access_ok(VERIFY_WRITE, (void *)arg, sizeof(Message_par_t)))
        return(retval); 

      argp = &Command;
      __lddk_copy_from_user( (void *) argp,(Command_par_t *) arg, sizeof(Command_par_t));

したがって、コードは問題なく機能しますが、必要かどうかはわかりません。最初の質問は、access_ok の戻りに関する次の説明から来ています。

  • 領域がアクセス可能である可能性が高い場合、関数はゼロ以外を返します (ただし、アクセスしても -EFAULT になる場合があります)。この関数は、アドレスがカーネルではなくユーザー空間にある可能性が高いことを確認するだけです。

これは、チェックしているポインターがおそらくユーザー空間で初期化されていることを確認するだけで、実際には何もしないことを意味しますか? ユーザー空間の呼び出し以外ではこの関数に入ることができず、このデバイスに対して有効なファイル記述子を開かない限り発生しないことがわかっているので、これは本当に必要ですか? NULL ポインターを取得していないことを確認するよりも安全ですか?

2 番目の質問は、次の説明から来ています。

  • type 引数は、VERIFY_READ または VERIFY_WRITE として指定できます。VERIFY_WRITE シンボリックは、メモリ領域が読み取り可能か書き込み可能かを識別します。

これは、コードの最初のチェックが冗長であることを意味しますか? 書き込み可能な領域をチェックする場合、景品として読み取り可能になりますか?

私は x86 アーキテクチャを使用しているので、access_ok() と __range_no_ok() の定義は、次のように /usr/src/linux-3.1.10-1.16/arch/x86/include/asm/uaccess.h のものです。

#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;                               \
})
4

3 に答える 3

13

__lddk_copy_from_user()単に を呼び出すだけの場合、これらのチェック自体が実行されるためcopy_from_user()access_ok()チェックは冗長になります。copy_from_user()

このaccess_ok()チェックにより、ユーザー空間アプリケーションがカーネルにカーネル アドレスからの読み取りまたはカーネル アドレスへの書き込みを要求していないことが保証されます (これらは整合性/セキュリティ チェックです)。ポインターがユーザー空間によって提供されたからといって、それが間違いなくユーザー空間ポインターであるとは限りません。多くの場合、「カーネル ポインター」は単に、仮想アドレス空間の特定の領域内を指していることを意味します。

さらに、 with を呼び出すaccess_ok()VERIFY_WRITE暗黙的に が呼び出されるVERIFY_READため、前者を確認した場合、後者も確認する必要はありません。


2019 年 のこのコミットの時点で、access_ok()もはやtype議論がないため、VERIFY_WRITE対決のVERIFY_READポイントは議論の余地があります。

于 2012-09-11T01:49:27.380 に答える
1

access_okマクロは、ポインタの有効性を簡単にチェックするだけです。たとえば、NULL または読み取り専用の引数を使用した誤った呼び出しをキャッチします。

理想的には、後でアクセス関数が失敗した場合でも、ドライバーが回復する必要があります。ただし、これは、高価なハードウェア操作の後でのみ発生する可能性があります。したがって、早期にチェックすることで、最も一般的なユーザー空間のプログラマー エラーに対してドライバーをより堅牢にすることができます。

編集: ええ、唯一の入力呼び出しがそこにある copy_from_user() である場合、チェックは冗長です。ただし、後で書き込みがある場合は、最初に書き込み可能かどうかを確認すると便利です。

そして、copy_from_user() の戻り値を実際に確認する必要があります。

于 2012-09-10T19:16:08.337 に答える
1

冗長ではありません。access_ok() はアドレスを検証し、この領域からの読み取りを試みます。アドレスが有効で、ユーザー空間に存在する場合は読み取りを試みます。それ以外の場合、関数はアドレスの失敗を示す EFAULT を返します。これは、NULL に対してチェックするよりも、領域へのアクセスを検証する方が安全な方法です (カーネルがクラッシュする可能性のあるセグメンテーション違反が発生する前に)。

さらに、access_ok() を使用して、読み取り/書き込みアクセスを確認できます。2 番目の呼び出しは、そのリージョンでの「書き込み」アクセスを検証するだけです。

于 2012-09-10T19:17:27.320 に答える