пользовательская наземная память перезаписывается на 8 байт указателем на наземную память ядра

#c #linux #memory

#c #linux #память

Вопрос:

Мы завершили многопоточную программу, которая является сложной. Я упрощаю это так:

  1. Запустите основной поток и инициализируйте пул потоков, который будет использоваться для запроса данных с удаленного сервера.
  2. Основной поток создает около 3000 новых задач и добавляет их в пул потоков.
  3. Каждая задача инициализируется буфером памяти. Поток пула потоков получает одну из задач, получает удаленную форму данных, копирует данные в буфер памяти, инициализированный ранее, и сообщает основному потоку прочитать данные.
  4. Когда основной поток считывает данные из буфера, он проверяет значение контрольной суммы данных для обнаружения ошибок.

Процессы выполняются на 2500 машинах, и на каждой машине одновременно выполняется около 20 процессов. Может возникнуть одна ошибка в отношении одного процесса за один день.

Сначала мы думаем, что причиной должны быть данные, возвращаемые удаленным сервером, поэтому мы добавляем проверку CRC данных к ответу на данные с удаленного сервера. Проверкой CRC32 было доказано, что данные всегда верны, когда они попадают в процесс.

Тогда мы думаем, что должна быть какая-то ошибка в указателе, используемом в пользовательской области, которая может перезаписать буфер памяти задачи. Поэтому мы добавляем защиту в буфер данных каждой задачи с помощью mprotect. Сначала скопируйте данные в буфер задачи, затем защитите буфер памяти, установив его как только готовый, в последний раз проверьте CRC ответа и буфер данных; оба они верны на данный момент. Но когда основной поток считывает данные из буфера, возникает ошибка данных!!!

Мы модифицируем код для прерывания, когда он обнаруживает ошибку данных, и ищем данные, которые перезаписаны. найден 0xffff881492499c88. Это значение является указателем виртуальной памяти ядра в X86_64 linux. Я загружаю основной дамп и обнаруживаю, что память задачи также находится в режиме только для чтения.

Я так смущен защитой памяти. Почему буфер пользовательской наземной памяти находится в режиме только для чтения и изменяется без ошибки сегментации?

Я не знаком с вещами ядра. Есть ли возможный способ для ядра изменить пользовательскую наземную память, которая может игнорировать привилегии памяти? Если ядро и пользовательское виртуальное пространство mmap-ed в одно и то же физическое пространство памяти, может ли ядро изменять память без дампа ядра? Если да, то как я могу отследить эти проблемы?

В моей машинной среде есть возможность cgroup управлять памятью процессов в ядре Linux 2.6.32.

Я работал над этим более 2 недель. Любое предложение приветствуется. Если была ошибка, связанная с этим случаем, пожалуйста, дайте мне знать. В ОС подключено несколько модулей ядра, которые имеют необходимый частный патч, поэтому следующая работа, которую я могу предпринять, — отключить подозрительный модуль и воспроизвести эту ошибку. Если он больше не может быть воспроизведен, может быть найден соответствующий модуль.

Комментарии:

1. Ошибки повреждения памяти (особенно подобные этой, возникающие крайне редко) трудно отлаживать. Попробуйте запустить свой код через профилировщик памяти (например, valgrind). Также проверьте код на наличие условий гонки (для этого вы можете использовать инструмент helgrind от valgrind, но есть и другие инструменты).

2. Нет, вы не можете записывать в пространство ядра. Наиболее вероятным объяснением является то, что вы перезаписываете указатель между записью в буфер и его чтением, поэтому вы фактически читаете из совершенно другой части памяти, чем вы записали.

3. Воспроизвести ошибку данных очень и очень сложно, при работе на 2500 машинах в течение дня может возникнуть одна на случайной машине. Я думаю, что valgrind не поможет в этой ситуации, что позволит снизить скорость в 10 раз. Кроме valgrind, мы изменили библиотеку tc_malloc для управления памятью, которая сохранит вновь освобожденную память неиспользуемой немедленно, ошибка данных не найдена в течение недели.

4. Я проверил указатель виртуальной памяти на пользовательской земле, он не изменился, но данные изменены, которые указаны, которые снова привязаны к данным, полученным с удаленного сервера. Поэтому я думаю, что основной поток считывает данные из правой части памяти.

5. Использование ECC RAM? Если нет, это может быть повреждение аппаратной памяти. Использование аппаратного обеспечения DMA? Может быть ошибка прошивки или ошибка драйвера. Например, карта Ethernet, использующая DMA, записывает в память, которую она использовала, но была удалена для другой цели, но карта еще не обновилась. Эти ошибки аппаратного типа могут и случаются.