# Unretained Dangling Pointers 最近enableされていっぱいUAFを発見しているUnretained由来のdangling ptrについて確認する。 ## Unretainedとは [base::Unretained](https://github.com/elkurin/elkurin-daily-notes/blob/main/docs/day20.md)のノートで触れた通り、実体はただの生ポインタ。 Callbackを作成するときに使われる。 ```cpp= base::BindOnce(&MyClass::DoSomething, base::Unretained(this), param); ``` BindOnceでは第一引数でweak pointerを渡すことで帰り先を束縛できる。本来はWeakPtrFactoryを用意して、クラスの最後尾に置くことで呼び元のオブジェクトが破壊されるとinvalidateされるようにして渡す。 呼び元が死んだらCallbackを呼ばないようにするため。 ただし、callbackが呼ばれるタイミングが呼び元が死ぬよりも絶対に早いときは、`base::Unretained(this)`すなわち自身の生ポインタを渡しても良い。 WeakPtrFactoryという無駄なオブジェクトが一つ減るのでいらないなら消せば良さそう。 しかしいらないと思って実は要りましたというケースはよくあって、それを発見してくれるのが今回enableされた unretained dangling ptr detector。 ## Unretained Dangling Ptr UnretainedによるUAFは[全体の37.5%](https://source.chromium.org/chromium/chromium/src/+/main:docs/unretained_dangling_ptr_guide.md;bpv=1)らしい。それを検知して直していきたい。 Unretainedのdetectorは前から存在していて、それがCrashするようになったのが今週。 [`kUnretainedDanglingPtrModeParam`](https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_alloc_features.cc;l=35;drc=e2c6afce60b755ff2c23b236baf045375715177a)で制御している。 ここのCrashするか否かのパラメータが[`kCrash`になった](https://chromium-review.googlesource.com/c/chromium/src/+/5351597)。それまでは`kDumpWithoutCrashing`になっていて、引っかかってもログを吐くだけだったので、test failureとしては見えなかった。 一応確認すると、このパラメータはUnretainedDanglingRawPtrDetectedFnに何を登録するかを決めていて、Crashなら[UnretainedDanglingRawPtrDetectedCrash](https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_alloc_support.cc;l=760;drc=c292340f0c721cd88f9b14c453c877c55d13ce6b)、つまりImmediateCrashを呼ぶ。 ここで登録したものは[`g_unretained_dangling_raw_ptr_detected_fn`](https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/src/partition_alloc/dangling_raw_ptr_checks.cc;l=15;drc=c292340f0c721cd88f9b14c453c877c55d13ce6b)に登録され、[ReportIfDangling](https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/src/partition_alloc/in_slot_metadata.h;l=304;drc=c292340f0c721cd88f9b14c453c877c55d13ce6b)から呼ばれる。 ## UAFが見つかったらどうする? このDetectorで見つかったら[base::UnsafeDangling](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/bind.h;l=211;drc=7824b6f569e030d369cb58ba731b9c56eaeb6d93)か[base::UnsafeDanglingUntriaged](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/bind.h;l=246;drc=7824b6f569e030d369cb58ba731b9c56eaeb6d93)マーカーがつけられる。 どちらもdanglingを許すためのannotationだが意味が違う。 UnsafeDanglingは特別な理由で恒久的にDanglingであることを許すが、UnsafeDanglingUntriagedは直さないといけないけどまだ手がついていないだけ。 どうやって直せばいいか? 1. Callbackが依存先が生きている間に必ず呼ばれるようにrefactorする 2. weak ptrを渡して、ライフタイム管理をしてもらう 3. ポインタの代わりにunique identifierを使う ## 困った話 UAFを教えてくれるのは良いし、Unretainedなやつは比較的わかりやすく直せるのでいいが、どのUnretainedで落ちているのかが非自明なのが難しかった。 これは実際に遭遇したスタック。 ``` Task trace: #0 0x5bb64870832f std::__Cr::make_unique<>() Stack trace: #0 0x5bb64b545642 base::debug::CollectStackTrace() #1 0x5bb6484fc6e3 base::debug::StackTrace::StackTrace() #2 0x5bb64b57f53f base::allocator::UnretainedDanglingRawPtrDetectedCrash() #3 0x5bb64d169faa base::internal::RawPtrBackupRefImpl<>::ReportIfDanglingInternal() #4 0x5bb648709593 base::internal::Invoker<>::RunOnce() #5 0x5bb64c679941 base::TaskAnnotator::RunTaskImpl() #6 0x5bb64b4f73fe base::internal::TaskTracker::RunSkipOnShutdown() #7 0x5bb64ce44754 base::internal::TaskTracker::RunTask() #8 0x5bb64b5797cf base::test::TaskEnvironment::TestTaskTracker::RunTask() #9 0x5bb64cca1ada base::internal::TaskTracker::RunAndPopNextTask() #10 0x5bb64cca0eff base::internal::WorkerThread::RunWorker() #11 0x5bb64b50c36a base::internal::WorkerThread::RunSharedWorker() #12 0x5bb64b50c166 base::internal::WorkerThread::ThreadMain() #13 0x5bb64b522988 base::(anonymous namespace)::ThreadFunc() #14 0x7e54a9c23d19 start_thread #15 0x7e54a9ca3f80 __GI___clone ``` このstd::make_uniqueはどこの子ですか?