531 lines
57 KiB
Plaintext
531 lines
57 KiB
Plaintext
Аудит 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 проверки не выполнялись.
|