copy_from_user


ユーザプロセスのメモリをカーネルスレッドプロセスが参照する場合、そのメモリアドレスは切り替わった前ユーザプロセスとなり、掛かるデータは元プロセスのデータであると限りません。従ってカーネル空間に転送して参照する必要があります。

ユーザメモリを扱う内部関数には、access_ok()で引数がユーザメモリかどうかチェックしますが(チェックしないのもあります)、デバイスドライバ/procfs等でユーザデータを扱うlkmを実装する場合、直接ユーザメモリを参照する実装でも、問題なく動作したりして、そのようなミスをプログラミングレベルで避けるためで、その引数を__userと宣言する事で、そのような実装を避ける事ができ、従って、copy_from_user()はコンパイルマターで、実装はmemcpy()に過ぎません。

copy_from_user()のユーザメモリ引数であるconst void __user *を考慮して実装されており、__CHECKER__でのカーネル実装では、__user __attribute__((noderef, address_space(1)))となります。

noderef, address_space(1)は、引数のアドレス空間はaddress_space(1)で、他のアドレス空間(0/2/3/4)との参照できず、noderefは引数ポインタとして参照できない。と言う事のようで、イメージとして__userは、int/char等のgccの型に相当するgccの超えた新しい型と言えます。address_space(1)は実アドレス空間を示すものでなく、コンパイルの型参照の整合性チェックの論理的アドレス空間型という事です。

__user型変数を__user型以外の変数へキャストしたり、そのポインタを直接カーネル内で参照したりすると、コンパイル時にワーニングが表示されるようです。(gccでなくsparceと言うコマンドで行うようですが・・・)

copy_from_user()から__copy_from_userをコールしています。そして両者ともinlineで、__must_check long 故に、copy_from_user()がエラーなく実行できたかのチェックすべく、その返り値を参照していなければ、コンパイル時にワーニングが表示されます。

copy_from_user()をコールしなくても、ユーザメモリを直接参照する構築も、一見問題なく実装できるケースもある故、そのようなプログラムミスを避けるために、__userの宣言による、copy_from_user()を導入したかと思います。
#ifdef __CHECKER __
 # define __user         __attribute__((noderef, address_space(1)))
#else
 # define __user
#endif

#if __GNUC_MINOR__ >= 4
#define __must_check            __attribute__((warn_unused_result))
#endif


static inline long copy_from_user(void *to,
               const void __user * from, unsigned long n)
{
       might_sleep();
       if (access_ok(VERIFY_READ, from, n))
               return __copy_from_user(to, from, n);
       else
               return n;
}
voidから__userへの転送故、__forceとしています。
static inline __must_check long __copy_to_user(void __user *to,
               const void *from, unsigned long n)
{
       if (__builtin_constant_p(n)) {
               switch(n) {
               case 1:
                       *(u8 __force *)to = *(u8 *)from;
                       return 0;
               case 2:
                       *(u16 __force *)to = *(u16 *)from;
                       return 0;
               case 4:
                       *(u32 __force *)to = *(u32 *)from;
                       return 0;
#ifdef CONFIG_64BIT
               case 8:
                       *(u64 __force *)to = *(u64 *)from;
                       return 0;
#endif
               default:
                       break;
               }
       }

       memcpy((void __force *)to, from, n);
       return 0;
}

補足

0から4までの論理的アドレス空間が定義されていて、各カテゴリ内でしかやり取りすべきでない変数として定義する事で、プログラムミスを防ぎます。かかる宣言は、カーネルの実実装マターと言うより、プログラミングマターという事です。
# define __user         __attribute__((noderef, address_space(1)))
# define __kernel       __attribute__((address_space(0)))
# define __iomem        __attribute__((noderef, address_space(2)))
# define __percpu       __attribute__((noderef, address_space(3)))
# define __rcu          __attribute__((noderef, address_space(4)))

初期の頃、copy_from_user()は、ユーザ空間をカーネル空間が参照する場合、アーキテクチャ的な特別な意味合い故の実装かと思って、些細な事柄がカーネル読み込みの大きな障害となっていたものでした。


最終更新 2015/10/21 18:29:46 - north
(2013/11/12 16:09:32 作成)


検索

アクセス数
3586188
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。