В ядре Linux устранена следующая уязвимость:
tcp: восстановить льготный период RCU в tcp_ao_destroy_sock
Зафиксируйте 51e547e8c89c («tcp: свободная информация/ключи TCP-AO/TCP-MD5 без RCU»)
удалил обратный вызов call_rcu() из tcp_ao_destroy_sock(), утверждая, что
«уничтожение информации/ключей откладывается до тех пор, пока не будет вызван деструктор сокета»
и поэтому «никто больше не может это обнаружить». Этот аргумент не справедлив для сайта вызова в tcp_connect().
(net/ipv4/tcp_output.c:4327-4332). В этот момент сокет находится в
TCP_SYN_SENT уже был вставлен в хэш инета
inet_hash_connect() в tcp_v4_connect() и, следовательно, очень
Обнаруживаемый: любой программный прерывание, выполняющий tcp_v4_rcv() на другом процессоре, может занять
сокет из ehash, войдите в tcp_inbound_hash() и загрузите
tp->ao_info через неявный RCU до того, как будет задействован bh_lock_sock_nested()
разрушающий процессор.
Затем путь чтения входит в __tcp_ao_do_lookup() (net/ipv4/tcp_ao.c:208).
который перезагружает tp->ao_info через rcu_dereference_check(); перезагрузка может
по-прежнему наблюдайте за указателем (вот-вот освободимся), потому что нет
sync_rcu() между rcu_assign_pointer(tp->ao_info, NULL) и
tcp_ao_info_free() в tcp_ao_destroy_sock(). Захваченный указатель
затем перешел к строке 223:
hlist_for_each_entry_rcu(ключ, &ao->head, node, ...)
Синхронный метод kfree() писателя может выполняться между строками
218 повторная выборка и итерация hlist строки 223. Плита используется повторно
(или просто с отметкой LIST_POISON1, если еще не использовался повторно) и итерация
Проходит контролируемую злоумышленником или отравляет память в контексте softirq.
Воспроизводитель (без отладочной прокладки, стандартный x86_64 v7.1-rc2 SMP+KASAN, QEMU+KVM):
непривилегированный процесс uid=1000 внутри CLONE_NEWUSER|CLONE_NEWNET
устанавливает TCP_MD5SIG + TCP_AO_ADD_KEY в TCP-сокет, распыляет подделку
TCP-AO сегментируется к своему возможному 4-кортежу через необработанные сокеты, а затем
вызывает метод Connect(). Срабатывает согласование md5-wins в tcp_connect().
tcp_ao_destroy_sock(); программа чтения журнала невыполненных прерываний softirq на обратной связи
Путь NAPI аварийно завершает работу на освобожденном пути ao->head.first:
Упс: общая ошибка защиты, возможно, для неканонических
адрес 0xfbd59c000000002f
КАСАН: возможно, доступ к дикой памяти в пределах досягаемости
[0xdead000000000178-0xdead00000000017f]
ЦП: 0 UID: 1000 PID: 100 Связь: repro_userns
RIP: 0010:__tcp_ao_do_lookup+0x107/0x1c0
Отслеживание вызовов: <IRQ>
__tcp_ao_do_lookup+0x107/0x1c0
tcp_ao_inbound_lookup.constprop.0+0x12a/0x200
tcp_inbound_ao_hash+0x5ea/0x1520
tcp_inbound_hash+0x7ce/0x1240
tcp_v4_rcv+0x1e7a/0x3e10
... Восстановите льготный период RCU: повторно добавьте структуру rcu_head в tcp_ao_info.
и замените синхронный tcp_ao_info_free() на call_rcu().
обратный вызов.
Читатели, которые захватили tp->ao_info до rcu_assign_pointer
Обнулив его, теперь видим, что объект остается действительным до rcu_read_unlock(). После применения патча репродуктор работает без проблем в течение 2000 итераций.
на той же сборке ядра.
Показать оригинальное описание (EN)
In the Linux kernel, the following vulnerability has been resolved: tcp: restore RCU grace period in tcp_ao_destroy_sock Commit 51e547e8c89c ("tcp: Free TCP-AO/TCP-MD5 info/keys without RCU") removed the call_rcu() callback from tcp_ao_destroy_sock(), arguing that "the destruction of info/keys is delayed until the socket destructor" and therefore "no one can discover it anymore". That argument does not hold for the call site in tcp_connect() (net/ipv4/tcp_output.c:4327-4332). At that point the socket is in TCP_SYN_SENT, has already been inserted into the inet ehash by inet_hash_connect() in tcp_v4_connect(), and is therefore very much discoverable: any softirq running tcp_v4_rcv() on another CPU can take the socket out of the ehash, walk into tcp_inbound_hash(), and load tp->ao_info via implicit RCU before bh_lock_sock_nested() is taken on the destroying CPU. The reader path then enters __tcp_ao_do_lookup() (net/ipv4/tcp_ao.c:208) which re-loads tp->ao_info via rcu_dereference_check(); the re-load can still observe the (about-to-be-freed) pointer because there is no synchronize_rcu() between rcu_assign_pointer(tp->ao_info, NULL) and tcp_ao_info_free() in tcp_ao_destroy_sock(). The captured pointer is then walked at line 223: hlist_for_each_entry_rcu(key, &ao->head, node, ...) The writer's synchronous kfree() is free to complete between the line 218 re-fetch and the line 223 hlist iteration. The slab is reused (or simply LIST_POISON1-stamped if not yet reused) and the iteration walks attacker-controlled or poison memory in softirq context. Reproducer (no debug shim, stock x86_64 v7.1-rc2 SMP+KASAN, QEMU+KVM): an unprivileged uid=1000 process inside CLONE_NEWUSER|CLONE_NEWNET installs TCP_MD5SIG + TCP_AO_ADD_KEY on a TCP socket, sprays forged TCP-AO segments toward its eventual 4-tuple via raw sockets, then calls connect(). The md5-wins reconciliation in tcp_connect() fires tcp_ao_destroy_sock(); the softirq backlog reader on the loopback NAPI path crashes on the freed ao->head.first walk: Oops: general protection fault, probably for non-canonical address 0xfbd59c000000002f KASAN: maybe wild-memory-access in range [0xdead000000000178-0xdead00000000017f] CPU: 0 UID: 1000 PID: 100 Comm: repro_userns RIP: 0010:__tcp_ao_do_lookup+0x107/0x1c0 Call Trace: <IRQ> __tcp_ao_do_lookup+0x107/0x1c0 tcp_ao_inbound_lookup.constprop.0+0x12a/0x200 tcp_inbound_ao_hash+0x5ea/0x1520 tcp_inbound_hash+0x7ce/0x1240 tcp_v4_rcv+0x1e7a/0x3e10 ... Restore the RCU grace period: re-add struct rcu_head to tcp_ao_info and replace the synchronous tcp_ao_info_free() with a call_rcu() callback. Readers that captured tp->ao_info before rcu_assign_pointer NULLed it now see the object remain valid until rcu_read_unlock(). With the patch applied the reproducer runs cleanly for 2000 iterations on the same kernel build.
Характеристики атаки
Последствия
Строка CVSS v3.1