Избегайте расы TOCTOU (время проверки, время использования) между stat и переименованием - PullRequest
1 голос
/ 27 июня 2019

Как избежать условия гонки TOCTOU (время проверки, время использования) для условия гонки между stat и переименованием для LOGFILE?

Требуется для перемещения файла журнала после того, как его размер превысит максимальный размер.

result = stat(LOGFILE, & data);
if (result != 0) {
  // stat failed
  // file probably does not exist
} else if (data.st_size > MAX_LOGSIZE) {
  unlink(PREV_LOGFILE);
  (void) rename(LOGFILE, PREV_LOGFILE);
}

1 Ответ

2 голосов
/ 27 июня 2019

Стандартный способ избежать TOCTTOU для файловых операций - open файл один раз , а затем делать все, что вам нужно, через дескриптор файла, а не имя файла.

Однако как для переименования, так и для отмены связывания файла требуется его путь (потому что им нужно знать, какую ссылку переименовать или удалить), поэтому вы не можете использовать этот подход здесь.Альтернативой может быть копирование содержимого файла в другое место, а затем усечение его до нуля байтов, хотя ваш сценарий с файлами журналов, вероятно, требует, чтобы операция была атомарной, что может быть трудно достичь.Другой подход - требовать жесткого контроля доступа к каталогу: если злоумышленник не может записать в каталог, он не может играть в игры TOCTTOU с вашим процессом.Вы можете использовать unlinkat и renameat, чтобы ограничить ваши пути для файлового дескриптора определенного каталога, чтобы вам не нужно было беспокоиться об изменении самого каталога.

Что-то вроде этого непроверенного кода может выполнить эту работуПредполагая POSIX-подобную платформу:

dirfd = open(LOGDIR, O_DIRECTORY);
// check for failure
res = fstatat(dirfd, LOGFILE, statbuf, AT_SYMLINK_NOFOLLOW);
if ((0 == res) && (S_ISREG(statbuf) && (data.st_size > MAX_LOGSIZE)) {
    unlinkat(dirfd, PREV_LOGFILE, 0);
    renameat(dirfd, LOGFILE, dirfd, PREV_LOGFILE);
}
close(dirfd);
...