diff --git a/AUDIT_FINDINGS.txt b/AUDIT_FINDINGS.txt new file mode 100644 index 00000000..08d3b428 --- /dev/null +++ b/AUDIT_FINDINGS.txt @@ -0,0 +1,253 @@ +======================================================================== + uTun3 AUDIT FINDINGS — Сводный отчёт +======================================================================== + + Аудит от 14.05.2026 — обход основных модулей + (use-after-free, out-of-bounds, утечки, reentrancy) + +======================================================================== +HIGH (19 проблем) +======================================================================== + +1. u_async.c:137-165 use-after-free + Частичный успех u_realloc в socket_array_add_internal: если часть + реаллоков прошла, а часть нет — старые массивы уже освобождены, + новые не выделены, sa->sockets/sa->fd_to_index/sa->index_to_fd + указывают на освобождённую память. + +2. u_async.c:842,1167 логическая ошибка + process_posted_tasks никогда не вызывается на Linux. + В epoll-пути: drain_wakeup_pipe не вызывает handle_wakeup/process_posted_tasks. + В poll-пути: wakeup-слот (i==0) обрабатывается с drain_wakeup_pipe и continue, + минуя зарегистрированный коллбэк. + Следствие: uasync_post из другого потока не доставляет задачи — posted_tasks + накапливаются бесконечно. + +3. u_async.c:1383,1398,1418 утечка памяти + При сбое создания wakeup (pipe/socket) в uasync_create не освобождаются: + ua->sockets, ua->timeout_heap, ua->timeout_pool, ua->epoll_fd. + +4. memory_pool.c:51-69 double-free + Нет детекта double-free. Повторный free создаёт цикл во freelist, + memory_pool_alloc выдаёт один и тот же блок двум потребителям. + +5. etcp.c:679-683 утечка памяти + input_queue_cb: при исчерпании inflight_pool вызывается + queue_entry_free(in_pkt) без queue_dgram_free(&in_pkt->ll). + dgram из data_pool утекает. + +6. etcp_connections.c:87,876-926 use-after-free + burst_resp_timer не отменяется в etcp_link_close. + Коллбэк burst_resp_timeout_cb срабатывает на освобождённом link. + +7. etcp_connections.c:846-854 use-after-free + stats_timer не отменяется при провале insert_link в etcp_link_new. + link_stats_timer_cb срабатывает на освобождённом link. + +8. etcp_connections.c:879-886 use-after-free + Ветка conn==NULL в etcp_link_close пропускает отмену ВСЕХ таймеров + (stats_timer, init_timer, shaper_timer, keepalive_timer, burst_resp_timer). + Любой из них сработает на освобождённом link. + +9. etcp_connections.c:1388,1531,1561 out-of-bounds read + INIT request парсинг: проверка pkt_len < 12 должна быть pkt_len < 26 + (23 байта заголовка). Пакет с длиной 12-25 пройдёт проверку, + но читает за границей расшифрованных данных. + +10. pkt_normalizer.c:33 null deref + if (!pn) pn->alloc_errors++ — разыменование NULL после u_calloc. + +11. pkt_normalizer.c:300 null deref + pn_buf_renew в цикле without проверки возврата → memcpy(NULL, ...) + при сбое memory_pool_alloc. + +12. eim_nat.c:80-93 логическая ошибка + Переполнение uint16_t порта: next_port с 65535→0, затем порты + ниже port_start выделяются (включая порт 0). Соединения нерабочие. + +13. eim_nat.c (весь файл) логическая ошибка + Нет механизма истечения/удаления NAT-записей. Таблица навсегда + исчерпывается при долгой работе, eim_nat_egress начинает отказывать. + +14. route_bgp.c:749,762-766 утечка памяти + route_bgp_process_nodeinfo: при пересоздании nodeinfo1 старая + очередь paths сохраняется в локальную переменную, но затем + перезаписывается новой queue_new() — утечка. + +15. route_bgp.c:904-907 утечка памяти + route_bgp_send_nodeinfo: при провале etcp_send не освобождаются + p (u_malloc) и e (queue_entry_new). + +16. route_ping.c:126-134 утечка памяти + route_ping_cancel_for_conn: nat_check_arg (p->arg, u_calloc) не + освобождается. В нормальном и таймаутном путях коллбэк освобождает, + но cancel-путь коллбэк не вызывает. + +17. ll_queue.c:321,439 reentrancy + Синхронный вызов коллбэка при первом queue_data_put (count==1) + без отложенного возобновления через uasync → рекурсивный + ack_timeout_check с устаревшим указателем current. + +18. secure_channel.c:379,711 потенциальный OOB write + sc_encrypt пишет в caller buffer без параметра capacity. + Контракт API опасен: вызывающий должен сам гарантировать размер буфера. + +19. secure_channel.c:432,756 потенциальный OOB write + sc_decrypt — аналогично, нет проверки capacity выходного буфера. + + +======================================================================== +MEDIUM (18 проблем) +======================================================================== + +20. u_async.c:591-639 use-after-free + Stale handle в uasync_cancel_timeout/uasync_call_soon_cancel. + Нет инвалидации (generation counter), пул может переиспользовать память. + +21. memory_pool.c:8,38,61,95 race condition + Глобальная g_total_free изменяется без блокировок, общая для всех пулов. + +22. memory_pool.c:51-69 потенциальный OOB write + Нет проверки принадлежности блока пулу. Free в чужой пул вызывает + memset с чужим object_size — частичная очистка или запись за границу. + +23. timeout_heap.c:43-45 утечка / несоответствие документации + Документация обещает free() при free_callback==NULL, код этого не делает. + +24. etcp.c:718-763 reentrancy + Вложенный ack_timeout_check (через input_send_q_cb→etcp_request_pkt→ + wait_ack_cb) обрабатывает не тот entry, т.к. outer loop использует + current, который мог быть перемещён/удалён внутренним вызовом. + +25. etcp.c:747,944 утечка / дрейф счётчиков + Непроверенный queue_data_put_with_index: при провале счётчики + (retransmissions_count, inflight_bytes) не откатываются. + +26. etcp_connections.c:1731,1763 out-of-bounds read + INIT_RESPONSE парсинг: нужно >=19 байт, проверяется только >=3. + +27. etcp_connections.c:748-753 use-after-free + бесконечный цикл + etcp_socket_remove: при link->conn==NULL remove_link пропускается, + num_channels не уменьшается, цикл перечитывает освобождённый conn->links[0]. + +28. etcp_connections.c:1736-1738 логическая ошибка + link_status перезаписывается значением remote_keepalive, игнорируя + локальный recv_keepalive. Временно помечает link UP даже когда + локальный приём мёртв. + +29. pkt_normalizer.c:43 integer overflow/underflow + frag_size = mtu - ACK_REZERV - UDP_HDR_SIZE - UDP_SC_HDR_SIZE. + При mtu < 158 беззнаковое вычитание даёт ~65379 → огромные буферы. + +30. pkt_normalizer.c:240 утечка памяти + При провале queue_data_put фрагмент и dgram не освобождаются. + +31. eim_nat.c:97,173 null deref + Нет проверки ctx->initialized/ctx->table в egress/ingress. + +32. eim_nat.c:268,293 логическая ошибка + Статические пробросы (port forward) вне port range недоступны. + +33. secure_channel.c:66 key hygiene + Нет sc_free_ctx — сессионный ключ не зануляется при уничтожении контекста. + +34. secure_channel.c:322,620 key hygiene + ECDH shared secret остаётся на стеке незанулённым после + sc_derive_session_key. + +35. secure_channel.c:352,680 nonce entropy + При постоянном провале random_bytes seed остаётся 0 (static init). + Nonce становится полностью предсказуемым (counter+timestamp). + +36. route_bgp.c:1122 утечка памяти + route_bgp_send_nat_info: etcp_send без проверки → pkt/e утекают при провале. + +37. route_bgp.c:1157 утечка памяти (аналогично #36) + route_bgp_send_nat_check_req: etcp_send без проверки. + +38. config_parser.c:1055-1066 логическая ошибка + update_config_keys дописывает [global] секцию в конец файла. + При повторном вызове получается дубликат секции, парсер может сбойнуть. + + +======================================================================== +LOW (13 проблем) +======================================================================== + +39. u_async.c:1594-1617 утечка (abort) + abort() в destroy до освобождения posted_tasks и мьютекса. + +40. u_async.c:1608 race condition + posted_tasks_head читается без posted_lock в destroy. + +41. memory_pool.c:47 truncation + size_t → uint32_t в u_calloc_impl (object_size > UINT32_MAX). + +42. memory_pool.h:16 signedness + int free_count может обернуться при двойном освобождении. + +43. timeout_heap.c:31 dead code + freed_count никогда не обновляется, timeout_heap_get_freed_count не реализован. + +44. timeout_heap.c:66-67 integer overflow + capacity*2 и sizeof(TimeoutEntry)*new_cap могут переполниться. + +45. ll_queue.c:106-127 dangling pointer + queue_free освобождает hash_table, но не чистит entry->hash_next. + +46. pkt_normalizer.c:374 десинхронизация потока + При провале ll_alloc_lldgram 2 байта заголовка уже consumed, + но фрагмент сброшен → потеря данных. + +47. eim_nat.c:105,181 unaligned access (ARM) + *(uint16_t*)(ip_data + 6) — невыровненный доступ на ARM. + +48. secure_channel.c:108-131 избыточность + CRC32 поверх CCM (аутентифицированное шифрование) избыточен. + +49. secure_channel.h:51-52 dead code + Неиспользуемые поля send_nonce[13]/recv_nonce[13]. + +50. config_parser.c:312 undefined behavior + ~0U << (32 - cidr) при cidr==0 — сдвиг на ширину типа. + +51. route_ping.c:227 integer overflow + uint16_t avg_rtt = timeout_ms * 10 — переполнение при timeout_ms > 6553. + + +======================================================================== +Файлы без ошибок +======================================================================== + +- route_node.c — чисто +- tun_if.c — чисто +- etcp_api.c — только порядок queue_entry_free/dgram_free (LOW, см. etcp.c Issue #7 старого аудита) +- etcp_loadbalancer.c — не аудирован (250 строк) + + +======================================================================== +Рекомендуемый порядок исправления +======================================================================== + +1. КРИТИЧЕСКИ ФУНКЦИОНАЛЬНЫЕ: + u_async.c #2 (posted_tasks не работают на Linux) + u_async.c #3 (утечки при сбое старта) + +2. USE-AFTER-FREE: + etcp_connections.c #6,#7,#8,#5 (висячие таймеры) + u_async.c #1 (частичный realloc) + ll_queue.c #17 (reentrancy) + +3. УТЕЧКИ ПАМЯТИ: + etcp.c #5 (dgram из data_pool) + route_bgp.c #14,#15,#36,#37 + route_ping.c #16 + +4. OOB / NULL DEREF: + etcp_connections.c #9,#26 (INIT bounds) + pkt_normalizer.c #10,#11 (null deref) + +5. ЛОГИКА: + eim_nat.c #12,#13 (порты + expiry) + memory_pool.c #4 (double-free) + config_parser.c #38 (дубликат секции)