Files
utun2/doc/chat_audit_findings.txt
T

129 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Chat: исправление приглашений и проверка контрактов — 26.10.2026
Граница работы
В начале аудита по /home/user/utun-doc-audit-checklist.txt установлены два дефекта:
успех invite_link_decode без узла подключения и разыменование непроверенного
выделения cm_invite_wrap. После согласования архитектуры исправлен весь путь
команды JOIN; добавлены тесты контрактов, отказов и жизненного цикла.
Полный аудит всех модулей src/chat не завершён. chat_sync.h остаётся [~].
Реализация
1. invite_link.c/.h — единый C-кодек для headless, Qt и Kotlin/JNI.
Только версия 0x03, строгий канонический base64 с padding, полный разбор,
одинаковый ключ во всех блоках, 1..32 адреса, family=4/6, proto=1/2/6,
ненулевой порт, согласованный REALITY. Канал — 1..INT64_MAX, join_key —
любой ненулевой uint64. Нулевой первый байт публичного ключа допустим.
Строки ограничены, превышение длины отклоняется; скрытый NUL не принимается.
Успех decode полностью определяет InviteData; ошибка оставляет out прежним.
Encode и сериализация адресов также не меняют выход при отказе.
Максимум с NUL — INVITE_LINK_MAX=1552: 32 IPv6, REALITY и пароль127 байт.
Из frontend удалены отдельные реализации декодирования; Qt/Kotlin только
преобразуют результат C-кодека в своё представление.
2. chat_sync_join_link/offer_link выполняются только в uasync и принимают ссылку.
Промежуточные chat_invite/addrs_data/cm_invite_wrap удалены. NCD bootstrap
строится целиком, включая REALITY; ошибка освобождает все временные адреса.
Общий объект локальной операции хранит роль, фазу, request/attempt, NCD и deadline.
PREPARE не реагирует на UP до установки handle; затем уже работающий транспорт
запускает обмен без ожидания повторного UP. Другой владелец сохраняет свой NCD.
BUSY относится к локальной операции; чужие JOIN обслуживаются независимо.
3. INFO_REQ/RESP, REQUEST, READY и ERROR несут attempt_id:8BE сразу после type.
JOIN_READY содержит также ровно8 байт ожидаемого group_id. Ответ сопоставляется
с peer/group/attempt/phase; повторное использование ссылки создаёт новый attempt.
Connection-узел хранит отдельные waiters попыток; повторный запрос не затирает
токен прежнего waiter. Есть deadline и ограничение8 waiters на peer; просроченные
удаляются при новых запросах и штатном обновлении кеша.
CHANNEL_INVITE переносит предложение и join_key; принимающий создаёт свой attempt.
Регистрация ключа A↔C и подписанные записи участников не используют attempt_id.
Сетевой формат JOIN изменён без поддержки старых узлов, согласно devel-контракту.
4. chat_join_port — отдельная удерживаемая ссылка frontend на вход команд.
Submit копирует ссылку, резервирует весь posted_task до принятия команды и
публикует без дополнительного выделения памяти. Закрытие и публикация защищены
mutex. Закрытый порт не содержит instance/uasync и не оживает при restart.
Close отменяет ожидающие команды; уже извлечённый uasync batch исполнит только
освобождение. Arg встроен в выделение task, повторного освобождения нет.
Qt и instance_lite публикуют/удерживают порт под собственным mutex; GUI/JNI
не читают chat_sync из внешнего потока. Chat stop закрывает приём до workers.
Request интерфейса не равен сетевому attempt. Headless использует request=0,
сохраняя собственную корреляцию JSON-команд отдельно от GUI.
5. Завершение: отделение локальной операции, снятие таймеров, освобождение своего
handle, затем единое событие peer:8/result:4/channel:8/request:8 (native).
Обработчик события может сразу начать новую попытку. Ранний отказ core не даёт
события; мост завершает свою уже принятую команду этим отказом. Принятая core
попытка получает один результат, включая cancel/delete/stop.
Cancel не откатывает членство, которое уже сохранил другой узел, и не удаляет
принятый NCD snapshot или рабочие линки других владельцев.
6. JOIN_INFO_RESP: подписанное описание и member/Merkle записываются в одной SQL
транзакции. Проверяется привязка connection-member к транспортному peer и
неизменность уже известных канальных ключей. Ошибка откатывает SQL и не создаёт
CHAT-группу. После commit публикуются runtime-ресурсы и уведомления member_sync.
Предыдущая семантика отложенной проверки неизвестного родителя сохранена;
успешный apply не называется подтверждением всей цепочки приглашений.
Ensure_channel_ready возвращает ошибку и сохраняет необходимый порядок:
группа → подписка → db_sync. При отказе убираются только новые runtime-ресурсы.
Проверенные данные после commit сохраняются для повторной подготовки.
Ключи приглашения, пароль и исходная ссылка не печатаются в новых логах.
Тесты
- test_invite_codec: 116342 проверки. Roundtrip1..32 адресов IPv4/IPv6/mixed/REALITY,
пароль127, SNI63, максимальная ссылка1551 байт; точный буфер и буфер на байт меньше,
сохранность выходов, усечения, алфавит всех256 байтов, старые версии, конфликт
ключей блоков, неизвестные/отсутствующие поля и границы значений.
- test_chat_join_faults: настоящий controller/NCD/SQLite, перехват только доставки
пакетов и точек отказа. Каждая аллокация подготовки, включая REALITY, отказы
transport/info/ready deadlines, send и NCD; повтор после отказа и чужое владение.
Запоздавшие ERROR/READY, чужой group/короткий READY, повтор результата, BUSY,
вложенный UP во время PREPARE и новый JOIN из обработчика завершения.
SQL-trigger и неверная подпись member откатывают канал; отказы group/db_sync/
подписки после commit сохраняют проверенные данные и убирают runtime-ресурсы.
Узел обслуживает чужой JOIN во время своей попытки. Проверены queued/active
дубликаты request, producer другого потока, старый порт после restart,
stop между копированием аргументов и публикацией (condvar-барьер), stop внутри
уже извлечённого batch. После teardown число u_malloc-блоков возвращается к исходному.
- test_chat_join_e2e: настоящий обмен A!=C, A==C, неверный ключ.
- test_dm_invite: приглашение из DM и реальное вступление.
- test_services: независимый lifecycle сервисов.
- test_chat_sync_schedule: фикстуры приведены к наличию активной попытки/waiter;
подтверждается синхронизация пары после JOIN_READY без нового транспортного UP.
- Qt test_invite_links/test_local_invite_ui: ссылки и UI на общем кодеке.
Результаты окончательных прогонов и ограничения приводятся ниже.
Итоговые результаты
Linux check.sh: 196/196 PASS, 0 failed, 0 skipped; proxy9/9, burst1/1,
load8/8, 134217728 проверенных байт, 190 секунд.
Лог: /tmp/chat-join-complete-check.log.
Отдельный финальный test_chat_join_faults с condvar-гонкой и вложенным UP PASS;
лог: /tmp/chat-join-race-final.log. Тот же вариант включён в окончательный check.sh.
ASAN/UBSan/LSan: 6/6 PASS, detect_leaks=1, halt_on_error=1.
Кодек, отказы JOIN, настоящий E2E, DM invite, services и планировщик.
В изолированной копии /tmp/utun-chat-join-sanitizers согласованно инструментированы
lib/src/Opus. Правка флагов Opus выполнена только в копии: рабочая сборка не смешивается
с sanitizer-объектами. Сводка: /tmp/chat-join-sanitizer-results.log;
по каждому набору: /tmp/chat-join-sanitizer-test_*.log.
Desktop CMake build PASS: /tmp/chat-join-desktop-verified-build.log.
Qt invite/local_invite: 2/2 PASS, /tmp/chat-join-desktop-verified-tests.log.
Android assembleDebug --offline PASS (JNI, Kotlin и APK; без установки на устройства):
/tmp/chat-join-android-verified-build.log.
Дополнительный старый Linux CMake-каркас Android headless не собрался из-за отсутствия
include speex/speex_echo.h в его собственном списке путей; этот отдельный каркас
не заменяет успешную APK-сборку и native Linux-фикстуры основного набора.
Windows/FreeBSD native build/runtime не выполнялись. git diff --check PASS.
Промежуточные отказы проверок устранены: размер size_t и порядок _GNU_SOURCE в
instance_lite, старое объявление InviteDataC в JNI; затем фикстура планировщика
обновлена под обязательные waiter/активную попытку. Тесты runtime-отката выявили
зависимость db_sync от группы; требуемый порядок восстановлен, добавлена очистка
новой группы при отказе истории/подписки. Это покрыто отрицательными сценариями.
Исходные изменения аудита и итоговые правки не коммитились и не публиковались.