FlawOpen

flawopen.com/Teardowns/cve-2025-39682-linux-ktls-zero-length-record-mishandling

● CVE-2025-39682 · CVSS 9.8 · Критический
Исследования · FlawOpen

Linux CVE-2025-39682: обработка пустой TLS-записи в kTLS

TLS-запись не-DATA нулевой длины в очереди приёма kTLS Linux могла обходить проверку типа на вызов recvmsg. Исправление проверяет тип обработанной записи, даже если не скопировано ни одного байта. В оценке CVSS Linux указаны возможная утечка данных из кучи ядра в пользовательское пространство, повреждение памяти и удалённые сбои ядра; это оценённые последствия, а не утверждение, что каждое затронутое устройство испытает все эффекты.

Вектор CVSSCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
💡 Простыми словами (ELI5)

Представьте почтовую сортировку, где в каждую ячейку разрешено класть письма только одной категории. У пустого конверта тоже есть категория, но старая сортировка выбирала смену ячейки по числу писем. При нуле писем она игнорировала категорию и смешивала следующий тип. Исправление ядра проверяет сам маркер категории, даже если в конверте нет данных.

Ключевые понятия и термины

kTLS
TLS в ядре: Linux может обрабатывать TLS-записи в ядре для подключённого TCP-сокета.
recvmsg()
Системный вызов приёма сокета, используемый приложением и путём приёма kTLS.
Тип содержимого TLS
Поле, классифицирующее запись как данные приложения или управляющую запись.
rx_list
Очередь ожидающих записей приёма, обрабатываемых последующими вызовами recvmsg.
CWE-754
Недостаточная проверка необычных или исключительных условий. NVD указывает CWE-754 со ссылкой на CISA-ADP; в самой записи CVE Linux CWE не назначен.

Связи со стандартами

Анализ первопричины

Приём Linux kTLS должен возвращать за один вызов recvmsg() либо непрерывные записи DATA, либо одну запись не-DATA. Если process_rx_list() обрабатывал начальную запись не-DATA нулевой длины, он мог установить тип TLS и вернуть err == 0. tls_sw_recvmsg() присваивал ноль переменной copied и использовал (copied && control != TLS_RECORD_TYPE_DATA) в условии остановки. Поскольку copied равнялся нулю, уже обработанный тип игнорировался и обработка продолжалась для следующих данных. Исправление upstream проверяет control напрямую: если тип задан и это не DATA, обработка прекращается; ноль остаётся маркером «тип ещё не сообщён». Уязвимость относится к системам, где включён и используется приём TLS ядром Linux (kTLS); это не ошибка всех пользовательских TLS-библиотек. Опубликованный Linux вектор CVSS 3.1 — 9,8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H). Оценивайте риск с учётом реального использования kTLS и версии ядра поставщика.

Пошаговый сценарий атаки

Шаг 1

kTLS получает записи

Приложение использует TCP-сокет с включённым приёмом TLS в ядре.

Шаг 2

Обрабатывается пустая ожидающая запись не-DATA

Случай возникает, если первая запись из rx_list не содержит полезных данных. Обработка может сообщить тип и вернуть ноль скопированных байтов.

Шаг 3

Старое условие зависит от числа скопированных байтов

При copied == 0 старое условие не проверяет, что обработанная запись была не-DATA.

Шаг 4

Цикл пересекает границу типов

Следующие данные могут обрабатываться в том же вызове recvmsg, нарушая правило одного типа. В оценке CVSS Linux указаны возможная утечка данных из кучи ядра в пользовательское пространство, повреждение памяти и удалённые сбои ядра; это оценённые последствия, а не утверждение, что каждое затронутое устройство испытает все эффекты.

Шаг 5

Исправление проверяет семантический маркер

Новая проверка останавливается, если control содержит заданный тип не-DATA, независимо от числа байтов.

Исходный код: Уязвимый vs Защищённый вариант

Фрагмент net/tls/tls_sw.c, не полная функция. Исправление upstream использует для условия остановки маркер сообщённого типа, а не количество скопированных байтов. control == 0 означает, что тип не сообщался; заданный тип не-DATA останавливает этот вызов recvmsg. Это точное логическое изменение upstream-патча.

Фрагмент исходного кода: Исходный код: net/tls/tls_sw.c

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
copied = err;
if (len <= copied ||
    (copied && control != TLS_RECORD_TYPE_DATA) || rx_more)
    goto end;
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
copied = err;
if (len <= copied || rx_more ||
    (control && control != TLS_RECORD_TYPE_DATA))
    goto end;

Чек-лист по защите системы для инженеров

Источники