14

カーネル空間からユーザー空間にデータをコピーするときは、copy_to_user()を使用する必要があり、memcpy()を使用するとシステムに問題が発生する、と常に言われています(本やチュートリアルで)。最近誤ってmemcpy()を使用しましたが、問題なく完全に機能しました。memcpy()の代わりにcopy_to_userを使用する必要があるのはなぜですか

私のテストコード(カーネルモジュール)は次のようなものです:

static ssize_t test_read(struct file *file, char __user * buf,
             size_t len, loff_t * offset)
{
    char ani[100];

    if (!*offset) {
        memset(ani, 'A', 100);
        if (memcpy(buf, ani, 100))
            return -EFAULT;
        *offset = 100;
        return *offset;
    }

    return 0;
}

struct file_operations test_fops = {
    .owner = THIS_MODULE,
    .read = test_read,
};

static int __init my_module_init(void)
{
    struct proc_dir_entry *entry;

    printk("We are testing now!!\n");
    entry = create_proc_entry("test", S_IFREG | S_IRUGO, NULL);
    if (!entry)
        printk("Failed to creats proc entry test\n");

    entry->proc_fops = &test_fops;
    return 0;
}
module_init(my_module_init);

ユーザースペースアプリから、/procエントリを読んでいて、すべて正常に動作しています。

copy_to_user()のソースコードを見ると、これも単純なmemcpy()であり、access_okを使用してポインターが有効かどうかを確認し、memcpyを実行していることがわかります。

したがって、現在の私の理解では、渡すポインタが確実であれば、copy_to_userの代わりにmemcpy()を常に使用できます

私の理解が正しくない場合は訂正してください。また、copy_to_userが機能し、memcpy()が失敗する例は非常に役立ちます。ありがとう。

4

2 に答える 2

33

これにはいくつかの理由があります。

まず、セキュリティ。カーネルは任意のアドレスに書き込むことができるため、取得して使用するユーザースペースアドレスを使用するだけmemcpyで、攻撃者が別のプロセスのページに書き込む可能性があります。これはセキュリティ上の大きな問題です。copy_to_userターゲットページが現在のプロセスで書き込み可能であることを確認します。

アーキテクチャに関する考慮事項もいくつかあります。たとえば、x86では、ターゲットページをメモリに固定する必要があります。一部のアーキテクチャでは、特別な指示が必要になる場合があります。等々。非常に移植性が高いというLinuxカーネルの目標には、この種の抽象化が必要です。

于 2013-02-20T01:31:23.273 に答える
0

この回答は遅れる可能性がありますが、とにかくcopy_to_user()、その姉妹copy_from_user()は両方とも、ユーザーが渡したパラメーターとバッファーのサイズについていくつかのサイズ制限チェックを行うsizeため、次の読み取りメソッドを実行します。

char name[] = "This message is from kernel space";
ssize_t read(struct file *f, char __user *to, size_t size, loff_t *loff){
                
    int ret = copy_to_user(to, name, size);
    if(ret){
        pr_info("[+] Error while copying data to user space");
        return ret;
    }
    pr_info("[+] Finished copying data to user space");
    return 0;
}

そして、ユーザースペースアプリread(ret, buffer, 10);OKとして読み取られますが、10を35以上に置き換えると、カーネルは次のエラーを発行します。

Buffer overflow detected (34 < 35)!

コピーが失敗してメモリリークが防止されます。同じcopy_from_user()ことが当てはまり、カーネルバッファサイズのチェックも行われます。


そのため、ポインタ(配列ではない)を使用するchar name[]char *nameサイズを決定できなくなり、カーネルからこのエラーが発生するため、使用する必要があります。

BUG: unable to handle page fault for address: ffffffffc106f280
#PF: supervisor write access in kernel mode
#PF: error_code(0x0003) - permissions violation

この答えが何らかの形で役立つことを願っています。

于 2021-06-30T13:05:28.537 に答える