我最近在自定义Linux内核(2.6.31.5,x86)驱动程序中遇到了一个问题,其中copy_to_user不会定期不将任何字节复制到用户空间。它将返回传递给它的字节数,表明它没有复制任何内容。经过代码检查后,我们发现代码在调用copy_to_user时禁用了中断,这违反了合同。更正此问题后,该问题不再发生。因为问题很少发生,所以我需要证明禁用中断会导致问题。

如果您查看以下来自arch / x86 / lib / usercopy_32.c rep的代码片段; movsl通过CX中的计数将单词复制到用户空间。尺寸在退出时使用CX更新。如果movsl执行正确,CX将为0。因为CX不为零,所以动议?为了适应copy_to_user的定义和观察到的行为,必须未执行指令。

/* Generic arbitrary sized copy.  */
#define __copy_user(to, from, size)                 \
do {                                    \
    int __d0, __d1, __d2;                       \
    __asm__ __volatile__(                       \
        "   cmp  $7,%0\n"                   \
        "   jbe  1f\n"                  \
        "   movl %1,%0\n"                   \
        "   negl %0\n"                  \
        "   andl $7,%0\n"                   \
        "   subl %0,%3\n"                   \
        "4: rep; movsb\n"                   \
        "   movl %3,%0\n"                   \
        "   shrl $2,%0\n"                   \
        "   andl $3,%3\n"                   \
        "   .align 2,0x90\n"                \
        "0: rep; movsl\n"                   \
        "   movl %3,%0\n"                   \
        "1: rep; movsb\n"                   \
        "2:\n"                          \
        ".section .fixup,\"ax\"\n"              \
        "5: addl %3,%0\n"                   \
        "   jmp 2b\n"                   \
        "3: lea 0(%3,%0,4),%0\n"                \
        "   jmp 2b\n"                   \
        ".previous\n"                       \
        ".section __ex_table,\"a\"\n"               \
        "   .align 4\n"                 \
        "   .long 4b,5b\n"                  \
        "   .long 0b,3b\n"                  \
        "   .long 1b,2b\n"                  \
        ".previous"                     \
        : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)   \
        : "3"(size), "0"(size), "1"(to), "2"(from)      \
        : "memory");                        \
} while (0)


我有2个想法:


当禁用中断时,不会发生页面错误,并且
然后代表movs?不执行任何操作而被跳过。返回值
则为CX,或未复制到用户空间的数量,因为
定义指定并观察到行为。
虽然确实发生了页面错误,但是linux无法处理它,因为禁用了中断,因此页面错误处理程序会跳过该指令,尽管我不知道页面错误处理程序将如何执行此操作。同样,在这种情况下,CX将保持不变,并且返回值将是正确的。


谁能指出我在英特尔手册中指定此行为的部分,或指出可能有用的其他Linux资源?

最佳答案

我找到了答案。我的第二个建议是正确的,而且机制就在我的面前。确实发生了页面错误,但是fixup_exception机制用于提供异常/继续机制。本节将条目添加到异常处理程序表中:

    ".section __ex_table,\"a\"\n"               \
    "   .align 4\n"                 \
    "   .long 4b,5b\n"                  \
    "   .long 0b,3b\n"                  \
    "   .long 1b,6b\n"                  \
    ".previous"                     \


这表示:如果IP地址是第一个条目,并且在故障处理程序中遇到异常,则将IP地址设置为第二个地址并继续。

因此,如果异常发生在“ 4:”,请跳至“ 5:”。如果异常发生在“ 0:”,则跳至“ 3:”,如果异常发生在“ 1:”,则跳至“ 6:”。

缺少的部分在arch / x86 / mm / fault.c的do_page_fault()中:

/*
 * If we're in an interrupt, have no user context or are running
 * in an atomic region then we must not take the fault:
 */
if (unlikely(in_atomic() || !mm)) {
    bad_area_nosemaphore(regs, error_code, address);
    return;
}


in_atomic返回true,因为我们处于write_lock_bh()锁中! bad_area_nosemaphore最终会进行修复。

如果发生page_fault(由于工作空间的概念,这不太可能发生),则该函数调用将失败并跳出__copy_user宏,并且未复制的字节设置为size,因为禁用了抢占。

关于linux-kernel - 当mov指令导致页面错误并在x86上禁用中断时会发生什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12607288/

10-13 08:06