From 1227027c2f167ede367d4792b603c7669576ad0a Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 14:59:12 +0200 Subject: [PATCH] Record validated encrypted attachment ownership fix and remaining audit scope --- doc/media_async_audit_findings.txt | 48 ++++++++++++++++++++++++++---- doc/tasks.md | 12 +++++--- tests/test_encrypted_file.c | 4 +++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/doc/media_async_audit_findings.txt b/doc/media_async_audit_findings.txt index af9e79a8..93f267e5 100644 --- a/doc/media_async_audit_findings.txt +++ b/doc/media_async_audit_findings.txt @@ -1,6 +1,6 @@ Аудит media_async / media_delivery — 2026-10-24 -Статус: частичный; остановлен на подтверждённом дефекте по AGENTS.md. -Код модулей не изменён. Сборка Linux и 20 профильных тестов прошли. +Статус: подтверждённый дефект исправлен; полный аудит двух каталогов ещё не завершён. +Ниже сохранены исходное воспроизведение и согласованный проект; результаты исправления — в конце. Подтверждённый дефект: исходник уничтожается через другое имя того же файла @@ -29,7 +29,7 @@ source/destination. Разные строки могут обозначать о - /tmp/media-encrypted-alias-lvVS77/ — сохранённые входы и результаты опыта. Проектные .c/.h для воспроизведения не менялись. -Архитектурно уточнённый фикс — проект, код ещё не изменён +Согласованная архитектура исправления Одной проверки file_io_paths перед fopen(wb) недостаточно для полного исправления контракта владения. Сейчас encrypted_file удаляет destination при ошибке, а @@ -151,8 +151,8 @@ DM не вызывает encrypted_file_write/read: он использует me - Профильные 20 тестов, ASAN/UBSan/LSan и полный набор. Изменение file_io требует также проверки его прежних пользователей: copy, video и media blocks/download. -Это архитектурный проект. Уточнённый фикс ещё не реализован и не тестировался; -успешные 20 тестов ниже относятся к текущей реализации до исправления. +Этот проект реализован. Успешные 20 тестов ниже относятся к исходной реализации; +проверки исправленного кода приведены в конце. Что проверено и что осталось @@ -172,4 +172,40 @@ media_delivery_chat, dm_media, dm_worker, services. /tmp/media-doc-audit-logs/. Штатный test_encrypted_file не покрывает alias. Старые сбои encrypted_attachment/media_delivery_chat в этом прогоне не воспроизвелись; один PASS не закрывает задачи о нестабильности. -Полный check.sh и новые sanitizer-прогоны в этом аудите не запускались. +На исходном этапе полный 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 или закрытия старых нестабильных тестов. diff --git a/doc/tasks.md b/doc/tasks.md index 8b3dd56c..ac47aeb1 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -4,19 +4,23 @@ ## Текущие задачи -[ ] **media_async encrypted_file: не уничтожать source через alias назначения** — подтверждено 24.10.2026. +[+] **media_async encrypted_file: не уничтожать source через alias назначения** — исправлено 24.10.2026. write/read отвергают только одинаковые строки; /./ и hardlink обходят проверку одного файла. write возвращает0, заменяя исходные30 байт контейнером пустого файла; read возвращает0, заменяя входной ciphertext182 байта plaintext30 байт. Проверены реальные функции, inode и SHA256. - Архитектурный фикс: один file_io_temp в ak_file; codec готовит закрытый проверенный temp, + Реализован один file_io_temp в ak_file; codec готовит закрытый проверенный temp, worker публикует его одним rename. Target не открывается с усечением; alias проверяется до work и перед publish. Durability задаётся на весь срок temp; ошибка directory sync после rename не считается откатом. Cleanup удаляет только собственные артефакты; crash recovery учитывает temp в media и decrypted. DM не вызывает codec: сохранить его контракт общих sync/publish helpers при изменениях file_io. Детали, проверка архитектуры исправления и незавершённый аудит: [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. + Добавлены восемь alias-сценариев, 23 отказа I/O с контролем fd/temp/метаданных, + отмена настоящего worker после rename до completion, startup/restart обоих деревьев. + Профильные21/21 и основной make check185/185 PASS; proxy9/9, burst1/1, load8/8 PASS + в отдельном network namespace,134217728 проверенных байт. ASAN/UBSan/LSan21/21 PASS, detect_leaks=1. + Логи: /tmp/media-fix-full-check.log, /tmp/media-fix-network-check.log, /tmp/media-fix-sanitizer-results.log. + Полный аудит остальных контрактов media_async/media_delivery ещё не завершён; Windows/FreeBSD не проверялись. [+] **Proxy: исправить HUP/backpressure, владение и rollback** — завершено 24.10.2026. Согласованный проект, причины и результаты: [doc/proxy_fix_plan.txt](proxy_fix_plan.txt). diff --git a/tests/test_encrypted_file.c b/tests/test_encrypted_file.c index 48613d9a..063281d7 100644 --- a/tests/test_encrypted_file.c +++ b/tests/test_encrypted_file.c @@ -18,7 +18,9 @@ static FILE *input_file, *output_file; FILE* __real_fopen(const char*, const char*); FILE* __real_fdopen(int, const char*); size_t __real_fread(void*, size_t, size_t, FILE*), __real_fwrite(const void*, size_t, size_t, FILE*); +#ifdef __GLIBC__ size_t __real___fread_chk(void*, size_t, size_t, size_t, FILE*); +#endif int __real_ferror(FILE*), __real_fflush(FILE*), __real_fclose(FILE*), __real_fsync(int); int __real_fseeko(FILE*, off_t, int), __real_rename(const char*, const char*); int __real_random_bytes(uint8_t*, size_t); @@ -40,10 +42,12 @@ size_t __wrap_fread(void* data, size_t size, size_t count, FILE* file) { if (file == input_file && inject(F_READ)) { read_failed = 1; return 0; } return __real_fread(data, size, count, file); } +#ifdef __GLIBC__ size_t __wrap___fread_chk(void* data, size_t cap, size_t size, size_t count, FILE* file) { if (file == input_file && inject(F_READ)) { read_failed = 1; return 0; } return __real___fread_chk(data, cap, size, count, file); } +#endif int __wrap_ferror(FILE* file) { return file == input_file && read_failed ? 1 : __real_ferror(file); } size_t __wrap_fwrite(const void* data, size_t size, size_t count, FILE* file) { return file == output_file && inject(F_WRITE) ? 0 : __real_fwrite(data, size, count, file);