CheckedUnsafePtr.h (20161B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // Diagnostic class template that helps finding dangling pointers. 6 7 #ifndef mozilla_CheckedUnsafePtr_h 8 #define mozilla_CheckedUnsafePtr_h 9 10 #include <cstddef> 11 #include <type_traits> 12 #include <utility> 13 14 #include "mozilla/Assertions.h" 15 #include "mozilla/Attributes.h" 16 #include "mozilla/DataMutex.h" 17 #include "mozilla/StackWalk.h" 18 #include "mozilla/StaticPrefs_dom.h" 19 #include "nsContentUtils.h" 20 #include "nsString.h" 21 #include "nsTArray.h" 22 23 #if defined __has_builtin 24 # if __has_builtin(__builtin_FUNCTION) 25 # define bt_function __builtin_FUNCTION() 26 # else 27 # define bt_function "__builtin_FUNCTION() is undefined" 28 # endif 29 # if __has_builtin(__builtin_FILE) 30 # define bt_file __builtin_FILE() 31 # else 32 # define bt_file "__builtin_FILE() is undefined" 33 # endif 34 # if __has_builtin(__builtin_LINE) 35 # define bt_line __builtin_LINE() 36 # else 37 # define bt_line -1 38 # endif 39 #else 40 # define bt_function "__builtin_FUNCTION() is undefined" 41 # define bt_file "__builtin_FILE() is undefined" 42 # define bt_line -1 43 #endif 44 45 namespace mozilla { 46 enum class CheckingSupport { 47 Disabled, 48 Enabled, 49 }; 50 51 template <typename T> 52 class CheckedUnsafePtr; 53 54 namespace detail { 55 56 static constexpr auto kSourceFileRelativePathMap = 57 std::array<std::pair<nsLiteralCString, nsLiteralCString>, 1>{ 58 {{"mozilla/dom/CheckedUnsafePtr.h"_ns, 59 "dom/quota/CheckedUnsafePtr.h"_ns}}}; 60 61 static inline nsDependentCSubstring GetSourceFileRelativePath( 62 const nsACString& aSourceFilePath) { 63 static constexpr auto error = "ERROR"_ns; 64 static constexpr auto mozillaRelativeBase = "mozilla/"_ns; 65 static constexpr auto thisSourceFileRelativePath = 66 "/dom/quota/CheckedUnsafePtr.h"_ns; 67 static constexpr auto filePath = nsLiteralCString(__FILE__); 68 69 MOZ_ASSERT(StringEndsWith(filePath, thisSourceFileRelativePath)); 70 static const auto sourceTreeBase = Substring( 71 filePath, 0, filePath.Length() - thisSourceFileRelativePath.Length()); 72 73 if (MOZ_LIKELY(StringBeginsWith(aSourceFilePath, sourceTreeBase))) { 74 return Substring(aSourceFilePath, sourceTreeBase.Length() + 1); 75 } 76 77 // The source file could have been exported to the OBJDIR/dist/include 78 // directory, so we need to check that case as well. 79 static constexpr auto commonHSourceFileRelativePath = 80 "/mozilla/dom/quota/CheckedUnsafePtr.h"_ns; 81 MOZ_ASSERT(StringEndsWith(filePath, commonHSourceFileRelativePath)); 82 static const auto objdirDistIncludeTreeBase = Substring( 83 filePath, 0, filePath.Length() - commonHSourceFileRelativePath.Length()); 84 85 if (MOZ_LIKELY( 86 StringBeginsWith(aSourceFilePath, objdirDistIncludeTreeBase))) { 87 const auto sourceFileRelativePath = 88 Substring(aSourceFilePath, objdirDistIncludeTreeBase.Length() + 1); 89 90 // Exported source files don't have to use the same directory structure as 91 // original source files. Check if we have a mapping for the exported 92 // source file. 93 const auto foundIt = std::find_if( 94 kSourceFileRelativePathMap.cbegin(), kSourceFileRelativePathMap.cend(), 95 [&sourceFileRelativePath](const auto& entry) { 96 return entry.first == sourceFileRelativePath; 97 }); 98 99 if (MOZ_UNLIKELY(foundIt != kSourceFileRelativePathMap.cend())) { 100 return Substring(foundIt->second, 0); 101 } 102 103 // If we don't have a mapping for it, just remove the mozilla/ prefix 104 // (if there's any). 105 if (MOZ_LIKELY( 106 StringBeginsWith(sourceFileRelativePath, mozillaRelativeBase))) { 107 return Substring(sourceFileRelativePath, mozillaRelativeBase.Length()); 108 } 109 110 // At this point, we don't know how to transform the relative path of the 111 // exported source file back to the relative path of the original source 112 // file. This can happen when QM_TRY is used in an exported nsIFoo.h file. 113 // If you really need to use QM_TRY there, consider adding a new mapping 114 // for the exported source file. 115 return sourceFileRelativePath; 116 } 117 118 nsCString::const_iterator begin, end; 119 if (RFindInReadable("/"_ns, aSourceFilePath.BeginReading(begin), 120 aSourceFilePath.EndReading(end))) { 121 // Use the basename as a fallback, to avoid exposing any user parts of the 122 // path. 123 ++begin; 124 return Substring(begin, aSourceFilePath.EndReading(end)); 125 } 126 127 return nsDependentCSubstring{static_cast<mozilla::Span<const char>>( 128 static_cast<const nsCString&>(error))}; 129 } 130 131 static inline void CheckedUnsafePtrStackCallback(uint32_t aFrameNumber, 132 void* aPC, void* aSP, 133 void* aClosure) { 134 auto* stack = static_cast<nsCString*>(aClosure); 135 MozCodeAddressDetails details; 136 MozDescribeCodeAddress(aPC, &details); 137 char buf[1025]; 138 (void)MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, 139 &details); 140 stack->Append(buf); 141 stack->Append("\n"); 142 } 143 144 class CheckedUnsafePtrBaseCheckingEnabled; 145 146 struct CheckedUnsafePtrCheckData { 147 using Data = nsTArray<CheckedUnsafePtrBaseCheckingEnabled*>; 148 149 DataMutex<Data> mPtrs{"mozilla::SupportsCheckedUnsafePtr"}; 150 }; 151 152 class CheckedUnsafePtrBaseCheckingEnabled { 153 friend class CheckedUnsafePtrBaseAccess; 154 155 protected: 156 CheckedUnsafePtrBaseCheckingEnabled() = delete; 157 CheckedUnsafePtrBaseCheckingEnabled( 158 const CheckedUnsafePtrBaseCheckingEnabled& aOther) = default; 159 CheckedUnsafePtrBaseCheckingEnabled(const char* aFunction, const char* aFile, 160 const int aLine) 161 : mFunctionName(aFunction), mSourceFile(aFile), mLineNo(aLine) {} 162 163 // When copying an CheckedUnsafePtr, its mIsDangling member must be copied as 164 // well; otherwise the new copy might try to dereference a dangling pointer 165 // when destructed. 166 void CopyDanglingFlagIfAvailableFrom( 167 const CheckedUnsafePtrBaseCheckingEnabled& aOther) { 168 mIsDangling = aOther.mIsDangling; 169 } 170 171 template <typename Ptr> 172 using DisableForCheckedUnsafePtr = std::enable_if_t< 173 !std::is_base_of<CheckedUnsafePtrBaseCheckingEnabled, Ptr>::value>; 174 175 // When constructing an CheckedUnsafePtr from a different kind of pointer it's 176 // not possible to determine whether it's dangling; therefore it's undefined 177 // behavior to construct one from a dangling pointer, and we assume that any 178 // CheckedUnsafePtr thus constructed is not dangling. 179 template <typename Ptr> 180 DisableForCheckedUnsafePtr<Ptr> CopyDanglingFlagIfAvailableFrom(const Ptr&) {} 181 182 template <typename F> 183 void WithCheckedUnsafePtrsImpl(CheckedUnsafePtrCheckData* const aRawPtr, 184 F&& aClosure) { 185 if (!mIsDangling && aRawPtr) { 186 const auto CheckedUnsafePtrs = aRawPtr->mPtrs.Lock(); 187 aClosure(this, *CheckedUnsafePtrs); 188 } 189 } 190 191 void DumpDebugMsg() { 192 fprintf(stderr, "CheckedUnsafePtr [%p]\n", this); 193 fprintf(stderr, "Location of creation: %s, %s:%d\n", mFunctionName.get(), 194 GetSourceFileRelativePath(mSourceFile).BeginReading(), mLineNo); 195 fprintf(stderr, "Stack of creation:\n%s\n", mCreationStack.get()); 196 fprintf(stderr, "Stack of last assignment\n%s\n\n", 197 mLastAssignmentStack.get()); 198 } 199 200 nsCString mFunctionName{EmptyCString()}; 201 nsCString mSourceFile{EmptyCString()}; 202 int32_t mLineNo{-1}; 203 nsCString mCreationStack{EmptyCString()}; 204 nsCString mLastAssignmentStack{EmptyCString()}; 205 206 private: 207 bool mIsDangling = false; 208 }; 209 210 class CheckedUnsafePtrBaseAccess { 211 protected: 212 static void SetDanglingFlag(CheckedUnsafePtrBaseCheckingEnabled& aBase) { 213 aBase.mIsDangling = true; 214 aBase.DumpDebugMsg(); 215 } 216 }; 217 218 template <typename T, CheckingSupport = T::SupportsChecking::value> 219 class CheckedUnsafePtrBase; 220 221 template <typename T, typename U, typename S = std::nullptr_t> 222 using EnableIfCompatible = std::enable_if_t< 223 std::is_base_of< 224 T, std::remove_reference_t<decltype(*std::declval<U>())>>::value, 225 S>; 226 227 template <typename T> 228 class CheckedUnsafePtrBase<T, CheckingSupport::Enabled> 229 : detail::CheckedUnsafePtrBaseCheckingEnabled { 230 public: 231 MOZ_IMPLICIT constexpr CheckedUnsafePtrBase( 232 const std::nullptr_t = nullptr, const char* aFunction = bt_function, 233 const char* aFile = bt_file, const int32_t aLine = bt_line) 234 : detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine), 235 mRawPtr(nullptr) { 236 if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) { 237 MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0, 238 &mCreationStack); 239 } 240 } 241 242 template <typename U, typename = EnableIfCompatible<T, U>> 243 MOZ_IMPLICIT CheckedUnsafePtrBase(const U& aPtr, 244 const char* aFunction = bt_function, 245 const char* aFile = bt_file, 246 const int32_t aLine = bt_line) 247 : detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine) { 248 if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) { 249 MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0, 250 &mCreationStack); 251 } 252 Set(aPtr); 253 } 254 255 CheckedUnsafePtrBase(const CheckedUnsafePtrBase& aOther, 256 const char* aFunction = bt_function, 257 const char* aFile = bt_file, 258 const int32_t aLine = bt_line) 259 : detail::CheckedUnsafePtrBaseCheckingEnabled(aFunction, aFile, aLine) { 260 if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) { 261 MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0, 262 &mCreationStack); 263 } 264 Set(aOther.Downcast()); 265 } 266 267 ~CheckedUnsafePtrBase() { Reset(); } 268 269 CheckedUnsafePtr<T>& operator=(const std::nullptr_t) { 270 // Assign to nullptr, no need to record the last assignment stack. 271 if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) { 272 mLastAssignmentStack.Truncate(); 273 } 274 Reset(); 275 return Downcast(); 276 } 277 278 template <typename U> 279 EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=(const U& aPtr) { 280 if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) { 281 mLastAssignmentStack.Truncate(); 282 MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0, 283 &mLastAssignmentStack); 284 } 285 Replace(aPtr); 286 return Downcast(); 287 } 288 289 CheckedUnsafePtrBase& operator=(const CheckedUnsafePtrBase& aOther) { 290 if (StaticPrefs::dom_checkedUnsafePtr_dumpStacks_enabled()) { 291 mLastAssignmentStack.Truncate(); 292 if (aOther.get()) { 293 MozStackWalk(CheckedUnsafePtrStackCallback, CallerPC(), 0, 294 &mLastAssignmentStack); 295 } 296 } 297 if (&aOther != this) { 298 if (aOther.get()) { 299 Replace(aOther.Downcast()); 300 } else { 301 Reset(); 302 } 303 } 304 return Downcast(); 305 } 306 307 constexpr T* get() const { return mRawPtr; } 308 309 private: 310 template <typename U, CheckingSupport> 311 friend class CheckedUnsafePtrBase; 312 313 CheckedUnsafePtr<T>& Downcast() { 314 return static_cast<CheckedUnsafePtr<T>&>(*this); 315 } 316 const CheckedUnsafePtr<T>& Downcast() const { 317 return static_cast<const CheckedUnsafePtr<T>&>(*this); 318 } 319 320 using Base = detail::CheckedUnsafePtrBaseCheckingEnabled; 321 322 template <typename U> 323 void Replace(const U& aPtr) { 324 Reset(); 325 Set(aPtr); 326 } 327 328 void Reset() { 329 WithCheckedUnsafePtrs( 330 [](Base* const aSelf, 331 detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) { 332 const auto index = aCheckedUnsafePtrs.IndexOf(aSelf); 333 aCheckedUnsafePtrs.UnorderedRemoveElementAt(index); 334 }); 335 mRawPtr = nullptr; 336 } 337 338 template <typename U> 339 void Set(const U& aPtr) { 340 this->CopyDanglingFlagIfAvailableFrom(aPtr); 341 mRawPtr = &*aPtr; 342 WithCheckedUnsafePtrs( 343 [](Base* const aSelf, 344 detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) { 345 aCheckedUnsafePtrs.AppendElement(aSelf); 346 }); 347 } 348 349 template <typename F> 350 void WithCheckedUnsafePtrs(F&& aClosure) { 351 this->WithCheckedUnsafePtrsImpl(mRawPtr, std::forward<F>(aClosure)); 352 } 353 354 T* mRawPtr; 355 }; 356 357 template <typename T> 358 class CheckedUnsafePtrBase<T, CheckingSupport::Disabled> { 359 public: 360 MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const std::nullptr_t = nullptr) 361 : mRawPtr(nullptr) {} 362 363 template <typename U, typename = EnableIfCompatible<T, U>> 364 MOZ_IMPLICIT constexpr CheckedUnsafePtrBase(const U& aPtr) : mRawPtr(aPtr) {} 365 366 constexpr CheckedUnsafePtr<T>& operator=(const std::nullptr_t) { 367 mRawPtr = nullptr; 368 return Downcast(); 369 } 370 371 template <typename U> 372 constexpr EnableIfCompatible<T, U, CheckedUnsafePtr<T>&> operator=( 373 const U& aPtr) { 374 mRawPtr = aPtr; 375 return Downcast(); 376 } 377 378 constexpr T* get() const { return mRawPtr; } 379 380 private: 381 constexpr CheckedUnsafePtr<T>& Downcast() { 382 return static_cast<CheckedUnsafePtr<T>&>(*this); 383 } 384 385 T* mRawPtr; 386 }; 387 } // namespace detail 388 389 class CheckingPolicyAccess { 390 protected: 391 template <typename CheckingPolicy> 392 static void NotifyCheckFailure(CheckingPolicy& aPolicy) { 393 aPolicy.NotifyCheckFailure(); 394 } 395 }; 396 397 template <typename Derived> 398 class CheckCheckedUnsafePtrs : private CheckingPolicyAccess, 399 private detail::CheckedUnsafePtrBaseAccess { 400 public: 401 using SupportsChecking = 402 std::integral_constant<CheckingSupport, CheckingSupport::Enabled>; 403 404 protected: 405 static constexpr bool ShouldCheck() { 406 static_assert( 407 std::is_base_of<CheckCheckedUnsafePtrs, Derived>::value, 408 "cannot instantiate with a type that's not a subclass of this class"); 409 return true; 410 } 411 412 void Check(detail::CheckedUnsafePtrCheckData::Data& aCheckedUnsafePtrs) { 413 if (!aCheckedUnsafePtrs.IsEmpty()) { 414 for (auto* const aCheckedUnsafePtrBase : aCheckedUnsafePtrs) { 415 SetDanglingFlag(*aCheckedUnsafePtrBase); 416 } 417 NotifyCheckFailure(*static_cast<Derived*>(this)); 418 } 419 } 420 }; 421 422 class CrashOnDanglingCheckedUnsafePtr 423 : public CheckCheckedUnsafePtrs<CrashOnDanglingCheckedUnsafePtr> { 424 friend class mozilla::CheckingPolicyAccess; 425 void NotifyCheckFailure() { MOZ_CRASH("Found dangling CheckedUnsafePtr"); } 426 }; 427 428 struct DoNotCheckCheckedUnsafePtrs { 429 using SupportsChecking = 430 std::integral_constant<CheckingSupport, CheckingSupport::Disabled>; 431 }; 432 433 namespace detail { 434 // Template parameter CheckingSupport controls the inclusion of 435 // CheckedUnsafePtrCheckData as a subobject of instantiations of 436 // SupportsCheckedUnsafePtr, ensuring that choosing a policy without checking 437 // support incurs no size overhead. 438 template <typename CheckingPolicy, 439 CheckingSupport = CheckingPolicy::SupportsChecking::value> 440 class SupportCheckedUnsafePtrImpl; 441 442 template <typename CheckingPolicy> 443 class SupportCheckedUnsafePtrImpl<CheckingPolicy, CheckingSupport::Disabled> 444 : public CheckingPolicy { 445 protected: 446 template <typename... Args> 447 explicit SupportCheckedUnsafePtrImpl(Args&&... aArgs) 448 : CheckingPolicy(std::forward<Args>(aArgs)...) {} 449 }; 450 451 template <typename CheckingPolicy> 452 class SupportCheckedUnsafePtrImpl<CheckingPolicy, CheckingSupport::Enabled> 453 : public CheckedUnsafePtrCheckData, public CheckingPolicy { 454 template <typename T> 455 friend class CheckedUnsafePtr; 456 457 protected: 458 template <typename... Args> 459 explicit SupportCheckedUnsafePtrImpl(Args&&... aArgs) 460 : CheckingPolicy(std::forward<Args>(aArgs)...) {} 461 462 ~SupportCheckedUnsafePtrImpl() { 463 if (this->ShouldCheck()) { 464 const auto ptrs = mPtrs.Lock(); 465 this->Check(*ptrs); 466 } 467 } 468 }; 469 470 struct SupportsCheckedUnsafePtrTag {}; 471 } // namespace detail 472 473 template <typename Condition, 474 typename CheckingPolicy = CrashOnDanglingCheckedUnsafePtr> 475 using CheckIf = std::conditional_t<Condition::value, CheckingPolicy, 476 DoNotCheckCheckedUnsafePtrs>; 477 478 #ifdef DEBUG 479 using AssertEnabled = std::true_type; 480 #else 481 using AssertEnabled = std::false_type; 482 #endif 483 484 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 485 using DiagnosticAssertEnabled = std::true_type; 486 #else 487 using DiagnosticAssertEnabled = std::false_type; 488 #endif 489 490 using ReleaseAssertEnabled = std::true_type; 491 492 // A T class that publicly inherits from an instantiation of 493 // SupportsCheckedUnsafePtr and its subclasses can be pointed to by smart 494 // pointers of type CheckedUnsafePtr<T>. Whenever such a smart pointer is 495 // created, its existence is tracked by the pointee according to its 496 // CheckingPolicy. When the pointee goes out of scope it then uses the its 497 // CheckingPolicy to verify that no CheckedUnsafePtr pointers are left pointing 498 // to it. 499 // 500 // The CheckingPolicy type is used to control the kind of verification that 501 // happen at the end of the object's lifetime. By default, debug builds always 502 // check for dangling CheckedUnsafePtr pointers and assert that none are found, 503 // while release builds forgo all checks. (Release builds incur no size or 504 // runtime penalties compared to bare pointers.) 505 template <typename CheckingPolicy> 506 class SupportsCheckedUnsafePtr 507 : public detail::SupportCheckedUnsafePtrImpl<CheckingPolicy>, 508 public detail::SupportsCheckedUnsafePtrTag { 509 public: 510 template <typename... Args> 511 explicit SupportsCheckedUnsafePtr(Args&&... aArgs) 512 : detail::SupportCheckedUnsafePtrImpl<CheckingPolicy>( 513 std::forward<Args>(aArgs)...) {} 514 }; 515 516 // CheckedUnsafePtr<T> is a smart pointer class that helps detect dangling 517 // pointers in cases where such pointers are not allowed. In order to use it, 518 // the pointee T must publicly inherit from an instantiation of 519 // SupportsCheckedUnsafePtr. An CheckedUnsafePtr<T> can be used anywhere a T* 520 // can be used, has the same size, and imposes no additional thread-safety 521 // restrictions. 522 template <typename T> 523 class CheckedUnsafePtr : public detail::CheckedUnsafePtrBase<T> { 524 static_assert( 525 std::is_base_of<detail::SupportsCheckedUnsafePtrTag, T>::value, 526 "type T must be derived from instantiation of SupportsCheckedUnsafePtr"); 527 528 public: 529 using detail::CheckedUnsafePtrBase<T>::CheckedUnsafePtrBase; 530 using detail::CheckedUnsafePtrBase<T>::get; 531 532 constexpr T* operator->() const { return get(); } 533 534 constexpr T& operator*() const { return *get(); } 535 536 MOZ_IMPLICIT constexpr operator T*() const { return get(); } 537 538 template <typename U> 539 constexpr bool operator==( 540 detail::EnableIfCompatible<T, U, const U&> aRhs) const { 541 return get() == aRhs.get(); 542 } 543 544 template <typename U> 545 friend constexpr bool operator==( 546 detail::EnableIfCompatible<T, U, const U&> aLhs, 547 const CheckedUnsafePtr& aRhs) { 548 return aRhs == aLhs; 549 } 550 551 template <typename U> 552 constexpr bool operator!=( 553 detail::EnableIfCompatible<T, U, const U&> aRhs) const { 554 return !(*this == aRhs); 555 } 556 557 template <typename U> 558 friend constexpr bool operator!=( 559 detail::EnableIfCompatible<T, U, const U&> aLhs, 560 const CheckedUnsafePtr& aRhs) { 561 return aRhs != aLhs; 562 } 563 }; 564 565 } // namespace mozilla 566 567 // nsTArray<T> requires by default that T can be safely moved with std::memmove. 568 // Since CheckedUnsafePtr<T> has a non-trivial copy constructor, it has to opt 569 // into nsTArray<T> using them. 570 template <typename T> 571 struct nsTArray_RelocationStrategy<mozilla::CheckedUnsafePtr<T>> { 572 using Type = std::conditional_t< 573 T::SupportsChecking::value == mozilla::CheckingSupport::Enabled, 574 nsTArray_RelocateUsingMoveConstructor<mozilla::CheckedUnsafePtr<T>>, 575 nsTArray_RelocateUsingMemutils>; 576 }; 577 578 template <typename T> 579 struct nsTArray_RelocationStrategy< 580 mozilla::NotNull<mozilla::CheckedUnsafePtr<T>>> { 581 using Type = 582 std::conditional_t<T::SupportsChecking::value == 583 mozilla::CheckingSupport::Enabled, 584 nsTArray_RelocateUsingMoveConstructor< 585 mozilla::NotNull<mozilla::CheckedUnsafePtr<T>>>, 586 nsTArray_RelocateUsingMemutils>; 587 }; 588 589 #endif // mozilla_CheckedUnsafePtr_h