c - 尝试从 mm_struct->start_code 复制时 memcpy 失败

标签 c linux memory-management linux-kernel kernel

我在 task->mm 指向的代码段中遇到了一些非常奇怪的行为,我希望有人能帮助我。我正在做的是将代码段拉出并将其放入缓冲区,然后从中生成 HMAC。偶尔我会得到一个哎呀!说不能使用终止于 memcpy() 的调用堆栈。似乎正在发生的事情是数据在复制过程的中间消失,它导致页面错误,然后是 Ooops。我广泛搜索了对 mm_struct 中使用的这种看似短暂的内存特性的引用,但一无所获。我不相信我在代码中做了任何有争议的事情;为简洁起见,此处删除了注释等。

struct mm_struct* __mm;
...

__mm = get_task_mm(__task);
if(likely(__mm))
{
    __buflen = (__mm->end_code - __mm->start_code);
    if(likely(__buflen > 0))
    {
        __buf = (unsigned char*)__get_buffer(__buflen);
        if(likely(__buf))
        {          
            preempt_disable();
            memcpy(__buf, (uint8_t*)__mm->start_code, __buflen);
            preempt_enable();

            mmput(__mm);

            if(unlikely(!__do_ntru_hmac(__buf, __buflen, __hmac)))
            {
                __retcode = 0;
            }

            __release_buffer(__buf, __buflen);
        }
        else
        {
            printk(KERN_ERR "[%s] Buffer allocation failure [%d]\n", __task->comm, __buflen);
            __retcode = 0;
        }

        ...

内存分配例程很简单,旨在能够一次分配大块内存。它们看起来像这样:

void* __get_buffer(unsigned long __buflen)
{
    if(likely(__buflen <= KMALLOC_MAX_SIZE))
    {
        return kmalloc(__buflen, GFP_KERNEL);
    }
    else
    {
        return (void*)__get_free_pages(GFP_KERNEL, get_order(__buflen));
    }

    return NULL;
}

void __release_buffer(void* __buffer, unsigned long __buflen)
{
    if(likely(__buflen <= KMALLOC_MAX_SIZE))
    {
        kfree(__buffer);
    }
    else
    {
        free_pages((unsigned long)__buffer, get_order(__buflen));
    }

    return;
 }

错误似乎是随机发生的,我无法将它与任务、父级或 struct task_struct 的任何其他组件联系起来。我尝试过使用互斥锁和自旋锁来保护 memcpy 期间的内存,我尝试使用 set_task_state() 完全停止任务并在复制后重新启动它,但似乎没有什么可以阻止问题。

更新:我仍在努力解决这个问题,尽管我会投入更多数据。这是糟糕的转储。

Mar 16 09:39:27 ubuntu kernel: [  324.229195] BUG: unable to handle kernel paging request at 0804b000
Mar 16 09:39:27 ubuntu kernel: [  324.229199] IP: [<c1312dfd>] memcpy+0x1d/0x40
Mar 16 09:39:27 ubuntu kernel: [  324.229221] *pdpt = 000000002cf4c001 *pde = 000000003b72c067
Mar 16 09:39:27 ubuntu kernel: [  324.229223] Oops: 0000 [#1] SMP
Mar 16 09:39:27 ubuntu kernel: [  324.229225] Modules linked in: aerolock(OF) vmhgfs(OF) vmw_balloon psmouse snd_ens1371 serio_raw gameport snd_ac97_codec ac97_bus snd_pcm snd_seq_midi btusb snd_rawmidi snd_seq_midi_event snd_seq snd_timer snd_seq_device vmwgfx snd ttm drm bnep rfcomm soundcore mac_hid bluetooth snd_page_alloc vmw_vmci i2c_piix4 parport_pc ppdev shpchp lp parport hid_generic usbhid hid pcnet32 mptspi ahci libahci mptscsih mptbase floppy mii vmw_pvscsi vmxnet3
Mar 16 09:39:27 ubuntu kernel: [  324.229256] CPU: 0 PID: 2880 Comm: aerolockd Tainted: GF          O 3.11.0-17-generic #31~precise1-Ubuntu
Mar 16 09:39:27 ubuntu kernel: [  324.229258] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/31/2013
Mar 16 09:39:27 ubuntu kernel: [  324.229259] task: f1816700 ti: ed774000 task.ti: ed774000
Mar 16 09:39:27 ubuntu kernel: [  324.229262] EIP: 0060:[<c1312dfd>] EFLAGS: 00010202 CPU: 0
Mar 16 09:39:27 ubuntu kernel: [  324.229264] EIP is at memcpy+0x1d/0x40
Mar 16 09:39:27 ubuntu kernel: [  324.229266] EAX: ecc80000 EBX: 00011cd0 ECX: 00003b34 EDX: 08048000
Mar 16 09:39:27 ubuntu kernel: [  324.229268] ESI: 0804b000 EDI: ecc83000 EBP: ed775e74 ESP: ed775e68
Mar 16 09:39:27 ubuntu kernel: [  324.229269]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
Mar 16 09:39:27 ubuntu kernel: [  324.229271] CR0: 80050033 CR2: 0804b000 CR3: 2d9d5000 CR4: 001407f0
Mar 16 09:39:27 ubuntu kernel: [  324.229345] Stack:
Mar 16 09:39:27 ubuntu kernel: [  324.229347]  00011cd0 f1816700 f33703b4 ed775eb0 f9ba3a1b 0000063b 00000000 00000000
Mar 16 09:39:27 ubuntu kernel: [  324.229353]  0000063c c1b80e4c f1816700 ed775ee0 08048000 ecc80000 00000000 f1816700
Mar 16 09:39:27 ubuntu kernel: [  324.229358]  f9baa952 f1816700 ed775f08 f9ba3b6e 00000000 00000000 00000000 c1b9d642
Mar 16 09:39:27 ubuntu kernel: [  324.229364] Call Trace:
Mar 16 09:39:27 ubuntu kernel: [  324.229370]  [<f9ba3a1b>] __generate_hmac+0x8b/0x190 [aerolock]
Mar 16 09:39:27 ubuntu kernel: [  324.229373]  [<f9ba3b6e>] __validate_hmac+0x4e/0x220 [aerolock]
Mar 16 09:39:27 ubuntu kernel: [  324.229377]  [<f9ba3da0>] ret_do_fork+0x60/0x70 [aerolock]
Mar 16 09:39:27 ubuntu kernel: [  324.229384]  [<c167f12a>] trampoline_handler+0x11a/0x1c0
Mar 16 09:39:27 ubuntu kernel: [  324.229390]  [<c10839a4>] ? wake_up_new_task+0xe4/0x150
Mar 16 09:39:27 ubuntu kernel: [  324.229394]  [<c1054bf5>] ? SyS_clone+0x25/0x30
Mar 16 09:39:27 ubuntu kernel: [  324.229397]  [<c1054bf5>] ? SyS_clone+0x25/0x30
Mar 16 09:39:27 ubuntu kernel: [  324.229400]  [<c167efee>] kretprobe_trampoline+0x16/0x38
Mar 16 09:39:27 ubuntu kernel: [  324.229404]  [<c167efd8>] ? kretprobe_trampoline_holder+0x8/0x8
Mar 16 09:39:27 ubuntu kernel: [  324.229406]  [<c167c937>] syscall_call+0x7/0xb
Mar 16 09:39:27 ubuntu kernel: [  324.229408] Code: c3 90 8d 74 26 00 e8 33 fe ff ff eb e8 90 55 89 e5 83 ec 0c 89 5d f4 89 75 f8 89 7d fc 3e 8d 74 26 00 89 cb 89 c7 c1 e9 02 89 d6 <f3> a5 89 d9 83 e1 03 74 02 f3 a4 8b 5d f4 8b 75 f8 8b 7d fc 89
Mar 16 09:39:27 ubuntu kernel: [  324.229439] EIP: [<c1312dfd>] memcpy+0x1d/0x40 SS:ESP 0068:ed775e68
Mar 16 09:39:27 ubuntu kernel: [  324.229444] CR2: 000000000804b000
Mar 16 09:39:27 ubuntu kernel: [  324.229447] ---[ end trace 3c014cb0223fa59a ]--- 

我尝试了很多不同的策略,但都以失败告终。例如 copy_from_user() 每次都失败;有时不阅读整个请求,有时返回部分请求。每次它在部分失败时,它都会在页面边界上失败——再次使内存看起来像是在复制过程中被带走。

考虑到我正在 Hook do_fork(),当我试图捕获它时,进程是否可以从内核空间过渡到用户空间?正如我之前提到的,我试过停止当前任务并重新启动后复制,但没有任何效果。

也很有趣;在运行 Ubuntu 12.04 (3.8.13-bone28) 的单个处理器 ARM BeagleBoard Black 上,我有相同的 (memcpy()) 代码在不同的负载下连续运行六周而没有出现故障。这个问题似乎只发生在我运行 Ubuntu 12.04 的 x86 机器上,然后只有当我对它施加重负载时,比如启动 Chromium。

抱歉在这里啰嗦了这么久,我被难住了。

有什么想法吗?

再次感谢,

皮特

最佳答案

正如您所发现的,您不能直接从用户空间可靠地复制内存。

使用 copy_from_user() 而不是 memcpy()。并且不要禁用抢占,这没有意义。

关于c - 尝试从 mm_struct->start_code 复制时 memcpy 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22273516/

相关文章:

Python 2.7 concurrent.futures.ThreadPoolExecutor 不并行化

iphone - 一个有 2 个所有者的对象

iphone - 自动释放池和内存管理

c - 如何在 nios II cpu 上构建一个简单的锁(互斥锁)

c - C 中的段错误、核心转储、gdb 输出

c++ - 类型转换的原理是什么?

linux - 在 unix 远程服务器中执行参数文件

Python shell 脚本

c - 是什么让它在标记化后插入新元素时不起作用

objective-c - iOS 内存管理 - 说明