Недавно я столкнулся с проблемой в произвольном ядре Linux (2.6.31.5, x86), где copy_to_user периодически не копировал любые байты в пространство пользователя. Он вернет количество байтов, переданных ему, что указывает на то, что он ничего не скопировал. После проверки кода мы обнаружили, что код отключил прерывания при вызове copy_to_user, который нарушает его. После исправления этого вопроса проблема прекратилась. Поскольку проблема возникла нечасто, мне нужно доказать, что отключение прерываний вызвало проблему.
Если вы посмотрите на фрагмент кода ниже из arch/x86/lib/usercopy_32.c rep; movsl копирует слова в пользовательское пространство по счету в CX. Размер обновляется CX при выходе. CX будет 0, если movsl выполняется правильно. Поскольку CX не равен нулю, movs? инструкции не должны выполняться, чтобы соответствовать определению 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 идеи, которые у меня есть:
- когда прерывания отключены, ошибка страницы не возникает и затем rep; варисторы? пропускается, ничего не делая. Возвращаемое значение тогда будет CX или количество, не скопированное в пользовательское пространство, так как определение и наблюдаемое поведение.
- Ошибка страницы, но linux не может ее обработать, поскольку прерывания отключены, поэтому обработчик ошибок страницы пропускает инструкцию, хотя я не знаю, как это сделает обработчик ошибок страницы. Опять же, в этом случае CX останется неизмененным, и возвращаемое значение будет правильным.
Может ли кто-нибудь указать мне разделы в руководствах Intel, которые определяют это поведение, или указать мне на любой дополнительный источник Linux, который может быть полезен?