diff --git a/doc/media_async_audit_findings.txt b/doc/media_async_audit_findings.txt new file mode 100644 index 00000000..5725360d --- /dev/null +++ b/doc/media_async_audit_findings.txt @@ -0,0 +1,70 @@ +Аудит media_async / media_delivery — 2026-10-24 +Статус: частичный; остановлен на подтверждённом дефекте по AGENTS.md. +Код модулей не изменён. Сборка Linux и 20 профильных тестов прошли. + +Подтверждённый дефект: исходник уничтожается через другое имя того же файла + +encrypted_file_write, src/media_async/encrypted_file.c:79-87, сравнивает только строки +source/destination. Разные строки могут обозначать один inode. fopen(destination,"wb") +усекает уже открытый source. Криптография затем успешно обрабатывает пустой файл. +Контейнер корректен для нулевого содержимого, но исходные данные потеряны; возврат равен 0. + +Воспроизведено на настоящих функциях из src/libutun.a: +- source и source с компонентом /./: исходные30 байт заменены контейнером152 байта, + plaintext_size=0, rc=0, SHA256 исходника изменился; +- source и hardlink: тот же результат; +- encrypted_file_read тоже пропускает оба вида alias: контейнер182 байта заменяется + plaintext30 байт, rc=0, исходный ciphertext больше не существует в прежнем виде. + +У read такая же проверка strcmp в строке118 и разрушающий fopen в строке142. +В текущем групповом pipeline вызывающий выбирает отдельный .part; воспроизведение +доказывает дефект файлового API, а не наблюдавшуюся потерю реального пользовательского вложения. +Ошибку нельзя замаскировать описанием одинаковых файлов как успешного no-op: +шифрование и расшифровка должны создавать другое содержимое. + +Диагностика: +- /tmp/media-encrypted-alias-repro.c — четыре проверки, сверяются inode, размеры и SHA256; +- /tmp/media-encrypted-alias-repro.mk — использует цели/библиотеки/флаги tests/Makefile; +- /tmp/media-encrypted-alias-repro.log — полный журнал с media=debug; +- /tmp/media-encrypted-alias-lvVS77/ — сохранённые входы и результаты опыта. +Проектные .c/.h для воспроизведения не менялись. + +Рекомендуемое исправление + +1. У обеих функций явно закрепить контракт: source остаётся неизменным; + destination — отдельный временный файл вызывающего, который допустимо удалить при ошибке. + Файлы/каталоги не должны подменяться во время вызова; это тот же контракт стабильности, + что у существующих file_io-примитивов. +2. До открытия назначения с усечением проверить идентичность через общий file_io_paths. + Результат1 (один файл) здесь означает ошибку. Отдельно проверять успешное открытие + источника до создания назначения. Использовать общие UTF-8/regular-file примитивы, + не дублировать POSIX/Windows сравнение идентичности в encrypted_file. +3. Ранняя ошибка не должна изменять source, удалять его alias или менять существующее + назначение. Сообщение об отказе должно назвать операцию/пути и причину до усечения. +4. Сохранить нынешнее владение: chat_attachment/dm_media публикуют результат после work. + Внутри encrypted_file не нужен второй независимый слой публикации или новый async owner. +5. Добавить регрессии write/read: совпадающая строка, /./, hardlink, конечная ссылка; + при отказе проверять неизменность файлов и отсутствие удаления alias. Проверить + отсутствующий источник и размеры0/1/32768/32769, обычные roundtrip и отмену pipeline. +6. Прогнать профильные тесты, ASAN/UBSan и полный набор; затем продолжить аудит + остальных контрактов обоих каталогов. Формат контейнера и nonce менять не требуется. + +Что проверено и что осталось + +Прочитаны все шесть пар .c/.h media_async, целиком file_transfer.c/h, заголовки +media_delivery/download/index/proto, sync.md и doc/encrypted_attachments.md. +Полная повторная сверка остальных реализаций media_delivery и исправление контрактов +ещё не выполнены. Прежние [V] в общем checklist относятся к предыдущим аудитам; +новые незавершённые пункты отмечены [~]. + +Linux: ./build.sh --no-gui -j4 — PASS;20/20 профильных тестов — PASS: +media_primitives, attachment, attachment_send, encrypted_file, encrypted_attachment, +media_async, media_index, media_index_regressions, media_delivery_sql, +media_delivery_download, media_download_timeout, media_query, media_blocks, +media_replication, media_delivery_integration, media_delivery_full, +media_delivery_chat, dm_media, dm_worker, services. +Логи: /tmp/media-audit-build.log, /tmp/media-doc-audit-results.log, +/tmp/media-doc-audit-logs/. Штатный test_encrypted_file не покрывает alias. +Старые сбои encrypted_attachment/media_delivery_chat в этом прогоне не воспроизвелись; +один PASS не закрывает задачи о нестабильности. +Полный check.sh и новые sanitizer-прогоны в этом аудите не запускались. diff --git a/doc/tasks.md b/doc/tasks.md index 2f0dd49e..3c5868bd 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -4,6 +4,17 @@ ## Текущие задачи +[ ] **media_async encrypted_file: не уничтожать source через alias назначения** — подтверждено 24.10.2026. + write/read отвергают только одинаковые строки; /./ и hardlink обходят проверку одного файла. + write возвращает0, заменяя исходные30 байт контейнером пустого файла; read возвращает0, + заменяя входной ciphertext182 байта plaintext30 байт. Проверены реальные функции, inode и SHA256. + До fopen(wb) проверять идентичность общими file_io-примитивами; один файл — ошибка без изменения/удаления. + Сохранить destination как временный файл вызывающего, публикацию оставить chat_attachment/dm_media. + Детали, проверка архитектуры исправления и незавершённый аудит: [media_async_audit_findings.txt](media_async_audit_findings.txt). + Воспроизведение: /tmp/media-encrypted-alias-repro.c, /tmp/media-encrypted-alias-repro.log. + Linux-сборка и20/20 профильных тестов PASS; штатный test_encrypted_file не проверяет alias. + /tmp/media-doc-audit-results.log. Код модулей не изменён; дальнейший аудит остановлен по AGENTS.md. + [+] **Proxy: исправить HUP/backpressure, владение и rollback** — завершено 24.10.2026. Согласованный проект, причины и результаты: [doc/proxy_fix_plan.txt](proxy_fix_plan.txt). HUP больше не обходит read_high_water/ручную паузу; idle HUP снимается с poll,