Патч к ядру Linux: использование

From EjudgeWiki

Навигация: Главная страница/Система ejudge/Патч к ядру Linux/Использование

В данном разделе описывается использование патча для ядер серии 2.6. Здесь описываются нискоуровневые интерфейсы, которые могут измениться (и изменяются) от версии к версии ядра. Поэтому рекомендуется использовать библиотеку reuse.

Включение безопасного режима работы процесса

Для включения безопасного режима работы используется системный вызов ptrace:

ptrace(0x4281, 0, 0, 0)

Здесь 0x4281 — «магическое» число - код запроса. Безопасный режим включается у текущего процесса. После включения безопасного режима процесс имеет право выполнить один системный вызов execve, то есть запустить на выполнение другую программу. Запускаемая программа будет работать в безопасном режиме, но уже не будет иметь право выполнять системный вызов exec.

При успешном завершении (то есть когда патч установлен в работающее ядро) системный вызов ptrace возвращает значение 0. Если патч не установлен, ptrace вернет значение -1, а переменная errno будет содержать код ошибки EINVAL.

Установка ограничения времени работы процесса в миллисекундах

Для установки ограничения времени работы процесса в миллисекундах используется системный вызов setrlimit со специальным кодом ресурса.

struct rlimit lim;
lim.rlim_cur = mtime; // устанавливаем ограничение mtime миллисекунд
lim.rlim_max = mtime; // устанавливаем ограничение mtime миллисекунд
setrlimit(RLIMIT_MCPU, &lim);

Константа RLIMIT_MCPU определена в заголовочных файлах исходных кодов ядра Linux после применения патча. Однако, эта константа не определена в пользовательских заголовочных файлах стандартной библиотеки. Поэтому вместо нее необходимо использовать численное значение, которое разное для разных версий ядра. Так, для ядер серии 2.4 RLIMIT_CPU равен 11, для ядер серии 2.6 до версии 2.6.25 RLIMIT_CPU равен 15, а начиная с версии 2.6.25 — 19.

Проверку версии ядра лучше всего проводить на этапе работы программы, проанализировав информацию, возвращаемую системным вызовом uname.

Для ядер версии >= 3 данная возможность более не поддерживается.

Контроль ошибки Memory Limit Exceeded

По умолчанию даже в безопасном режиме диагностирование ошибки Memory Limit Exceeded отключено. Поведение системы в ситуации превышения лимита памяти процессом не отличается от стандартного, то есть:

  • если лимит памяти превышен при запуске программы (например, слишком большая область глобальных переменных), процесс снимается с выполнения посылкой ему сигнала SIGKILL (для непривилегированного участника это отображается как Run-time Error);
  • если лимит памяти превышен при работе со стеком (например, вследствии слишком глубокой рекурсии или слишком большой области локальных переменных), процесс снимается с выполнения посылкой ему сигнала SIGSEGV (для непривилегированного участника это отображается как Run-time Error);
  • если лимит памяти превыщен при выполнении системного вызова выделения памяти, такого как sbrk или mmap, системный вызов завершается с соответствующим кодом ошибки, и дальнейшая обработка ошибки возлагается на работающую программу.

Для включения диагностики ошибки Memory Limit Exceeded требуется выполнить системный вызов ptrace:

ptrace(0x4280, 0, 0, 0)

Здесь 0x4280 — «магическое» число - код запроса. После включения режима диагностики ошибки Memory Limit Exceeded в ситуации превышения лимита памяти процессом в любой из трех ситуаций ему посылается сигнал SIGKILL, и устанавливается специальный флаг. Это значит, что при включенной диагностике ошибки Memory Limit Exceeded программа не имеет возможность отреагировать на исчерпание памяти в куче.

Значение флага ошибки Memory Limit Exceeded можно получить с помощью системных вызовов семейства wait*. В слове состояния процесса (параметр status) устанавливается бит 0x10000, проверить который можно следующим образом:

wait(&status);
if ((status & 0x10000)) {
  // Memory Limit Exceeded
}

Контроль ошибки Security Violation

Хотя в безопасном режиме все потенциально опасные системные вызовы запрещены, и программа не может нанести урона системе, может оказаться желательным отслеживать явные попытки выполнения опасных системных вызовов со стороны процесса. Например, если процесс пытается выполнить системный вызов fork, значит программа сознательно пытается нарушить функционирование системы, так как стандартная библиотека никогда не выполнит fork при выполнении безобидных операций (выделения памяти и пр.). С другой стороны, есть системные вызовы, такие как open, которые выполняются стандартной библиотекой неявно. Например, вызов некоторых (безобидных) функций стандартной библиотеки может привести к попытке открыть на чтение файл /etc/passwd.

Поэтому все запрещенные системные вызовы разделены на две группы: группу категорически запрещенных системных вызовов, попытка использования которых вызывает установку флага ошибки Security Violation, и группу остальных запрещенных системных вызовов.

Чтобы проверить, были ли в ходе выполнения процесса попытки выполнения категорически запрещенных системных вызовов, можно использовать следующий код:

wait(&status);
if ((status & 0x20000)) {
  // Security Violation
}

Так как стандартно в переменной status используется только младшие 16 бит, после проверки на ошибки Memory Limit Exceeded, Security Violation и Time-Limit Exceeded рекомендуется явно очистить старшие биты, чтобы гарантировать корректную работу макросов проверки статуса завершения процесса WIFEXITED и пр.

status &= 0xffff;

Контроль ошибки Time-limit exceeded

Для включения диагностики ошибки time-limit exceeded требуется выполнить системный вызов ptrace:

ptrace(0x4282, 0, 0, 0)

Здесь 0x4282 — «магическое» число - код запроса.

В случае превышения максимального времени выполнения процесса ему посылается сигнал SIGKILL (как и в случае работы ядра Linux без патча), и дополнительно устанавливается флаг состояния процесса.

Чтобы проверить, было ли превышение времени работы, можно использовать следующий код:

wait(&status);
if ((status & 0x40000)) {
  // Time-limit Exceeded
}

Так как стандартно в переменной status используется только младшие 16 бит, после проверки на ошибки Memory Limit Exceeded, Security Violation и Time-Limit Exceeded рекомендуется явно очистить старшие биты, чтобы гарантировать корректную работу макросов проверки статуса завершения процесса WIFEXITED и пр.

status &= 0xffff;