Files
utun2/doc/media_async_audit_findings.txt
T

531 lines
57 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.
Аудит media_async / media_delivery — 2026-10-24
Статус: alias encrypted_file, граница voice_file, переанонс SQLite и расхождение MIME write/read исправлены.
Сверка двух каталогов завершена: все11 заголовков, sync.md, _desc.txt и encrypted_attachments.md.
Общий service_lifecycle.md проверен в media-части; разделы других сервисов не считаются закрытыми.
Ниже сохранена хронология воспроизведений и исправлений; прежние отметки незавершённости относятся к этапам.
Последние проверки MIME и итоговая архитектурная сверка — в конце.
Подтверждённый дефект: исходник уничтожается через другое имя того же файла
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 для воспроизведения не менялись.
Согласованная архитектура исправления
Одной проверки file_io_paths перед fopen(wb) недостаточно для полного исправления
контракта владения. Сейчас encrypted_file удаляет destination при ошибке, а
ak_file_done повторно удаляет заранее вычисленный .part даже при раннем отказе.
Нужно убрать открытие произвольного назначения с усечением и очистку по одному имени.
Граница модулей
- lib/file_io: UTF-8/regular-file проверки, идентичность, эксклюзивный temp,
flush/sync/close, atomic rename, владение temp и защита от orphan cleanup.
- encrypted_file: создание/проверка контейнера в собственном temp; без публикации
конечного имени, SQLite и состояния async-задачи.
- chat_attachment: задача владеет temp, выбирает конечное имя и выполняет одну
публикацию; completion меняет состояние БД/UI и освобождает только свои ресурсы.
Подготовка, sync и rename остаются в worker, как сейчас; новые файловые I/O в uasync не добавляются.
Предлагаемый контракт write/read
1. Функция получает source, конечный target_path и указатель на обнулённый file_io_temp.
target_path задаёт место будущего результата; он не открывается с усечением.
До начала работы file_io_paths(source,target_path) обязан вернуть 0.
Совпадение строки, /./, hardlink и конечная ссылка назначения отклоняются без изменений.
2. Источник открывается общим file_io_open_read. Только после успешного открытия
создаётся уникальный temp рядом с target_path через file_io_temp_open.
На POSIX он имеет права 0600; создание эксклюзивное, владелец сразу учтён в общем реестре.
3. Контейнер пишется/проверяется через уже открытый FILE этого temp. Нет второго .part,
второго открытия выхода по пути или дополнительного слоя публикации внутри codec.
4. Полностью проверяются данные, закрытие входа и durable завершение выхода.
При ошибке удаляется только созданный этой операцией temp; существующие source/target сохраняются.
При успехе возвращается закрытый, синхронизированный temp, который ещё не опубликован.
Результат info/type у read присваивается только после полного успешного завершения подготовки.
5. Владелец file_io_temp размещается прямо в ak_file и имеет стабильный адрес до done;
копировать его структуру нельзя: реестр хранит адрес владельца.
Функции не принимают уже активный output и не уничтожают его при отказе аргументов.
Единая политика durability
В file_io_temp_open добавить flags, в file_io_temp хранить выбранный режим на весь
срок операции. FILE_IO_DURABLE включает обе части: finish и publish.
Обычные пользователи передают 0 и сохраняют свою семантику; отдельные флаги в каждом
finish/publish не нужны. Это предотвращает случайное сохранение файла без сохранения имени.
Durable finish: fflush -> fsync/_commit того же открытого fd -> fclose. Ошибка любой
фазы сохраняет первый код ошибки; close выполняется и при отказе sync. Открывать путь
заново ради fsync не нужно. После отказа temp остаётся у владельца до discard.
Publish повторно проверяет идентичность source/target и принимает только закрытый temp.
На POSIX: rename и fsync каталога назначения; соседний temp находится в том же каталоге.
На Windows: общий wide API и MOVEFILE_WRITE_THROUGH. Родительский каталог вычисляется
без обрезания: имя без '/' означает '.', '/file' означает '/'.
Политика sync принадлежит file_io, а не формату зашифрованного контейнера.
Точка фиксации — успешный rename. До неё ошибка сохраняет старый target.
После rename ошибка fsync каталога означает: результат уже установлен, долговечность
имени не подтверждена. Нельзя обещать откат, удалять новый файл вслепую или восстанавливать
старое содержимое. В этот момент temp уже отсоединён от реестра и его имя в owner пусто.
Этот переход должен быть явно описан в file_io.h и проверен тестом.
Длительный fsync выполняется после снятия блокировки реестра временных файлов.
Владение задачи и отмена
ak_file хранит file_io_temp вместо temporary[...]. Worker после успешной подготовки
один раз вызывает file_io_temp_publish с тем же target и источником.
До publish поле temporary непустое; после успешного rename оно пустое даже при
ошибке directory sync. По этому переходу задача фиксирует факт своей публикации.
Completion всегда может вызвать discard: после rename он ничего не удаляет.
Cleanup при отмене/удалении канала учитывает отдельно действительно созданные/
заменённые артефакты. Вычисленный путь не является доказательством владения файлом.
При отказе до создания выхода нельзя удалять существующий target или его alias.
Отдельных async owners, refcount и фоновых потоков не требуется. Ключ по-прежнему сохраняется
до шифрования; ready получателя/публикация сообщения подтверждаются только после
успешного worker и проверки актуальности канала в completion. Авторская строка ключа
сохраняется до worker по прежним правилам. Общей транзакции ФС/SQLite нет: после rename
путь остаётся известен attachment_keys для последующей очистки/повторной операции.
Кеш и восстановление после crash
Активный temp защищён существующим file_io-реестром, в том числе после finish.
В chat_msg убрать специальное сохранение вычисленного cipher_path.part; конечный
cipher_path по-прежнему защищается attachment_keys, временный файл — своим owner.
После crash реестра нет: такие temp допустимо удалить.
Нельзя ограничиться нынешним orphan walk дерева media: plaintext-temp находится
в decrypted, а ak_recover_preparations знает только фиксированный .part.
В init/очистке после завершения workers нужно удалять оставшиеся временные файлы
строгого собственного формата <UUID>.uenc.tmp.<32hex> / <UUID>.file.tmp.<32hex>
в управляемых каталогах media/decrypted. Использовать file_io_remove_orphan,
чтобы сохранить активные temp другого instance того же процесса. Конечные файлы,
ключи и данные иных форматов такой проход не удаляет; ошибка обхода логируется.
Область изменений
Основные файлы: lib/file_io.c/h, media_async/encrypted_file.c/h,
chat/chat_attachment.c, chat/chat_msg.c, test_encrypted_file/test_media_primitives
и сетевые проверки зашифрованных вложений. Остальные вызовы file_io_temp_open
в media_async/media_delivery/video переводятся на явные flags=0.
DM не вызывает encrypted_file_write/read: он использует media_crypto streams
и общие sync/publish helper-функции из encrypted_file.h. При правке общего слоя
необходимо сохранить их durable контракт; перенос общих операций в file_io должен
быть прямым, без совместимых обёрток и без изменения DM state machine.
Формат контейнера, nonce, протокол ключей и схема БД для этого фикса не меняются.
Регрессии и критерии готовности
- Обе операции: одинаковая строка, /./, hardlink, symlink, отсутствующий/необычный source;
source и прежний target не меняются, alias не удаляется, чужой temp не освобождается.
- Обычные файлы размером 0/1/32767/32768/32769 байт, большой файл, UTF-8 пути;
успешная подготовка не меняет target до publish. Read не публикует info/type при ошибке.
- Внедрение ошибок open/fdopen/read/write/seek/flush/fsync/input-close/output-close/
rename/directory-sync/RNG; отсутствие утечек fd/памяти/регистраций temp.
- Подмена target на hardlink между подготовкой и publish: повторная проверка
отклоняет публикацию, исходник сохраняется, удаляется только собственный temp.
- Отказ directory sync после rename: новый файл цел, temp-owner отсоединён,
ошибка не представляется как откат; повторная операция корректна.
- Orphan cleanup во время work, после finish, после publish; crash/restart
в обоих деревьях, чужие имена сохраняются. Отмена, stop и удаление канала
до work, после подготовки и после rename: один done, нет публикации сообщения/ready.
- Профильные 20 тестов, ASAN/UBSan/LSan и полный набор. Изменение file_io требует
также проверки его прежних пользователей: copy, video и media blocks/download.
Этот проект реализован. Успешные 20 тестов ниже относятся к исходной реализации;
проверки исправленного кода приведены в конце.
Что проверено и что осталось
Прочитаны все шесть пар .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-прогоны не запускались.
Результаты исправления — 2026-10-24
- file_io_temp получил единый FILE_IO_DURABLE: sync открытого fd при finish,
write-through rename на Windows и directory sync после rename на POSIX.
После rename temporary пуст, включая отказ directory sync; discard не удаляет результат.
Общие sync/publish функции перенесены в file_io; DM вызывает их напрямую.
- Write/read готовят отдельный закрытый temp. Source/target не открываются с усечением;
совпадающая строка, /./, hardlink и конечная ссылка отклоняются до создания выхода.
Read выдаёт info/type только после успешного закрытия/сохранения проверенного результата.
- ak_file хранит один output, учитывает подготовленный plaintext snapshot и выполненный rename.
Worker публикует один раз; completion очищает только собственные артефакты.
Кеш использует реестр владельцев; recovery очищает строгие temp в media и decrypted.
Построение путей и SQL recovery используют одну каноническую media_base.
- test_encrypted_file: прежние шесть размеров, восемь alias-сценариев обоих API,
активный output, alias перед publish и 23 отказа I/O с контролем fd/temp/метаданных.
До rename прежний target сохраняется; после отказа directory sync новый файл установлен.
- test_media_primitives: durable и обычный режим, sync failure до rename, failure после
rename, относительный/корневой каталог, защита активного temp в worker и после close.
- test_attachment_send детерминированно ждёт настоящего crypto rename без обработки
completion, затем останавливает сервис: нет сообщения/ключа/оставшихся файлов/temp.
- test_encrypted_attachment при каждом startup/restart проверяет оба дерева:
stale temp удалены; открытый/закрытый активные owners, конечные файлы и чужие имена сохранены.
- Профильный Linux-набор: 21/21 PASS, включая video/no_ffmpeg, DM, services и media delivery.
Основной make check: 185/185 PASS. TUN в отдельном network namespace с тем же бинарником:
proxy9/9, burst1/1, load8/8 PASS; 134217728 проверенных байт.
- ASAN/UBSan/LSan: 21/21 PASS с detect_leaks=1. Все встроенные зависимости собраны
с sanitizer-флагами в /tmp/utun-media-fix-sanitizers; обычные объекты проекта не смешивались.
Windows/FreeBSD build/runtime не проверялись.
Логи: /tmp/media-fix-profile-results.log, /tmp/media-fix-full-check.log,
/tmp/media-fix-network-check.log, /tmp/media-fix-sanitizer-results.log,
/tmp/media-fix-cancel-after-rename.log, /tmp/media-fix-encrypted-file.log.
Проверка реализации не выявила противоречий владения в перечисленных сценариях.
Частичные отметки остальных файлов аудита сохраняются; этот фикс не означает полного
завершения проверки media_async/media_delivery или закрытия старых нестабильных тестов.
Продолжение аудита — 2026-10-24: граница длительности voice_file
voice_file.h обещает максимум24ч; attachment_validate (attachment.c:35) отвергает
duration_ms >86400000. Однако voice_file.c:16 проверяет samples/channels/rate >86400:
целочисленное деление отбрасывает долю секунды. Записи с полными кадрами на24ч+20..980мс
проходят проверку аргументов. Затем voice_file.c:37 вычисляет точную длительность
в миллисекундах по числу20мс кадров, уже выходящую за предел метаданных.
attachment_send.c вызывает voice_file_encode в worker, а attachment_validate —
при публикации результата. При успешном полном кодировании такой результат будет
отклонён после подготовки файла. Это дефект границы записей длиннее суток;
сбой обычных коротких записей или потеря данных этим опытом не установлены.
Диагностическое воспроизведение на настоящем voice_file_encode из src/libutun.a:
- /tmp/media-voice-duration-repro.c и .mk; сборка через tests/Makefile и его библиотеки/флаги.
- Linux64: виртуальный read-only PCM достаточного размера, без чтения или заполнения.
Linker wrapper opus_codec_encoder_create фиксирует достижение кодировщика
и возвращает NULL до чтения PCM/открытия output. Реальная проверка аргументов не подменена.
- Частоты8000/12000/16000/24000/48000, mono/stereo; для каждой пары проверены
24ч-20мс, ровно24ч, +19мс хвоста, +20мс, +980мс и +1000мс: всего60 случаев.
- Ровно24ч и +19мс хвоста проходят, так как хвост короче полного кадра отбрасывается;
+1000мс отвергается до encoder. На +20/+980мс во всех10 сочетаниях codec достигнут,
тогда как attachment_validate соответствующей длительности возвращает-1:
duration_guard_mismatches=20, confirmed=1.
- Все60 вызовов возвращают-1: для допустивших encoder это намеренная инъекция отказа,
для +1000мс — штатная валидация. Опыт доказывает ошибку допуска к encoder,
но не является24-часовым кодированием или сквозной проверкой отправки такого файла.
- Полный журнал с media=info/debug=info: /tmp/media-voice-duration-repro.log;
сборка: /tmp/media-voice-duration-build.log. Проектные .c/.h не менялись.
Проект простого исправления:
1. Задать один ATTACHMENT_DURATION_MAX_MS=86400000 в attachment.h; использовать
в валидаторе voice/video и проверке video completion вместо отдельных литералов.
2. После проверки rate/channels считать frame_samples=(rate/50)*channels,
frames=count/frame_samples. До создания encoder/открытия файла отклонять
frames<25 либо frames>ATTACHMENT_DURATION_MAX_MS/20 (4320000).
Проверяется длительность записываемых полных кадров, хвост <20мс по-прежнему отбрасывается.
3. Только после ограничения frames считать samples=frames*frame_samples
и duration_ms=frames*20. Размер samples не превосходит count, длительность
помещается в uint32_t; большие count не требуют потенциально переполняющего умножения на20.
4. Сообщение раннего отказа различает неверные параметры и недопустимую длительность;
для длительности выводит count/rate/channels/frames и разрешённые границы.
Дополнительный helper/API или изменение pipeline/формата не нужны.
5. В test_attachment добавить проверку границ500мс/24ч, хвоста <20мс,
превышения на полный кадр, разных rate/channels и раннего отказа без изменения файла/метаданных.
Верхние границы проверять диагностической точкой до encoder без реального кодирования суток;
сохранить существующий настоящий Opus roundtrip короткой записи.
На этапе подтверждения аудит был остановлен по AGENTS.md:10, без изменения рабочего кода.
Пользователь согласовал исправление; результаты приведены ниже.
Результаты исправления voice_file — 2026-10-24
- ATTACHMENT_DURATION_MAX_MS в attachment.h задаёт один предел86400000мс:
его используют валидатор voice/video, video completion и кодировщик голоса.
- voice_file считает frames=count/frame и проверяет25..4320000 до создания Opus
и открытия выходного файла. Метаданные при раннем отказе не изменяются.
Хвост <20мс сохраняет прежнее поведение: отбрасывается, включая хвост после ровно24ч.
- Проверка архитектуры: frame всегда положителен и не больше1920 для разрешённых rate/channels;
деление count/frame не переполняется. После ограничения frames произведение frames*frame
не превосходит исходного count; frames*20 не превосходит86400000 и помещается в uint32_t.
Подготовка, публикация, владение PCM/файлом и формат контейнера не меняются.
- Лог раннего отказа содержит count/rate/channels/frames и разрешённые границы;
debug принятой записи показывает число кадров, длительность и отброшенный хвост.
- test_attachment: 120 граничных вызовов для пяти частот и mono/stereo.
Все70 недопустимых длительностей отвергнуты до encoder; все50 допустимых
дошли до диагностического encoder с инъекцией отказа. Во всех120 случаях
побайтно сохранены исходные metadata и содержимое существующего output.
Linux64 использует виртуальный read-only PCM; суточное кодирование не выполняется.
- Настоящий Opus encode/decode: прежняя1с запись и десять записей на500мс с хвостом
почти20мс, все rate/channels, все три preset. Проверены header, декодирование,
число20мс кадров, EOF и валидность метаданных результата.
- validate/encode/decode voice/video принимают86400000мс и отвергают86400001мс.
- ./build.sh --no-gui -j4 — PASS. В изменённых C-файлах/тесте предупреждений сборки нет;
общие прежние предупреждения остальных модулей остаются в полном логе.
- Профильные тесты10/10 PASS: attachment, attachment_send, encrypted_file,
encrypted_attachment, media_async, dm_media, dm_worker, services, video, video_no_ffmpeg.
Первый запуск attachment внутри sandbox выдал запреты создания stream fd;
весь профильный набор, включая attachment, успешно повторён вне sandbox.
Логи: /tmp/media-voice-fix-build.log, /tmp/media-voice-fix-profile-results.log,
/tmp/media-voice-fix-profile-logs/test_attachment.log и остальные логи этого каталога.
Новый полный make check и ASAN/UBSan/LSan не запускались; прежние результаты encrypted_file
выше не относятся к этому изменению. Windows/FreeBSD не проверялись.
voice_file.h и attachment.h сохраняют [~]: завершение этого фикса не закрывает
оставшуюся сверку контрактов всего аудита. Повторная проверка media_delivery ещё не завершена.
Продолжение аудита — 2026-10-24: переанонс записей SQLite
Полностью прочитаны media_index.c/h, media_download.c/h, media_delivery.c/h,
file_transfer.c/h, media_delivery_proto.h и sync.md. Повторно сверены media_crypto.c/h.
В media_crypto.h уточнены native байты nonce, границы CCM/буферов, пустой поток,
момент выдачи размеров/хеша и отсутствие close/fsync/rename; неверный аргумент не обнуляет выходы.
В file_transfer.h уточнены lookup/авторизация, 16 операций, размер1..INT64_MAX,
усечение своего временного назначения даже при отказе запуска, таймаут и отмена/владение callback.
Изменения только описаний сохранены коммитом5dbca4a2.
Подтверждённый дефект
media_delivery_announce_local_blocks (media_delivery.c:1610) читает media_id/block_id
через sqlite3_column_blob, но проверяет только ненулевой указатель. Затем
media_delivery_send_have_block копирует по16 байт в пакет и подписывает объявление.
media_files имеет BLOB NOT NULL без CHECK длины: такие строки допустимы текущей схемой.
Обычные media_index writers пишут правильные длины; опыт моделирует повреждённые
локальные данные, а не доказанный путь внесения повреждения удалённым участником.
Настоящие SQLite, схема, обработчик, выбор группы/суперузла и Ed25519; перехвачена
только транспортная отправка в существующем tests/media_delivery_fixture.h:
- Контрольные UUID16/16, chunk0, chat_id111: один корректный HAVE_BLOCK.
- media_id длиной1 байт: один HAVE_BLOCK с16-байтным media_id, из них15 байт вне BLOB.
В журнале вместо ожидаемого единственного7a видны посторонние ненулевые байты памяти.
- block_id длиной1 байт: аналогичное чтение15 лишних байт в сетевой block_id.
- media_id или block_id длиной17 байт: отправка с молчаливым усечением до16.
- chunk=-1: wire_chunk=4294967295; chunk=4294967296: wire_chunk=0.
Причина — sqlite3_column_int с последующим приведением к uint32_t без диапазона.
- chat_id111junk: strtoull без endptr принимает префикс111 и направляет объявление в группу111.
- Итого8 случаев,7 повреждённых строк из7 отправлены; invalid_records_sent=7, confirmed=1.
Ошибки соответствующих полей в модуле не логируются. Пакеты сохранены только стендом,
реальные удалённые узлы в опыте не участвовали. Ошибка ASAN или падение не воспроизводились.
Артефакты: /tmp/media-announcement-audit.c, /tmp/media-announcement-audit.mk,
/tmp/media-announcement-audit-build.log, /tmp/media-announcement-audit.log.
Сборка через tests/Makefile с настоящими src/libutun.a/libuasync.a и штатными флагами;
проектные .c для диагностики не менялись. Очистка собственного стенда исправлена и повторно проверена.
Отдельно по коду видно: результаты bind/завершающего step/finalize игнорируются;
SQL-ошибку окончания обхода нельзя выдавать как обычное «done». Инъекция SQL-ошибок здесь не выполнялась.
Проект простого исправления
1. Проверять строку на границе чтения SQLite до доступа как к фиксированным структурам:
media_id/block_id имеют тип BLOB и ровно16 байт; проверить полученные указатели.
Скопировать прошедшие проверку UUID в локальные массивы до вложенных SQL/отправок.
2. chunk читать как int64: тип INTEGER, 0..UINT32_MAX, только затем cast.
chat_id — TEXT, длина1..20 без embedded NUL, каноническое ненулевое uint64 decimal.
Полный разбор должен отвергать суффиксы, знаки, пробелы, leading zero и переполнение;
строка канала должна сохранять свой смысл при переводе в router group_id.
3. Плохую строку пропускать с WARN, указав rowid/типы/длины/некорректное поле,
без чтения за BLOB и без анонса. Корректные соседние строки продолжать обрабатывать.
Никакого общего выделяемого снимка или отката уже отправленных корректных анонсов не требуется.
4. Проверять bind, terminal step и finalize; SQL-ошибки логировать с фазой/кодами
и количеством обработанных строк. Не превращать их в обычный успешный финальный лог.
Сохранить существующий void API, поштучные анонсы и wire-формат.
5. Регрессии в штатной media-цели: valid row, длины UUID0/1/15/16/17 и неверные SQL-типы,
chunk=-1/0/UINT32_MAX/UINT32_MAX+1, неверные/граничные chat_id, embedded NUL;
смесь корректных/плохих записей и ошибки SQL. Перехват HAVE_BLOCK обязан доказать,
что наружу ушли только исходные16 байт проверенных UUID и правильный номер группы/блока.
Аудит остановлен по AGENTS.md:10 после подтверждения дефекта и подготовки проекта исправления.
Новые полные build/check/sanitizer-прогоны не запускались: изменения в репозитории только документальные.
media_crypto.h и file_transfer.h можно отметить [V]; media_delivery.h/proto.h остаются [~]:
нужно исправление этого пути и завершение сверки описаний. Остальные прежние отметки не закрывают
весь аудит. Остались контракты attachment/attachment_send/voice, связанные encrypted/lifecycle документы
и устаревший _desc.txt (RTT-подбор, direct-only репликация и её интервалы расходятся с текущим кодом).
Результаты исправления SQLite-переанонса — 2026-10-24
- Проверка остаётся внутри существующего обхода media_files. UUID допускаются только
как BLOB ровно16 байт; chunk — INTEGER в0..UINT32_MAX, читается через int64.
Исходные SQL-типы получены до преобразований. Проверенные UUID копируются
в локальные16-байтные массивы до вложенных запросов/отправок.
- chat_id имеет тип TEXT, длину1..20; разбор по объявленной длине отвергает embedded NUL,
знаки/пробелы/суффиксы, ноль/leading zero и переполнение. Допускается весь ненулевой uint64,
включая UINT64_MAX. Условие gid<=(UINT64_MAX-digit)/10 до операции исключает переполнение.
- Непроверенные фиксированные memcpy и сужающие cast устранены. Некорректная строка
пропускается с WARN: id/поле, типы/длины, исходный chunk. Хорошие соседние строки продолжают обрабатываться.
- prepare/bind/завершающий step/finalize проверяются. Отказ SQL логирует фазу/коды
и неполный обход; обычный complete-лог не выдаётся. Счётчики rows/eligible/skipped
описывают обработку, а не гарантированную доставку. Уже корректно отправленные пакеты не откатываются.
- Архитектура сохраняет существующие void API, wire-формат, источник ролей и единичные анонсы;
нет общего снимка/новых ресурсов, транзакций или состояния. Statement закрывается и при ошибке bind/step.
- test_media_delivery_sql: 36 случаев полей,32 недопустимых строки не отправляются,
четыре допустимые сохраняют точные UUID/group/chunk/timestamp. Проверены границы
UINT32_MAX/UINT64_MAX, все заявленные длины UUID и неверные SQL-типы, embedded NUL.
В смеси valid/invalid/valid отправлены только два корректных анонса.
- Linux: пять внедрений prepare/bind/first-step/step после первой строки/finalize.
До отказа сохранены0/0/0/1/2 корректных отправки, statement не остаётся активным;
проверяется ERROR с фазой и отсутствие ложного complete. После каждого отказа
обычный повтор успешно отправляет обе строки с исходными UUID и номерами блоков.
После захвата файлового лога тест явно восстанавливает console_enabled.
- ./build.sh --no-gui -j4 PASS; в изменённых C-файлах предупреждений нет.
Профиль12/12 PASS: delivery_sql, query, blocks, replication, index/index_regressions,
delivery_download, download_timeout, delivery_integration/full/chat, services.
После изменения только вывода диагностики SQL-тест повторно прошёл отдельно.
- ASAN/UBSan/LSan4/4 PASS: delivery_sql/query/blocks/replication, detect_leaks=1.
Свежая копия /tmp/utun-media-announcement-sanitizers содержит текущий код;
CPPFLAGS=-fsanitize=address,undefined применён и к встроенным зависимостям.
Обычные объекты проекта не смешивались с sanitizer-объектами.
Первый configure внутри sandbox отклонён LeakSanitizer из-за ptrace;
сборка и тесты успешно выполнены вне sandbox.
Логи: /tmp/media-announcement-fix-build.log, /tmp/media-announcement-fix-profile-results.log,
/tmp/media-announcement-fix-sql-final.log, /tmp/media-announcement-sanitizer-build.log,
/tmp/media-announcement-sanitizer-tests-build.log, /tmp/media-announcement-sanitizer-results.log.
Полный make check и Windows/FreeBSD не запускались. Завершение фикса не означает завершения
сверки оставшихся заголовков и документов двух каталогов.
Продолжение сверки описаний — 2026-10-24: MIME контейнера
Сверены и уточнены attachment.h, attachment_send.h, voice_file.h, media_delivery_proto.h
и комментарий неиспользуемого HAVE_BLOCK timeout в media_delivery.h:
- attachment: UTF-8 и только ASCII C0/DEL, границы полей по kind, состояние выходов
при ошибке encode/decode, content_type не является валидатором kind.
- voice_file: собственное перезаписываемое назначение, поздняя ошибка close может
изменить info; native контейнер и отсутствие fsync/атомарной публикации.
- attachment_send: владение начинается при вызове trampoline, при отказе самого post
остаётся у caller; PCM/compressor, контекст освобождения, DM generation, канальные
request_id/reply, внутренний UUID подготовки и события подготовки вместо обещания доставки.
- proto: вне SYNC числа native-endian; SERVE_REG/LEAVE только вспомогательный учёт,
загрузчик их не посылает. SERVE_ACK не отправляется; приём SERVE_ACK/HAVE_BLOCK_ACK игнорируется,
ожидания ACK/его таймера нет. Не менялись команды, wire или runtime-поведение.
Коммиты описаний:08692fa4 и4e4c60ee. Из общего service_lifecycle повторно сверена
только media-секция111..196 по текущему порядку start/quiesce/destroy и прочитанным модулям;
за пределами media документ не считается полностью проверенным.
Подтверждённый дефект MIME
encrypted_file_write проверяет только ненулевой type и strlen(type)<32, затем шифрует строку.
encrypted_file_read требует непустую NUL-терминированную строку в32-байтном поле,
ASCII33..126 без '"' и '\\'. Поэтому write может успешно подготовить непригодный для read контейнер.
chat_attachment_send (chat_attachment.c:198..225) имеет такую же слабую входную проверку:
до сохранения ключа/старта worker проверяется лишь наличие указателя и длина.
Реальный roundtrip, без подмены криптографии или файловых функций:
- Контроль application/octet-stream: write=0/read=0, hash расшифрованного temp равен исходному.
- Пустой MIME, перевод строки, двойная кавычка, обратная косая черта и не-ASCII UTF-8:
все пять write=0, после publish и вычисления точного внешнего hash соответствующий read=-1.
Ключ, author, UUID и контейнер при чтении те же. Ошибка — invalid metadata/type,
а не аутентификация или порча контейнера в опыте.
- При ошибке read прежние info/type сохранены, temp очищен; SHA256 исходника неизменен во всех6 случаях.
Это подтверждённый дефект принятия аргумента в API. Сквозная отправка/открытие карточки GUI не моделировалась.
- /tmp/media-content-type-audit.c и .mk, сборка через tests/Makefile и настоящие библиотеки;
/tmp/media-content-type-audit-build.log, /tmp/media-content-type-audit.log.
Артефакты: /tmp/media-content-type-audit-FdtMC3/. Лог содержит MIME как hex, без управляющих символов в тексте.
Проект простого исправления
1. Экспортировать encrypted_file_validate_type в encrypted_file.h: 0/-1, bounded strnlen(type,32),
непустая строка длиной1..31, ASCII33..126 кроме двойной кавычки и обратной косой черты.
Это ограничение существующего контейнера, не новый полноценный MIME parser и не правило для всех сообщений чата.
Диагностика показывает длину/позицию/hex ошибочного байта, не выводит сырую непроверенную строку.
2. Вызывать один валидатор в chat_attachment_send до выделения задачи/генерации ключа/записи в БД,
в encrypted_file_write до открытия source/temp и в read для расшифрованного32-байтного поля.
Существующие проверки длины расшифрованного блока сохраняются; правило MIME не дублируется в трёх местах.
3. Не менять формат, nonce, crypto, DB state machine или файловое владение. Reader остаётся строгим;
writer не должен выдавать успех для метаданных, которые этот же reader не принимает.
4. Регрессии: NULL/пустая строка/31/32 байта, каждый запрещённый символ, ASCII-границы
и валидные значения; writer на раннем отказе сохраняет source/target/output.
Для reader плохие metadata шифруются тестом с известным ключом и пересчитанным внешним hash:
отказ не теряет прежние info/type и не оставляет temp. Проверить send-отказ без строки ключа/worker/файлов.
На этапе воспроизведения аудит был остановлен по AGENTS.md:10. Затем пользователь разрешил продолжать исправления.
Реализация и проверка MIME-фикса — 2026-10-24
- encrypted_file_validate_type использует bounded strnlen(type,32) и одно правило для send/write/read.
NULL, пустая/нетерминированная строка и недопустимый байт отклоняются с WARN: длина/позиция/hex.
Успешная проверка имеет DEBUG-лог; сырые непроверенные строки в лог не попадают.
- send проверяет type до выделения задачи, генерации/сохранения ключа и старта worker.
write отказывает до файловых операций и изменения output. read проверяет уже аутентифицированное
поле после проверки длины metadata и сохраняет атомарное присваивание info/type только при успехе.
Валидатор не выполняет I/O и разрешён в uasync; файловые write/read остаются worker-only.
- Правило сохраняет действующий набор reader, включая короткое значение "a"; это не полный MIME parser.
Не добавлены новый wire, изменение nonce/crypto, состояние БД или второй владелец файлов.
Код:90964e7a/f4cc6920, регрессии:aace975a.
- test_encrypted_file: все256 значений первого байта,92 допустимых; NULL отдельно.
Восемь ранних отказов сохраняют output побайтно, source/cipher/target по SHA256.
Три настоящих roundtrip проверяют длины1/24/31, metadata, SHA256 plaintext и сохранность target до publish.
Семь плохих wire-полей зашифрованы настоящим CCM с пересчитанным внешним SHA256:
read отказывает, сохраняя info/type/старый target/source; temp и fd не остаются.
- test_attachment_send: восемь отказов до ключей/worker, число файлов и исходный байт сохранены.
Сохранён прежний сценарий реального encrypted worker, отмены после публикации и recovery.
- ./build.sh --no-gui -j4 PASS; в изменённых C-файлах предупреждений нет.
Профиль10/10 PASS: encrypted_file, attachment_send, encrypted_attachment, attachment, media_async,
services, media_delivery_chat/full, dm_media, dm_worker.
- ASAN/UBSan/LSan4/4 PASS: encrypted_file, attachment_send, encrypted_attachment, services.
detect_leaks=1, halt_on_error=1; встроенные зависимости также инструментированы через CPPFLAGS.
Отдельная копия /tmp/utun-media-mime-sanitizers исключает смешение обычных/sanitizer-объектов.
Первый build остановился из-за нехватки /tmp; очищены воспроизводимые объекты прежних копий,
затем повторная сборка и весь прогон завершились успешно. Исходники и журналы сохранены.
Логи: /tmp/media-mime-fix-build.log, /tmp/media-mime-fix-profile-results.log,
/tmp/media-mime-fix-profile-logs/, /tmp/media-mime-sanitizer-build-retry.log,
/tmp/media-mime-sanitizer-tests-build.log, /tmp/media-mime-sanitizer-results.log,
/tmp/media-mime-sanitizer-test_*.log. Полный check и Windows/FreeBSD для MIME-фикса не запускались.
encrypted_file.h теперь согласован с реализацией. Последующая сверка общих описаний завершена ниже.
Завершение аудита media_async/media_delivery — 2026-10-24
- Все11 публичных заголовков двух каталогов сверены по полным функциям и структурам реализаций:
media_async/crypto, attachment/send, encrypted_file, voice_file, media_index/download/delivery/proto,
file_transfer. Уточнены назначения, входы/выходы, владение и потоки, ошибки и wire-ограничения.
- _desc.txt заменён актуальным описанием подготовки, поиска/загрузки, relay, каталога и репликации.
Убраны прежние предположения о RTT-выборе, только прямых соединениях, часовых повторах и ACK-окне.
Отражены фактические QUERY/CM/transfer UUID, пределы выдач, per-group RX-курсор и атомарный приём страницы.
sync.md уточняет отсутствие ожидания HAVE ACK и проверки SQLite при локальном переанонсе.
- encrypted_attachments.md целиком сопоставлен с chat_attachment.c и источниками desktop/Android UI:
inputbar, MessageList/attachment_key_ui, JNI snapshot, ChatRepository, MessageBubble/Reply/ChannelList.
Уточнены MIME, видимость desktop-кнопки при наличии pending requests и preview цитаты после расшифровки.
Контейнер не выдаётся UI как готовый файл; путь/метаданные заменяются после готовности plaintext.
Это сверка исходников, без нового интерактивного GUI/Android runtime-прогона.
- media-раздел service_lifecycle.md сверён по текущему start/quiesce/join/done/destroy и владельцам
worker/temp/CM/relay. Дописано, что ранний MIME-валидатор допустим в uasync и не выполняет I/O.
Общий документ сохраняет частичную отметку: голос, звонки и другие немедийные владельцы вне этой проверки.
- Архитектура исправлений остаётся простой: формат/валидация контейнера принадлежат encrypted_file,
temp/durability — file_io, публикация и БД/UI — задаче chat_attachment; один владелец на ресурс.
Media_index регистрирует подготовленное, delivery проверяет передачу и ведёт каталог своей группы.
Синхронные приём/проверка/сборка delivery в uasync и отсутствие общей транзакции ФС/SQLite
явно описаны как действующие границы. File_transfer оставляет ключи/авторизацию/проверку caller.
- Все четыре воспроизведённых дефекта исправлены и имеют регрессии. Неразобранных подтверждённых
дефектов в рамках этого аудита не осталось. Последние изменения после MIME-прогона — только описания;
git diff --check проходит. Для MIME действуют профиль10/10 и ASAN/UBSan/LSan4/4 выше;
прежние полные185/186 и sanitizer21/21 не приписываются последующим исправлениям.
Коммиты общей сверки:91fed902 (delivery),8d175049 (encrypted/UI/lifecycle).
Чек-лист: все media-заголовки и encrypted_attachments.md — [V], общий service_lifecycle.md — [~].
Windows/FreeBSD, новый полный check и интерактивные GUI/Android проверки не выполнялись.