Repository navigation
Conversation
安全审查发现(issue #4):分段加载的 5 处手写 mu_.unlock()…mu_.lock() 窗口毫无异常安全——窗口内的 std::string::assign / make_shared / push_back 分配大小可被仓库内容驱动(ODB 解压无上限),失败时 (1) 外层 lock_guard 对未上锁的 mutex 解锁(C++/POSIX 双重 UB); (2) 异常穿透 libfuse 纯 C 栈帧且全仓库无 handler → std::terminate, 挂载进程 SIGABRT,所有在途请求失败。 加重缺陷:/commits 生成段先置 commits_generating_=true 再解锁, 抛异常后 flag 永不复位、epoch 不再前进、无 notify——后续所有 /commits open 永久阻塞在条件变量上,每个阻塞 open 占用一个 libfuse worker(默认上限 10),耗尽后整个文件系统失去响应。 三部分同批修复: - src/lock_guards.hpp 新增 ScopedUnlock RAII 守卫(构造解锁/析构 重锁,含异常展开路径),替换全部 5 处手写窗口:tree_locked、 commit_facts_locked、load_blob_locked、cb_open 超大 blob 钉住、 cb_open commits 生成;窗口语义逐行不变(锁外读、锁内缓存 重查/插入顺序保持)。 - /commits single-flight 状态机抽成可测的 SingleFlight + GeneratorFinish(finish 经同一 unique_lock 幂等重锁,任意退出 路径复位 flag/推进 epoch/notify);open_commits 生成段包 try/catch(bad_alloc 等映射为生成失败,§3.5 重试语义),可见 语义不变:fast path、搭车者共享缓冲(0)或 -EIO、失败下次 open 重试。 - 六个 FUSE 回调(getattr/readlink/opendir/open/read/statfs) 边界兜底:catch (bad_alloc) → -ENOMEM,catch (...) → -EIO; cb_open 的 Handle 改 unique_ptr 管理以免异常路径泄漏。 新增 tests/unit/test_lock_guards.cpp(Catch2 v3):ScopedUnlock 窗口内未锁/出作用域重锁、异常展开后 mutex 可正常加解锁; SingleFlight 生成者抛异常后搭车者限时醒来得到失败结果且 flag 已复位、成功路径搭车共享、finish 幂等且 GeneratorFinish 恰好 触发一次。CHANGELOG 记录 Unreleased/Fixed(RFC 0000 §3.4 语境)。 Closes #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修复 #4(异常安全:分段加载解锁窗口在分配失败时触发 mutex UB 与进程终止;/commits 生成异常使 single-flight 永久卡死)。
问题概述
src/gitmount.cpp有 5 处手写mu_.unlock() … mu_.lock()窗口,窗口内的分配(std::string::assign/make_shared/push_back)大小可被仓库内容驱动(ODB 解压无大小上限),在内存压力 /ulimit -v/ 并发 cache miss 下失败时:std::lock_guard对未上锁的 mutex 调unlock()——C++/POSIX 双重 UB;std::terminate→ SIGABRT,挂载进程整体死亡,所有在途请求失败。加重缺陷:
open_commits生成段先置commits_generating_ = true再g.unlock();若生成段抛异常,flag 永不复位、epoch 不再前进、无 notify——后续所有/commitsopen 在条件变量上永久阻塞,每个阻塞 open 占用一个 libfuse worker(fuse_loop_mt默认上限 10),耗尽后整个文件系统(不只 /commits)失去响应。回调边界一旦加 catch,卡死即成为主导症状,因此两部分必须同批修复。实现要点
ScopedUnlockRAII 守卫(新文件src/lock_guards.hpp,GPL-3.0-or-later,仿现有头文件风格):构造解锁、析构重锁(含异常展开路径),不可拷贝。替换全部 5 处手写窗口:tree_locked、commit_facts_locked、load_blob_locked、cb_open超大 blob 钉住、cb_opencommits 生成。各窗口语义逐行不变(锁外读、锁内缓存重查/插入的顺序保持)。SingleFlight+GeneratorFinish飞行守卫(同文件):把/commits的 single-flight 状态机(mutex/cv/generating/epoch)抽成可单测的小类;GeneratorFinish析构经同一unique_lock幂等重锁后finish()(复位 flag、推进 epoch、notify_all),保证任意退出路径——包括异常——状态机复位。open_commits重写为使用它,可见语义不变:fast path、搭车者醒来后commits_buffer_有则共享(0)无则 -EIO、生成失败下次 open 重试(§3.5)。生成段包try/catch: catch (...) { ok = false; }——bad_alloc等任何异常映射为生成失败,不再逃逸出open_commits。锁序不变:flight 内部 mutex 仍是最外层锁。cb_getattr/cb_readlink/cb_opendir/cb_open/cb_read/cb_statfs六个回调体外套catch (const std::bad_alloc&) → -ENOMEM、catch (...) → -EIO——terminate 并非窗口专属,任何逃逸出回调的异常都无 handler。cb_readdir/cb_releasedir/cb_release不分配,不加。附带:cb_open的Handle改unique_ptr接管,异常路径不再泄漏。测试结果
tests/unit/test_lock_guards.cpp(Catch2 v3,已注册进gitmount_tests):ScopedUnlock:另一线程try_lock验证窗口内未锁、出作用域已重锁;窗口内 throw → 外层lock_guard正常展开后 mutex 处于可正常 lock/unlock 状态(无死锁/二次解锁);SingleFlight:(a) 生成者抛异常 → 搭车者在 10s 截止内醒来、得到失败结果,随后begin_or_wait立即成为生成者(flag 已复位);(b) 成功路径生成者手动 finish,搭车者等待后返回并共享结果;(c) 裸 double-finish 无 UB,GeneratorFinish手动 finish + 析构恰好触发一次(epoch 断言)。cmake -DCMAKE_BUILD_TYPE=Debug -DGITMOUNT_WERROR=ON构建零告警;ctest:unit + integration 全部通过(23 assertions / 5 test cases 新增);改动文件已过clang-format -i(18.1.8,与 CI 一致)。ulimit -v放大 blob 场景需真实 /dev/fuse 环境按 issue 验收标准复核(本机 integration 套件通过;未以 root 运行)。风险说明
open_commits的锁获取/释放顺序逐行保持;/commits可见语义(fast path / 搭车共享 / 失败重试)不变。变化仅在异常路径:UB/terminate/永久卡死 → 单次请求 -ENOMEM/-EIO 且系统继续服务。list_commits的分 chunk unlock/lock 回调对按 RFC 0000 §3.4 保留:其抛点(append/push_back)均在 mu_ 持有段内,unlock/lock 对之间无分配,外层lock_guard展开安全(已在实现中核实)。[Unreleased]段落与其他并行 PR 可能冲突,合并时取条目并集即可。epoch()访问器为测试/诊断而设(要求调用方持锁),生产代码未使用。English summary
Five manual
mu_.unlock()…mu_.lock()windows in the segmented-loading path had no exception safety: an allocation failure inside a window (attacker-sizable via ODB object headers, no cap) made the surroundingstd::lock_guardunlock an already-unlocked mutex (UB) and the exception escape through libfuse's C frames intostd::terminate. A throw inside/commitsgeneration additionally wedgedcommits_generating_forever: later opens blocked on the condition variable until libfuse's worker pool (default 10) was exhausted and the whole filesystem stalled.Fix (issue #4, RFC 0000 §3.4 context): a
ScopedUnlockRAII guard (src/lock_guards.hpp) replaces all five windows (re-lock on every exit path, unwinding included); the/commitssingle-flight state machine is extracted into a testableSingleFlightclass with aGeneratorFinishguard that resets the flag, advances the wake-up epoch and notifies riders on every exit path — re-locking idempotently through the sameunique_lock— with the generation body wrapped incatch (...) { ok = false; }(EIO, retry on next open per §3.5); and the six allocating FUSE callbacks (getattr/readlink/opendir/open/read/statfs) translate escaped exceptions at the boundary into-ENOMEM(bad_alloc) /-EIO. New Catch2 unit tests cover the guard semantics and both SingleFlight exit paths; full existing suites stay green.Closes #4