diff --git a/applications/settings-manager/main.cpp b/applications/settings-manager/main.cpp index c77461d45..d514aff16 100644 --- a/applications/settings-manager/main.cpp +++ b/applications/settings-manager/main.cpp @@ -154,7 +154,7 @@ int main(int argc, char *argv[]){ if(args.length() == 3 && args.at(2) == "crash"){ trigger_crash(); }else if(args.length() == 3 && args.at(2) == "transaction"){ - sentry_transaction("settings", "transaction", [](Transaction* t){ + sentry_transaction("Dummy rot transaction", "transaction", [](Transaction* t){ sentry_span(t, "span", "Transaction span", []{ qDebug() << "Triggered transaction"; }); diff --git a/applications/system-service/application.cpp b/applications/system-service/application.cpp index e187376a5..b4fd75129 100644 --- a/applications/system-service/application.cpp +++ b/applications/system-service/application.cpp @@ -37,7 +37,7 @@ void Application::launchNoSecurityCheck(){ #endif startSpan("starting", "Application is starting"); } - Oxide::Sentry::sentry_transaction("application", "launch", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Launch Application", "launch", [this](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -123,7 +123,7 @@ void Application::pauseNoSecurityCheck(bool startIfNone){ return; } O_INFO("Pausing " << path()); - Oxide::Sentry::sentry_transaction("application", "pause", [this, startIfNone](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Pause Application", "pause", [this, startIfNone](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -151,7 +151,7 @@ void Application::interruptApplication(){ ){ return; } - Oxide::Sentry::sentry_transaction("application", "interrupt", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Interrupt Application", "interrupt", [this](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -236,7 +236,7 @@ void Application::resumeNoSecurityCheck(){ O_DEBUG("Can't Resume" << path() << "Already running!"); return; } - Oxide::Sentry::sentry_transaction("application", "resume", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Resume Application", "resume", [this](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -265,7 +265,7 @@ void Application::uninterruptApplication(){ ){ return; } - Oxide::Sentry::sentry_transaction("application", "uninterrupt", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Uninterrupt Application", "uninterrupt", [this](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -320,7 +320,7 @@ void Application::stopNoSecurityCheck(){ if(state == Inactive){ return; } - Oxide::Sentry::sentry_transaction("application", "stop", [this, state](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Stop Application", "stop", [this, state](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -536,7 +536,7 @@ void Application::saveScreen(){ if(m_screenCapture != nullptr){ return; } - Oxide::Sentry::sentry_transaction("application", "saveScreen", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Save screen for Application", "saveScreen", [this](Oxide::Sentry::Transaction* t){ O_INFO("Saving screen..."); QByteArray bytes; Oxide::Sentry::sentry_span(t, "save", "Save the framebuffer", [&bytes]{ @@ -813,7 +813,7 @@ const QString Application::resourcePath() { return "/tmp/tarnish-chroot/" + name const QString Application::chrootPath() { return resourcePath() + "/chroot"; } void Application::mountAll(){ - Oxide::Sentry::sentry_transaction("application", "mount", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Mount directories for Application", "mount", [this](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -869,7 +869,7 @@ void Application::mountAll(){ } void Application::umountAll(){ - Oxide::Sentry::sentry_transaction("application", "umount", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Unmount directories for Application", "umount", [this](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -940,7 +940,7 @@ QStringList Application::getActiveMounts(){ void Application::showSplashScreen(){ auto frameBuffer = EPFrameBuffer::framebuffer(); O_DEBUG("Waiting for other painting to finish..."); - Oxide::Sentry::sentry_transaction("application", "showSplashScreen", [this, frameBuffer](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Show Application Splash Screen", "showSplashScreen", [this, frameBuffer](Oxide::Sentry::Transaction* t){ #ifdef SENTRY if(t != nullptr){ sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); @@ -1031,7 +1031,7 @@ void Application::recallScreen() { return; } Oxide::Sentry::sentry_transaction( - "application", "recallScreen", [this](Oxide::Sentry::Transaction *t) { + "Recall Application Screen", "recallScreen", [this](Oxide::Sentry::Transaction *t) { O_DEBUG("Uncompressing screen..."); QImage img; Oxide::Sentry::sentry_span( diff --git a/applications/system-service/appsapi.cpp b/applications/system-service/appsapi.cpp index 6094f36d3..554182658 100644 --- a/applications/system-service/appsapi.cpp +++ b/applications/system-service/appsapi.cpp @@ -27,7 +27,7 @@ AppsAPI::AppsAPI(QObject* parent) m_processManagerApplication("/"), m_taskSwitcherApplication("/"), m_sleeping(false) { - Oxide::Sentry::sentry_transaction("apps", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Init Apps API", "init", [this](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "start", "Launching initial application", [this](Oxide::Sentry::Span* s){ Oxide::Sentry::sentry_span(s, "singleton", "Setup singleton", [this]{ singleton(this); @@ -104,7 +104,7 @@ AppsAPI::AppsAPI(QObject* parent) } void AppsAPI::startup(){ - Oxide::Sentry::sentry_transaction("apps", "startup", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Apps API Startup", "startup", [this](Oxide::Sentry::Transaction* t){ if(applications.isEmpty()){ O_INFO("No applications found"); notificationAPI->errorNotification(_noApplicationsMessage); @@ -189,7 +189,7 @@ QDBusObjectPath AppsAPI::registerApplicationNoSecurityCheck(QVariantMap properti return applications[name]->qPath(); } QDBusObjectPath path; - Oxide::Sentry::sentry_transaction("apps", "registerApplication", [this, &path, name, properties](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Register Application", "registerApplication", [this, &path, name, properties](Oxide::Sentry::Transaction* t){ Q_UNUSED(t); path = QDBusObjectPath(getPath(name)); auto app = new Application(path, reinterpret_cast(this)); @@ -221,7 +221,7 @@ void AppsAPI::reload(){ if(!hasPermission("apps")){ return; } - Oxide::Sentry::sentry_transaction("apps", "reload", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Reload Apps", "reload", [this](Oxide::Sentry::Transaction* t){ Q_UNUSED(t); writeApplications(); readApplications(); @@ -356,7 +356,7 @@ QVariantMap AppsAPI::pausedApplications(){ } void AppsAPI::unregisterApplication(Application* app){ - Oxide::Sentry::sentry_transaction("apps", "unregisterApplication", [this, app](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Unregister Application", "unregisterApplication", [this, app](Oxide::Sentry::Transaction* t){ Q_UNUSED(t); auto name = app->name(); if(applications.contains(name)){ diff --git a/applications/system-service/dbusservice.cpp b/applications/system-service/dbusservice.cpp index 1cbbf169d..331fb198b 100644 --- a/applications/system-service/dbusservice.cpp +++ b/applications/system-service/dbusservice.cpp @@ -60,7 +60,7 @@ DBusService::DBusService(QObject* parent) : APIBase(parent), apis(){ #ifdef SENTRY sentry_breadcrumb("dbusservice", "Initializing APIs", "info"); #endif - Oxide::Sentry::sentry_transaction("dbus", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("DBus Service Init", "init", [this](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "apis", "Initialize APIs", [this](Oxide::Sentry::Span* s){ Oxide::Sentry::sentry_span(s, "wifi", "Initialize wifi API", [this]{ apis.insert("wifi", APIEntry{ diff --git a/applications/system-service/notificationapi.cpp b/applications/system-service/notificationapi.cpp index 98ca91eca..f88de2e50 100644 --- a/applications/system-service/notificationapi.cpp +++ b/applications/system-service/notificationapi.cpp @@ -12,7 +12,7 @@ NotificationAPI* NotificationAPI::singleton(NotificationAPI* self){ } NotificationAPI::NotificationAPI(QObject* parent) : APIBase(parent), notificationDisplayQueue(), m_enabled(false), m_notifications(), m_lock() { - Oxide::Sentry::sentry_transaction("apps", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Notification API init", "init", [this](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ singleton(this); }); diff --git a/applications/system-service/powerapi.cpp b/applications/system-service/powerapi.cpp index 78d4847ec..c7f60b538 100644 --- a/applications/system-service/powerapi.cpp +++ b/applications/system-service/powerapi.cpp @@ -10,7 +10,7 @@ PowerAPI* PowerAPI::singleton(PowerAPI* self){ PowerAPI::PowerAPI(QObject* parent) : APIBase(parent), m_chargerState(ChargerUnknown){ - Oxide::Sentry::sentry_transaction("power", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Power API Init", "init", [this](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ singleton(this); }); diff --git a/applications/system-service/screenapi.cpp b/applications/system-service/screenapi.cpp index ffd4b49b7..0e47c61bd 100644 --- a/applications/system-service/screenapi.cpp +++ b/applications/system-service/screenapi.cpp @@ -74,7 +74,7 @@ QDBusObjectPath ScreenAPI::addScreenshot(QByteArray blob){ Screenshot* ScreenAPI::addScreenshot(QString filePath){ Screenshot* instance; - Oxide::Sentry::sentry_transaction("screen", "addScreenshot", [this, filePath, &instance](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Add Screenshot", "addScreenshot", [this, filePath, &instance](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "screenshot", "Create screenshot", [this, filePath, &instance]{ auto path = QString(OXIDE_SERVICE_PATH "/screenshots/") + QFileInfo(filePath).completeBaseName().remove('-').remove('.'); instance = new Screenshot(path, filePath, this); @@ -135,7 +135,7 @@ ScreenAPI* ScreenAPI::singleton(ScreenAPI* self){ } ScreenAPI::ScreenAPI(QObject* parent) : APIBase(parent), m_screenshots(), m_enabled(false) { - Oxide::Sentry::sentry_transaction("screen", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Screen API Init", "init", [this](Oxide::Sentry::Transaction* t){ qDBusRegisterMetaType>(); Oxide::Sentry::sentry_span(t, "mkdirs", "Create screenshots directory", [this]{ mkdirs("/home/root/screenshots/"); @@ -202,7 +202,7 @@ bool ScreenAPI::drawFullscreenImage(QString path, double rotate) { img = img.transformed(QTransform().rotate(rotate)); } Oxide::Sentry::sentry_transaction( - "screen", "drawFullscrenImage", + "Draw Fullscreen Image", "drawFullscrenImage", [img, path](Oxide::Sentry::Transaction *t) { Q_UNUSED(t); Oxide::dispatchToMainThread([img] { diff --git a/applications/system-service/systemapi.cpp b/applications/system-service/systemapi.cpp index 94981a0e8..0b74bafb9 100644 --- a/applications/system-service/systemapi.cpp +++ b/applications/system-service/systemapi.cpp @@ -21,7 +21,7 @@ QDebug operator<<(QDebug debug, Touch* touch){ void SystemAPI::PrepareForSleep(bool suspending){ auto device = deviceSettings.getDeviceType(); if(suspending){ - Oxide::Sentry::sentry_transaction("system", "suspend", [this, device](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Suspend System", "suspend", [this, device](Oxide::Sentry::Transaction* t){ if(autoLock()){ lockTimestamp = QDateTime::currentMSecsSinceEpoch() + lockTimer.remainingTime(); O_DEBUG("Auto Lock timestamp:" << lockTimestamp); @@ -66,7 +66,7 @@ void SystemAPI::PrepareForSleep(bool suspending){ O_INFO("Suspending..."); }); }else{ - Oxide::Sentry::sentry_transaction("system", "resume", [this, device](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Resume System", "resume", [this, device](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "inhibit", "Inhibit sleep", [this]{ inhibitSleep(); }); @@ -144,7 +144,7 @@ SystemAPI::SystemAPI(QObject* parent) touches(), swipeStates(), swipeLengths() { - Oxide::Sentry::sentry_transaction("system", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("System API Init", "init", [this](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "settings", "Sync settings", [this](Oxide::Sentry::Span* s){ Oxide::Sentry::sentry_span(s, "swipes", "Default swipe values", [this]{ for(short i = Right; i <= Down; i++){ diff --git a/applications/system-service/wifiapi.cpp b/applications/system-service/wifiapi.cpp index 6f4a6a132..8274b72c8 100644 --- a/applications/system-service/wifiapi.cpp +++ b/applications/system-service/wifiapi.cpp @@ -18,7 +18,7 @@ WifiAPI::WifiAPI(QObject* parent) m_link(0), m_scanning(false) { - Oxide::Sentry::sentry_transaction("wifi", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Wifi API Init", "init", [this](Oxide::Sentry::Transaction* t){ Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ singleton(this); }); diff --git a/applications/task-switcher/controller.h b/applications/task-switcher/controller.h index ed4dc5990..5a1f3d1ae 100644 --- a/applications/task-switcher/controller.h +++ b/applications/task-switcher/controller.h @@ -192,7 +192,7 @@ class Controller : public QObject { } void updateImage(){ qDebug() << "Updating background..."; - Oxide::Sentry::sentry_transaction("controller", "updateImage", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_transaction("Update Task Switcher Background Image", "updateImage", [this](Oxide::Sentry::Transaction* t){ QImage* img = nullptr; Oxide::Sentry::sentry_span(t, "previousApplications", "Get image from previous application", [this, &img](Oxide::Sentry::Span* s){ auto previousApplications = appsApi->previousApplications(); diff --git a/package b/package index 18e729588..c04021e9e 100644 --- a/package +++ b/package @@ -5,7 +5,7 @@ pkgnames=(oxide oxide-extra oxide-utils inject_evdev liboxide liboxide-dev libsentry) _oxidever=$(grep 'VERSION =' qmake/common.pri | awk '{print $3}') pkgver="$_oxidever~VERSION~" -_sentryver=0.7.6 +_sentryver=0.7.9 timestamp="$(date -u +%Y-%m-%dT%H:%MZ)" maintainer="Eeems " url=https://oxide.eeems.codes diff --git a/shared/sentry/sentry.pro b/shared/sentry/sentry.pro index 512a7378b..ea1fc7616 100644 --- a/shared/sentry/sentry.pro +++ b/shared/sentry/sentry.pro @@ -28,7 +28,9 @@ PRE_TARGETDEPS += $$OUT_PWD/lib/libsentry.so sentry_install.target = $$OUT_PWD/lib/libsentry.so sentry_install.depends = sentry_build sentry_install.commands = \ - cmake --install $$OUT_PWD/src --prefix $$OUT_PWD --config RelWithDebInfo + cmake --install $$OUT_PWD/src \ + --prefix $$OUT_PWD \ + --config RelWithDebInfo QMAKE_EXTRA_TARGETS += sentry_install QMAKE_CLEAN += -r $$OUT_PWD/src/ diff --git a/shared/sentry/src/CHANGELOG.md b/shared/sentry/src/CHANGELOG.md index 42f07718f..757f94a97 100644 --- a/shared/sentry/src/CHANGELOG.md +++ b/shared/sentry/src/CHANGELOG.md @@ -1,5 +1,36 @@ # Changelog +## 0.7.9 + +**Fixes**: + +- Check file-writer construction when writing envelope to path. ([#1036](https://github.com/getsentry/sentry-native/pull/1036)) + +## 0.7.8 + +**Features**: + +- Let the envelope serialization stream directly to the file. ([#1021](https://github.com/getsentry/sentry-native/pull/1021)) +- Support 16kb page sizes on Android 15. ([#1028](https://github.com/getsentry/sentry-native/pull/1028)) + +## 0.7.7 + +**Fixes**: + +- Further clean up of the exported dependency configuration. ([#1013](https://github.com/getsentry/sentry-native/pull/1013), [crashpad#106](https://github.com/getsentry/crashpad/pull/106)) +- Clean-up scope flushing synchronization in crashpad-backend. ([#1019](https://github.com/getsentry/sentry-native/pull/1019), [crashpad#109](https://github.com/getsentry/crashpad/pull/109)) +- Rectify user-feedback comment parameter guard. ([#1020](https://github.com/getsentry/sentry-native/pull/1020)) + +**Internal**: + +- Updated `crashpad` to 2024-06-11. ([#1014](https://github.com/getsentry/sentry-native/pull/1014), [crashpad#105](https://github.com/getsentry/crashpad/pull/105)) + +**Thank you**: + +- [@JonLiu1993](https://github.com/JonLiu1993) +- [@dg0yt](https://github.com/dg0yt) +- [@stima](https://github.com/stima) + ## 0.7.6 **Fixes**: diff --git a/shared/sentry/src/CMakeLists.txt b/shared/sentry/src/CMakeLists.txt index 00ac137ab..426bd0682 100644 --- a/shared/sentry/src/CMakeLists.txt +++ b/shared/sentry/src/CMakeLists.txt @@ -62,6 +62,7 @@ option(SENTRY_BUILD_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PRO option(SENTRY_LINK_PTHREAD "Link platform threads library" ON) if(SENTRY_LINK_PTHREAD) + set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) endif() @@ -276,7 +277,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS400") endif() if(SENTRY_TRANSPORT_CURL) - if(NOT CURL_FOUND) # Some other lib might bring libcurl already + if(NOT TARGET CURL::libcurl) # Some other lib might bring libcurl already find_package(CURL REQUIRED COMPONENTS AsynchDNS) endif() @@ -284,7 +285,7 @@ if(SENTRY_TRANSPORT_CURL) endif() if(SENTRY_TRANSPORT_COMPRESSION) - if(NOT ZLIB_FOUND) + if(NOT TARGET ZLIB::ZLIB) find_package(ZLIB REQUIRED) endif() @@ -387,11 +388,7 @@ if(SENTRY_LINK_PTHREAD) endif() # apply platform libraries to sentry library -if(SENTRY_LIBRARY_TYPE STREQUAL "STATIC") - target_link_libraries(sentry PUBLIC ${_SENTRY_PLATFORM_LIBS}) -else() - target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS}) -endif() +target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS}) # suppress some errors and warnings for MinGW target if(MINGW) @@ -599,4 +596,9 @@ endif() if(SENTRY_BUILD_SHARED_LIBS) target_link_libraries(sentry PRIVATE "$<$,$>:-Wl,--build-id=sha1,--version-script=${PROJECT_SOURCE_DIR}/src/exports.map>") + + # Support 16KB page sizes + target_link_libraries(sentry PRIVATE + "$<$:-Wl,-z,max-page-size=16384>" + ) endif() diff --git a/shared/sentry/src/README.md b/shared/sentry/src/README.md index b3223d6cf..ffb3ba6e9 100644 --- a/shared/sentry/src/README.md +++ b/shared/sentry/src/README.md @@ -261,8 +261,8 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`. | | | | | | | | Backends | | | | | | | - inproc | ✓ | ✓ | ✓ | ☑ | | -| - crashpad | ☑ | ☑ | ✓ | | | -| - breakpad | ✓ | ✓ | ☑ | (✓) | (✓) | +| - crashpad | ☑ | ☑ | ☑ | | | +| - breakpad | ✓ | ✓ | ✓ | (✓) | (✓) | | - none | ✓ | ✓ | ✓ | ✓ | | Legend: diff --git a/shared/sentry/src/external/crashpad/.gitignore b/shared/sentry/src/external/crashpad/.gitignore index 791393599..aa7162e52 100644 --- a/shared/sentry/src/external/crashpad/.gitignore +++ b/shared/sentry/src/external/crashpad/.gitignore @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Keep sorted + *.Makefile *.ninja *.pyc @@ -19,18 +21,21 @@ *.xcodeproj *~ .*.sw? -.cache .DS_Store +.cache .gdb_history .gdbinit /Makefile +/build/fuchsia /out /third_party/edo/edo +/third_party/fuchsia-gn-sdk /third_party/fuchsia/.cipd /third_party/fuchsia/clang /third_party/fuchsia/qemu /third_party/fuchsia/sdk /third_party/googletest/googletest +/third_party/gyp/gyp /third_party/libfuzzer /third_party/linux/.cipd /third_party/linux/clang diff --git a/shared/sentry/src/external/crashpad/BUILD.gn b/shared/sentry/src/external/crashpad/BUILD.gn index 83c4a32c0..8be9eee75 100644 --- a/shared/sentry/src/external/crashpad/BUILD.gn +++ b/shared/sentry/src/external/crashpad/BUILD.gn @@ -39,7 +39,8 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { if (crashpad_is_in_fuchsia) { # TODO(fuchsia:46559): Fix the leaks and remove this. deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ] - # TODO(fxbug.dev/108368): Remove this once the underlying issue is addressed. + # TODO(fxbug.dev/42059784): Remove this once the underlying issue is + # addressed. exclude_toolchain_tags = [ "hwasan" ] } if (crashpad_is_android) { diff --git a/shared/sentry/src/external/crashpad/CMakeLists.txt b/shared/sentry/src/external/crashpad/CMakeLists.txt index edf23ce25..4f1c9adc4 100644 --- a/shared/sentry/src/external/crashpad/CMakeLists.txt +++ b/shared/sentry/src/external/crashpad/CMakeLists.txt @@ -21,7 +21,7 @@ else() endif() option(CRASHPAD_ZLIB_SYSTEM "Use system zlib library" "${CRASHPAD_ZLIB_SYSTEM_DEFAULT}") -if(CRASHPAD_ZLIB_SYSTEM AND NOT ZLIB_FOUND) +if(CRASHPAD_ZLIB_SYSTEM AND NOT TARGET ZLIB::ZLIB) find_package(ZLIB REQUIRED) endif() @@ -83,7 +83,7 @@ else() enable_language(ASM) endif() -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(crashpad_interface INTERFACE) diff --git a/shared/sentry/src/external/crashpad/DEPS b/shared/sentry/src/external/crashpad/DEPS index 80c1aabf3..b4fe4fff5 100644 --- a/shared/sentry/src/external/crashpad/DEPS +++ b/shared/sentry/src/external/crashpad/DEPS @@ -29,7 +29,7 @@ vars = { deps = { 'buildtools': Var('chromium_git') + '/chromium/src/buildtools.git@' + - '8b16338d17cd71b04a6ba28da7322ab6739892c2', + '8919328651a559f8a974641d40fe712062cc6718', 'buildtools/clang_format/script': Var('chromium_git') + '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' + @@ -47,7 +47,7 @@ deps = { '9719c1e1e676814c456b55f5f070eabad6709d31', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '9e21183c1ea369398d6f6ddd302c8db580bd19c4', + 'bd56f6933f2fa021a44766ced638a18f477ef1c1', 'crashpad/third_party/libfuzzer/src': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + 'fda403cf93ecb8792cb1d061564d89a6553ca020', @@ -86,6 +86,16 @@ deps = { 'dep_type': 'cipd', 'condition': 'host_os == "win"', }, + 'crashpad/build/fuchsia': { + 'packages': [ + { + 'package': 'chromium/fuchsia/test-scripts', + 'version': 'latest', + } + ], + 'condition': 'checkout_fuchsia', + 'dep_type': 'cipd', + }, 'crashpad/third_party/linux/clang/linux-amd64': { 'packages': [ { @@ -100,7 +110,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/third_party/clang/mac-amd64', - 'version': 'MAOjNhwTu5JU3P_0C9dITiyCTtQ1n7lRJnMfB9hhvOkC', + 'version': 'latest', }, ], 'condition': 'checkout_fuchsia and host_os == "mac"', @@ -110,25 +120,20 @@ deps = { 'packages': [ { 'package': 'fuchsia/third_party/clang/linux-amd64', - 'version': 'Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C', + 'version': 'latest', }, ], 'condition': 'checkout_fuchsia and host_os == "linux"', 'dep_type': 'cipd' }, 'crashpad/third_party/fuchsia-gn-sdk': { - 'url': Var('chromium_git') + '/chromium/src/third_party/fuchsia-gn-sdk.git@' + - '0d6902558d92fe3d49ba9a8f638ddea829be595b', - 'condition': 'checkout_fuchsia', - }, - 'crashpad/third_party/fuchsia/sdk/mac-amd64': { 'packages': [ { - 'package': 'fuchsia/sdk/core/mac-amd64', + 'package': 'chromium/fuchsia/gn-sdk', 'version': 'latest' }, ], - 'condition': 'checkout_fuchsia and host_os == "mac"', + 'condition': 'checkout_fuchsia', 'dep_type': 'cipd' }, 'crashpad/third_party/fuchsia/sdk/linux-amd64': { @@ -254,12 +259,25 @@ hooks = [ 'crashpad/build/install_linux_sysroot.py', ], }, + { + # Avoid introducing unnecessary PRESUBMIT.py file from build/fuchsia. + # Never fail and ignore the error if the file does not exist. + 'name': 'Remove the PRESUBMIT.py from build/fuchsia', + 'pattern': '.', + 'condition': 'checkout_fuchsia', + 'action': [ + 'rm', + '-f', + 'crashpad/build/fuchsia/PRESUBMIT.py', + ], + }, { 'name': 'Generate Fuchsia Build Definitions', 'pattern': '.', 'condition': 'checkout_fuchsia', 'action': [ 'python3', + 'crashpad/build/fuchsia_envs.py', 'crashpad/build/fuchsia/gen_build_defs.py' ], }, diff --git a/shared/sentry/src/external/crashpad/client/BUILD.gn b/shared/sentry/src/external/crashpad/client/BUILD.gn index bc67b32f1..bd150ab94 100644 --- a/shared/sentry/src/external/crashpad/client/BUILD.gn +++ b/shared/sentry/src/external/crashpad/client/BUILD.gn @@ -174,6 +174,7 @@ source_set("client_test") { "annotation_list_test.cc", "annotation_test.cc", "crash_report_database_test.cc", + "crashpad_info_test.cc", "length_delimited_ring_buffer_test.cc", "prune_crash_reports_test.cc", "ring_buffer_annotation_test.cc", diff --git a/shared/sentry/src/external/crashpad/client/annotation_list.cc b/shared/sentry/src/external/crashpad/client/annotation_list.cc index bcf7ca76a..c73660772 100644 --- a/shared/sentry/src/external/crashpad/client/annotation_list.cc +++ b/shared/sentry/src/external/crashpad/client/annotation_list.cc @@ -19,6 +19,46 @@ namespace crashpad { +template +T* AnnotationList::IteratorBase::operator*() const { + CHECK_NE(curr_, tail_); + return curr_; +} + +template +T* AnnotationList::IteratorBase::operator->() const { + CHECK_NE(curr_, tail_); + return curr_; +} + +template +AnnotationList::IteratorBase& AnnotationList::IteratorBase::operator++() { + CHECK_NE(curr_, tail_); + curr_ = curr_->GetLinkNode(); + return *this; +} + +template +AnnotationList::IteratorBase AnnotationList::IteratorBase::operator++( + int) { + T* const old_curr = curr_; + ++(*this); + return IteratorBase(old_curr, tail_); +} + +template +bool AnnotationList::IteratorBase::operator!=( + const IteratorBase& other) const { + return !(*this == other); +} + +template +AnnotationList::IteratorBase::IteratorBase(T* head, const Annotation* tail) + : curr_(head), tail_(tail) {} + +template class AnnotationList::IteratorBase; +template class AnnotationList::IteratorBase; + AnnotationList::AnnotationList() : tail_pointer_(&tail_), head_(Annotation::Type::kInvalid, nullptr, nullptr), @@ -65,49 +105,6 @@ void AnnotationList::Add(Annotation* annotation) { } } -AnnotationList::Iterator::Iterator(Annotation* head, const Annotation* tail) - : curr_(head), tail_(tail) {} - -AnnotationList::Iterator::~Iterator() = default; - -Annotation* AnnotationList::Iterator::operator*() const { - CHECK_NE(curr_, tail_); - return curr_; -} - -AnnotationList::Iterator& AnnotationList::Iterator::operator++() { - CHECK_NE(curr_, tail_); - curr_ = curr_->GetLinkNode(); - return *this; -} - -bool AnnotationList::Iterator::operator==( - const AnnotationList::Iterator& other) const { - return curr_ == other.curr_; -} - -AnnotationList::ConstIterator::ConstIterator(const Annotation* head, - const Annotation* tail) - : curr_(head), tail_(tail) {} - -AnnotationList::ConstIterator::~ConstIterator() = default; - -const Annotation* AnnotationList::ConstIterator::operator*() const { - CHECK_NE(curr_, tail_); - return curr_; -} - -AnnotationList::ConstIterator& AnnotationList::ConstIterator::operator++() { - CHECK_NE(curr_, tail_); - curr_ = curr_->GetLinkNode(); - return *this; -} - -bool AnnotationList::ConstIterator::operator==( - const AnnotationList::ConstIterator& other) const { - return curr_ == other.curr_; -} - AnnotationList::Iterator AnnotationList::begin() { return Iterator(head_.GetLinkNode(), tail_pointer_); } diff --git a/shared/sentry/src/external/crashpad/client/annotation_list.h b/shared/sentry/src/external/crashpad/client/annotation_list.h index eec7fb4c1..ce1f86139 100644 --- a/shared/sentry/src/external/crashpad/client/annotation_list.h +++ b/shared/sentry/src/external/crashpad/client/annotation_list.h @@ -15,6 +15,8 @@ #ifndef CRASHPAD_CLIENT_ANNOTATION_LIST_H_ #define CRASHPAD_CLIENT_ANNOTATION_LIST_H_ +#include + #include "build/build_config.h" #include "client/annotation.h" @@ -61,47 +63,46 @@ class AnnotationList { void Add(Annotation* annotation); //! \brief An InputIterator for the AnnotationList. - class Iterator { + template + class IteratorBase { public: - ~Iterator(); + using difference_type = signed int; + using value_type = T*; + using reference = T*; + using pointer = void; + using iterator_category = std::input_iterator_tag; - Annotation* operator*() const; - Iterator& operator++(); - bool operator==(const Iterator& other) const; - bool operator!=(const Iterator& other) const { return !(*this == other); } + IteratorBase(const IteratorBase& other) = default; + IteratorBase(IteratorBase&& other) = default; - private: - friend class AnnotationList; - Iterator(Annotation* head, const Annotation* tail); + ~IteratorBase() = default; - Annotation* curr_; - const Annotation* const tail_; + IteratorBase& operator=(const IteratorBase& other) = default; + IteratorBase& operator=(IteratorBase&& other) = default; - // Copy and assign are required. - }; + T* operator*() const; + T* operator->() const; - //! \brief An InputIterator for iterating a const AnnotationList. - class ConstIterator { - public: - ~ConstIterator(); + IteratorBase& operator++(); + IteratorBase operator++(int); - const Annotation* operator*() const; - ConstIterator& operator++(); - bool operator==(const ConstIterator& other) const; - bool operator!=(const ConstIterator& other) const { - return !(*this == other); + bool operator==(const IteratorBase& other) const { + return curr_ == other.curr_; } + bool operator!=(const IteratorBase& other) const; + private: friend class AnnotationList; - ConstIterator(const Annotation* head, const Annotation* tail); + IteratorBase(T* head, const Annotation* tail); - const Annotation* curr_; - const Annotation* const tail_; - - // Copy and assign are required. + T* curr_ = nullptr; + const Annotation* tail_ = nullptr; }; + using Iterator = IteratorBase; + using ConstIterator = IteratorBase; + //! \brief Returns an iterator to the first element of the annotation list. Iterator begin(); ConstIterator begin() const { return cbegin(); } diff --git a/shared/sentry/src/external/crashpad/client/annotation_list_test.cc b/shared/sentry/src/external/crashpad/client/annotation_list_test.cc index 0ac87ffae..41ecfa05a 100644 --- a/shared/sentry/src/external/crashpad/client/annotation_list_test.cc +++ b/shared/sentry/src/external/crashpad/client/annotation_list_test.cc @@ -14,7 +14,10 @@ #include "client/annotation.h" +#include +#include #include +#include #include #include "base/rand_util.h" @@ -27,6 +30,52 @@ namespace crashpad { namespace test { namespace { +#if (__cplusplus >= 202002L) +template + requires std::input_iterator +void VerifyIsInputIterator(Iterator) {} +#else +template +struct IsLegacyIteratorImpl { + static constexpr bool value = + std::is_copy_constructible_v && + std::is_copy_assignable_v && std::is_destructible_v && + std::is_swappable_v && + // check that std::iterator_traits has the necessary types (check only one + // needed as std::iterator is required to define only if all are defined) + !std::is_same_v::reference, + void> && + std::is_same_v()), Iterator&> && + !std::is_same_v()), void>; +}; + +template +struct IsLegacyInputIteratorImpl { + static constexpr bool value = + IsLegacyIteratorImpl::value && + std::is_base_of_v< + std::input_iterator_tag, + typename std::iterator_traits::iterator_category> && + std::is_convertible_v() != + std::declval()), + bool> && + std::is_convertible_v() == + std::declval()), + bool> && + std::is_same_v()), + typename std::iterator_traits::reference> && + std::is_same_v()), Iterator&> && + std::is_same_v()++), Iterator> && + std::is_same_v())), + typename std::iterator_traits::reference>; +}; + +template +void VerifyIsInputIterator(Iterator) { + static_assert(IsLegacyInputIteratorImpl::value); +} +#endif + TEST(AnnotationListStatic, Register) { ASSERT_FALSE(AnnotationList::Get()); EXPECT_TRUE(AnnotationList::Register()); @@ -222,6 +271,23 @@ TEST_F(AnnotationList, IteratorMultipleAnnotationsInsertedAndRemoved) { EXPECT_EQ(const_iterator, annotations_.cend()); } +TEST_F(AnnotationList, IteratorIsInputIterator) { + one_.Set("1"); + two_.Set("2"); + + // Check explicitly that the iterators meet the interface of an input + // iterator. + VerifyIsInputIterator(annotations_.begin()); + VerifyIsInputIterator(annotations_.cbegin()); + VerifyIsInputIterator(annotations_.end()); + VerifyIsInputIterator(annotations_.cend()); + + // Additionally verify that std::distance accepts the iterators. It requires + // the iterators to comply to the input iterator interface. + EXPECT_EQ(std::distance(annotations_.begin(), annotations_.end()), 2); + EXPECT_EQ(std::distance(annotations_.cbegin(), annotations_.cend()), 2); +} + class RaceThread : public Thread { public: explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {} diff --git a/shared/sentry/src/external/crashpad/client/crashpad_client_ios.cc b/shared/sentry/src/external/crashpad/client/crashpad_client_ios.cc index a34cc15e3..663a17c43 100644 --- a/shared/sentry/src/external/crashpad/client/crashpad_client_ios.cc +++ b/shared/sentry/src/external/crashpad/client/crashpad_client_ios.cc @@ -53,6 +53,40 @@ namespace crashpad { namespace { +// Thread-safe version of `base::apple::ScopedMachReceiveRight` which allocates +// the Mach port upon construction and deallocates it upon destruction. +class ThreadSafeScopedMachPortWithReceiveRight { + public: + ThreadSafeScopedMachPortWithReceiveRight() + : port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) {} + + ThreadSafeScopedMachPortWithReceiveRight( + const ThreadSafeScopedMachPortWithReceiveRight&) = delete; + ThreadSafeScopedMachPortWithReceiveRight& operator=( + const ThreadSafeScopedMachPortWithReceiveRight&) = delete; + + ~ThreadSafeScopedMachPortWithReceiveRight() { reset(); } + + mach_port_t get() { return port_.load(); } + void reset() { + mach_port_t old_port = port_.exchange(MACH_PORT_NULL); + if (old_port == MACH_PORT_NULL) { + // Already reset, nothing to do. + return; + } + kern_return_t kr = mach_port_mod_refs( + mach_task_self(), old_port, MACH_PORT_RIGHT_RECEIVE, -1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "ThreadSafeScopedMachPortWithReceiveRight mach_port_mod_refs"; + kr = mach_port_deallocate(mach_task_self(), old_port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "ThreadSafeScopedMachPortWithReceiveRight mach_port_deallocate"; + } + + private: + std::atomic port_; +}; + // A base class for signal handler and Mach exception server. class CrashHandler : public Thread, public UniversalMachExcServer::Interface, @@ -169,14 +203,14 @@ class CrashHandler : public Thread, } bool InstallMachExceptionHandler() { - exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); - if (!exception_port_.is_valid()) { + mach_port_t exception_port = exception_port_.get(); + if (exception_port == MACH_PORT_NULL) { return false; } kern_return_t kr = mach_port_insert_right(mach_task_self(), - exception_port_.get(), - exception_port_.get(), + exception_port, + exception_port, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { MACH_LOG(ERROR, kr) << "mach_port_insert_right"; @@ -194,7 +228,7 @@ class CrashHandler : public Thread, if (!exception_ports.GetExceptionPorts(mask, &original_handlers_) || !exception_ports.SetExceptionPort( mask, - exception_port_.get(), + exception_port, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE)) { return false; @@ -393,7 +427,7 @@ class CrashHandler : public Thread, Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action); } - base::apple::ScopedMachReceiveRight exception_port_; + ThreadSafeScopedMachPortWithReceiveRight exception_port_; ExceptionPorts::ExceptionHandlerVector original_handlers_; struct sigaction old_action_ = {}; internal::InProcessHandler in_process_handler_; diff --git a/shared/sentry/src/external/crashpad/client/crashpad_client_linux.cc b/shared/sentry/src/external/crashpad/client/crashpad_client_linux.cc index 238413709..da2ee866c 100644 --- a/shared/sentry/src/external/crashpad/client/crashpad_client_linux.cc +++ b/shared/sentry/src/external/crashpad/client/crashpad_client_linux.cc @@ -207,16 +207,30 @@ class SignalHandler { static void HandleOrReraiseSignal(int signo, siginfo_t* siginfo, void* context) { - if (handler_->first_chance_handler_ && - handler_->first_chance_handler_( - signo, siginfo, static_cast(context))) { - return; - } - // Only handle the first fatal signal observed. If another thread receives a // crash signal, it waits for the first dump to complete instead of // requesting another. if (!handler_->disabled_.test_and_set()) { + // but we also need to ensure that a handling thread that gets preempted + // with a non-masked signal, can proceed to handle that signal. + SignalHandler::handling_signal_on_thread_ = true; + } + if (SignalHandler::handling_signal_on_thread_) { + // TODO(supervacuus): + // On Linux the first-chance handler is executed in the context of a + // signal-handler, which can be asynchronous (crashpad handles `SIGQUIT`) + // and even interrupt itself if the first-chance handler crashes with a + // non-masked synchronous signal. This means we not only need to be safe + // with multiple threads raising signals with thread-specific signal- + // masks, but also with reentrancy from the same thread. Adapting our + // first-chance handler to this would mean to adapt the pipeline, because + // even if the handler was safe wrt the above scenarios, there is + // currently no way in the event model to correlate crashes this way. + if (handler_->first_chance_handler_ && + handler_->first_chance_handler_( + signo, siginfo, static_cast(context))) { + return; + } handler_->HandleCrash(signo, siginfo, context); handler_->WakeThreads(); if (handler_->last_chance_handler_ && @@ -274,10 +288,12 @@ class SignalHandler { #else std::atomic_flag disabled_; #endif + thread_local static bool handling_signal_on_thread_; static SignalHandler* handler_; }; SignalHandler* SignalHandler::handler_ = nullptr; +thread_local bool SignalHandler::handling_signal_on_thread_ = false; // Launches a single use handler to snapshot this process. class LaunchAtCrashHandler : public SignalHandler { @@ -470,8 +486,14 @@ bool CrashpadClient::StartHandler( return false; } - std::vector argv = BuildHandlerArgvStrings( - handler, database, metrics_dir, url, http_proxy, annotations, arguments, attachments); + std::vector argv = BuildHandlerArgvStrings(handler, + database, + metrics_dir, + url, + http_proxy, + annotations, + arguments, + attachments); argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get())); argv.push_back("--shared-client-connection"); @@ -694,8 +716,14 @@ bool CrashpadClient::StartHandlerAtCrash( const std::map& annotations, const std::vector& arguments, const std::vector& attachments) { - std::vector argv = BuildHandlerArgvStrings( - handler, database, metrics_dir, url, http_proxy, annotations, arguments, attachments); + std::vector argv = BuildHandlerArgvStrings(handler, + database, + metrics_dir, + url, + http_proxy, + annotations, + arguments, + attachments); auto signal_handler = LaunchAtCrashHandler::Get(); return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_); diff --git a/shared/sentry/src/external/crashpad/client/crashpad_client_linux_test.cc b/shared/sentry/src/external/crashpad/client/crashpad_client_linux_test.cc index 5d492009c..ae76a7e4b 100644 --- a/shared/sentry/src/external/crashpad/client/crashpad_client_linux_test.cc +++ b/shared/sentry/src/external/crashpad/client/crashpad_client_linux_test.cc @@ -765,7 +765,7 @@ class StartHandlerForChildTest : public Multiprocess { __builtin_trap(); - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); } StartHandlerForClientTest test_state_; diff --git a/shared/sentry/src/external/crashpad/client/crashpad_client_win.cc b/shared/sentry/src/external/crashpad/client/crashpad_client_win.cc index cd1b206b0..63e2e66cd 100644 --- a/shared/sentry/src/external/crashpad/client/crashpad_client_win.cc +++ b/shared/sentry/src/external/crashpad/client/crashpad_client_win.cc @@ -155,10 +155,6 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { return EXCEPTION_CONTINUE_SEARCH; } - if (first_chance_handler_ && first_chance_handler_(exception_pointers)) { - return EXCEPTION_CONTINUE_SEARCH; - } - // Otherwise, we know the handler startup has succeeded, and we can continue. // Tracks whether a thread has already entered UnhandledExceptionHandler. @@ -180,6 +176,17 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { SleepEx(INFINITE, false); } + // TODO(supervacuus): + // On Windows the first-chance handler is executed inside the UEF which is + // synchronous with respect to thread it is executing on. However, any other + // running thread can also raise an exception and therefore will also execute + // a UEF, at which point they would run concurrently. Adapting to this would + // mean to adapt the pipeline, because even if the handler was UEF-safe, + // there is currently no way in which to correlate non-nested crashes. + if (first_chance_handler_ && first_chance_handler_(exception_pointers)) { + return EXCEPTION_CONTINUE_SEARCH; + } + // Otherwise, we're the first thread, so record the exception pointer and // signal the crash handler. g_crash_exception_information.thread_id = GetCurrentThreadId(); diff --git a/shared/sentry/src/external/crashpad/client/crashpad_info.cc b/shared/sentry/src/external/crashpad/client/crashpad_info.cc index cd6f23481..781c0e382 100644 --- a/shared/sentry/src/external/crashpad/client/crashpad_info.cc +++ b/shared/sentry/src/external/crashpad/client/crashpad_info.cc @@ -16,6 +16,7 @@ #include +#include "base/numerics/safe_conversions.h" #include "build/build_config.h" #include "util/misc/address_sanitizer.h" #include "util/misc/from_pointer_cast.h" @@ -33,6 +34,21 @@ namespace { // understand new versions. constexpr uint32_t kCrashpadInfoVersion = 1; +// Creates a `UserDataMinidumpStreamListEntry` with the given fields, and +// returns a pointer to it. The caller takes ownership of the returned object. +crashpad::internal::UserDataMinidumpStreamListEntry* CreateListEntry( + uint64_t next, + uint32_t stream_type, + const void* data, + size_t size) { + auto to_be_added = new crashpad::internal::UserDataMinidumpStreamListEntry(); + to_be_added->next = next; + to_be_added->stream_type = stream_type; + to_be_added->base_address = crashpad::FromPointerCast(data); + to_be_added->size = base::checked_cast(size); + return to_be_added; +} + } // namespace namespace crashpad { @@ -123,16 +139,50 @@ CrashpadInfo::CrashpadInfo() user_data_minidump_stream_head_(nullptr), annotations_list_(nullptr) {} -void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type, - const void* data, - size_t size) { - auto to_be_added = new internal::UserDataMinidumpStreamListEntry(); - to_be_added->next = - FromPointerCast(user_data_minidump_stream_head_); - to_be_added->stream_type = stream_type; - to_be_added->base_address = FromPointerCast(data); - to_be_added->size = base::checked_cast(size); - user_data_minidump_stream_head_ = to_be_added; +UserDataMinidumpStreamHandle* CrashpadInfo::AddUserDataMinidumpStream( + uint32_t stream_type, + const void* data, + size_t size) { + user_data_minidump_stream_head_ = CreateListEntry( + crashpad::FromPointerCast(user_data_minidump_stream_head_), + stream_type, + data, + size); + return user_data_minidump_stream_head_; +} + +UserDataMinidumpStreamHandle* CrashpadInfo::UpdateUserDataMinidumpStream( + UserDataMinidumpStreamHandle* stream_to_update, + uint32_t stream_type, + const void* data, + size_t size) { + // Create a new stream that points to the node `stream_to_update` points to. + const auto new_stream = + CreateListEntry(stream_to_update->next, stream_type, data, size); + + // If `stream_to_update` is head of the list, replace the head with + // `new_stream`. + if (stream_to_update == user_data_minidump_stream_head_) { + user_data_minidump_stream_head_ = new_stream; + } else { + // Otherwise, find the node before `stream_to_update`, and make it point to + // `new_stream` instead. + auto current = user_data_minidump_stream_head_; + while (current) { + auto next = reinterpret_cast( + current->next); + if (next == stream_to_update) { + current->next = FromPointerCast(new_stream); + break; + } + current = next; + } + CHECK(current) + << "Tried to update a UserDataMinidumpStream that doesn't exist"; + } + + delete stream_to_update; + return new_stream; } } // namespace crashpad diff --git a/shared/sentry/src/external/crashpad/client/crashpad_info.h b/shared/sentry/src/external/crashpad/client/crashpad_info.h index ebfe8fe88..7d894cb0c 100644 --- a/shared/sentry/src/external/crashpad/client/crashpad_info.h +++ b/shared/sentry/src/external/crashpad/client/crashpad_info.h @@ -56,6 +56,8 @@ struct UserDataMinidumpStreamListEntry { } // namespace internal +using UserDataMinidumpStreamHandle = internal::UserDataMinidumpStreamListEntry; + //! \brief A structure that can be used by a Crashpad-enabled program to //! provide information to the Crashpad crash handler. //! @@ -221,9 +223,41 @@ struct CrashpadInfo { //! which is `0xffff`. //! \param[in] data The base pointer of the stream data. //! \param[in] size The size of the stream data. - void AddUserDataMinidumpStream(uint32_t stream_type, - const void* data, - size_t size); + //! \return A handle to the added stream, for use in calling + //! UpdateUserDataMinidumpStream() if needed. + UserDataMinidumpStreamHandle* AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size); + + //! \brief Replaces the given stream with an updated stream. + //! + //! Creates a new memory block referencing the given \a data and \a size with + //! type \a stream_type. The memory referred to be \a data and \a size is + //! owned by the caller and must remain valid while it is in effect for the + //! CrashpadInfo object. + //! + //! Frees \a stream_to_update and returns a new handle to the updated stream. + //! + //! \param[in] stream_to_update A handle to the stream to be updated, received + //! from either AddUserDataMinidumpStream() or previous calls to this + //! function. + //! \param[in] stream_type The stream type identifier to use. This should be + //! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream` + //! which is `0xffff`. + //! \param[in] data The base pointer of the stream data. + //! \param[in] size The size of the stream data. + //! \return A handle to the new memory block that references the updated data, + //! for use in calling this method again if needed. + UserDataMinidumpStreamHandle* UpdateUserDataMinidumpStream( + UserDataMinidumpStreamHandle* stream_to_update, + uint32_t stream_type, + const void* data, + size_t size); + + internal::UserDataMinidumpStreamListEntry* + GetUserDataMinidumpStreamHeadForTesting() { + return user_data_minidump_stream_head_; + } enum : uint32_t { kSignature = 'CPad', diff --git a/shared/sentry/src/external/crashpad/client/crashpad_info_test.cc b/shared/sentry/src/external/crashpad/client/crashpad_info_test.cc new file mode 100644 index 000000000..8ea7f8a15 --- /dev/null +++ b/shared/sentry/src/external/crashpad/client/crashpad_info_test.cc @@ -0,0 +1,142 @@ +// Copyright 2024 The Crashpad Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/crashpad_info.h" + +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr uint32_t kTestStreamType = 0x33333; + +class CrashpadInfoTest : public testing::Test { + protected: + CrashpadInfo& crashpad_info() { return crashpad_info_; } + + // Returns the current head of the list of streams in `crashpad_info_`. Note + // that the returned pointer is invalidated if a stream is added or updated. + internal::UserDataMinidumpStreamListEntry* GetCurrentHead() { + return crashpad_info().GetUserDataMinidumpStreamHeadForTesting(); + } + + // Returns a pointer to the next node in the list after the given `node`. + internal::UserDataMinidumpStreamListEntry* GetNext( + internal::UserDataMinidumpStreamListEntry* node) { + return reinterpret_cast( + node->next); + } + + internal::UserDataMinidumpStreamListEntry* initial_head() { + return initial_head_; + } + + internal::UserDataMinidumpStreamListEntry* initial_tail() { + return initial_tail_; + } + + private: + void SetUp() override { + ASSERT_EQ(nullptr, GetCurrentHead()); + + // Create a simple test list with the structure + // `initial_head_` -> `initial_tail_`. + initial_tail_ = AddStream(0x11111, kInitialTailData); + initial_head_ = AddStream(0x22222, kInitialHeadData); + + // Validate the list's contents. + auto current = GetCurrentHead(); + ASSERT_EQ(initial_head_, current); + ASSERT_EQ(kInitialHeadData, reinterpret_cast(current->base_address)); + current = GetNext(current); + ASSERT_EQ(initial_tail_, current); + ASSERT_EQ(nullptr, GetNext(current)); + } + + void TearDown() override { + // Free the list. The list lives until process exit in production, but must + // be freed in tests as multiple tests run in the same process. + auto current = GetCurrentHead(); + while (current) { + auto next = GetNext(current); + delete current; + current = next; + } + } + + internal::UserDataMinidumpStreamListEntry* AddStream(uint32_t stream_type, + const char* data) { + return reinterpret_cast( + crashpad_info().AddUserDataMinidumpStream( + stream_type, data, strlen(data))); + } + + CrashpadInfo crashpad_info_; + + static constexpr char kInitialHeadData[] = "head"; + static constexpr char kInitialTailData[] = "tail"; + + internal::UserDataMinidumpStreamListEntry* initial_head_ = nullptr; + internal::UserDataMinidumpStreamListEntry* initial_tail_ = nullptr; +}; + +// Tests that updating the head of the list updates the head pointer, the new +// head contains the updated data, and the updated node points to the next node. +TEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamHead) { + const std::string new_data = "this is a new string"; + const auto new_entry = crashpad_info().UpdateUserDataMinidumpStream( + initial_head(), kTestStreamType, new_data.data(), new_data.size()); + const auto head = GetCurrentHead(); + EXPECT_EQ(new_entry, head); + EXPECT_EQ(new_data.data(), reinterpret_cast(head->base_address)); + EXPECT_EQ(new_data.size(), head->size); + EXPECT_EQ(kTestStreamType, head->stream_type); + EXPECT_EQ(initial_tail(), GetNext(head)); +} + +// Tests that updating the tail of the list results in a tail pointing to +// nullptr, and that the node before the updated node points to it. +TEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamTail) { + const std::string new_data = "new"; + const auto new_entry = crashpad_info().UpdateUserDataMinidumpStream( + initial_tail(), kTestStreamType, new_data.data(), new_data.size()); + const auto tail = GetNext(GetCurrentHead()); + EXPECT_EQ(new_entry, tail); + EXPECT_EQ(nullptr, GetNext(tail)); +} + +// Tests that the handle returned from updating an entry is usable for updating +// the entry again. +TEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamMultipleTimes) { + // Update the entry at the head; the updated entry should become the new head. + const std::string new_data = "new"; + const auto new_entry_1 = crashpad_info().UpdateUserDataMinidumpStream( + initial_head(), kTestStreamType, new_data.data(), new_data.size()); + EXPECT_EQ(new_entry_1, GetCurrentHead()); + + // Update the updated entry again; another new entry should replace it as + // head. + const auto new_entry_2 = crashpad_info().UpdateUserDataMinidumpStream( + new_entry_1, kTestStreamType, new_data.data(), new_data.size()); + EXPECT_NE(new_entry_1, new_entry_2); + EXPECT_EQ(new_entry_2, GetCurrentHead()); + EXPECT_EQ(initial_tail(), GetNext(GetCurrentHead())); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/src/external/crashpad/client/ios_handler/exception_processor.mm b/shared/sentry/src/external/crashpad/client/ios_handler/exception_processor.mm index 470b0e76f..0268b3a12 100644 --- a/shared/sentry/src/external/crashpad/client/ios_handler/exception_processor.mm +++ b/shared/sentry/src/external/crashpad/client/ios_handler/exception_processor.mm @@ -43,7 +43,6 @@ #include #include -#include "base/bit_cast.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/memory/free_deleter.h" @@ -552,7 +551,7 @@ id ObjcExceptionPreprocessor(id exception) { LoggingUnwStep(&cursor) > 0 && unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS) { auto uiwindowimp_lambda = [](IMP* max) { - IMP min = *max = bit_cast(nullptr); + IMP min = *max = nullptr; unsigned int method_count = 0; std::unique_ptr method_list( class_copyMethodList(NSClassFromString(@"UIWindow"), diff --git a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_handler.cc b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_handler.cc index dfdc03c9b..be1269209 100644 --- a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_handler.cc +++ b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_handler.cc @@ -468,9 +468,12 @@ InProcessHandler::ScopedReport::ScopedReport( num_frames_(num_frames), rootMap_(writer) { DCHECK(writer); + // Grab the report creation time before writing the report. + uint64_t report_time_nanos = ClockMonotonicNanoseconds(); InProcessIntermediateDumpHandler::WriteHeader(writer); InProcessIntermediateDumpHandler::WriteProcessInfo(writer, annotations); - InProcessIntermediateDumpHandler::WriteSystemInfo(writer, system_data); + InProcessIntermediateDumpHandler::WriteSystemInfo( + writer, system_data, report_time_nanos); } InProcessHandler::ScopedReport::~ScopedReport() { diff --git a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc index a7ac5dae1..5e6c42df6 100644 --- a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc +++ b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc @@ -415,7 +415,7 @@ void MaybeCaptureMemoryAround(IOSIntermediateDumpWriter* writer, IOSIntermediateDumpWriter::ScopedArrayMap memory_region(writer); WriteProperty( - writer, IntermediateDumpKey::kThreadContextMemoryRegionAddress, &address); + writer, IntermediateDumpKey::kThreadContextMemoryRegionAddress, &target); // Don't use WritePropertyBytes, this one will fail regularly if |target| // cannot be read. writer->AddPropertyBytes(IntermediateDumpKey::kThreadContextMemoryRegionData, @@ -446,7 +446,7 @@ void CaptureMemoryPointedToByThreadState(IOSIntermediateDumpWriter* writer, MaybeCaptureMemoryAround(writer, thread_state.__r15); MaybeCaptureMemoryAround(writer, thread_state.__rip); #elif defined(ARCH_CPU_ARM_FAMILY) - MaybeCaptureMemoryAround(writer, thread_state.__pc); + MaybeCaptureMemoryAround(writer, arm_thread_state64_get_pc(thread_state)); for (size_t i = 0; i < std::size(thread_state.__x); ++i) { MaybeCaptureMemoryAround(writer, thread_state.__x[i]); } @@ -616,7 +616,8 @@ void InProcessIntermediateDumpHandler::WriteProcessInfo( // static void InProcessIntermediateDumpHandler::WriteSystemInfo( IOSIntermediateDumpWriter* writer, - const IOSSystemDataCollector& system_data) { + const IOSSystemDataCollector& system_data, + uint64_t report_time_nanos) { IOSIntermediateDumpWriter::ScopedMap system_map( writer, IntermediateDumpKey::kSystemInfo); @@ -702,6 +703,11 @@ void InProcessIntermediateDumpHandler::WriteSystemInfo( } else { CRASHPAD_RAW_LOG("host_statistics"); } + + uint64_t crashpad_uptime_nanos = + report_time_nanos - system_data.InitializationTime(); + WriteProperty( + writer, IntermediateDumpKey::kCrashpadUptime, &crashpad_uptime_nanos); } // static @@ -870,7 +876,7 @@ void InProcessIntermediateDumpHandler::WriteThreadInfo( #if defined(ARCH_CPU_X86_64) vm_address_t stack_pointer = thread_state.__rsp; #elif defined(ARCH_CPU_ARM64) - vm_address_t stack_pointer = thread_state.__sp; + vm_address_t stack_pointer = arm_thread_state64_get_sp(thread_state); #endif vm_size_t stack_region_size; @@ -917,12 +923,12 @@ void InProcessIntermediateDumpHandler::WriteModuleInfo( uint32_t image_count = image_infos->infoArrayCount; const dyld_image_info* image_array = image_infos->infoArray; - for (uint32_t image_index = 0; image_index < image_count; ++image_index) { + for (int32_t image_index = image_count - 1; image_index >= 0; --image_index) { IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); ScopedVMRead image; if (!image.Read(&image_array[image_index])) { CRASHPAD_RAW_LOG("Unable to dyld_image_info"); - return; + continue; } if (image->imageFilePath) { @@ -961,19 +967,19 @@ void InProcessIntermediateDumpHandler::WriteExceptionFromSignal( WriteProperty(writer, IntermediateDumpKey::kSignalNumber, &siginfo->si_signo); WriteProperty(writer, IntermediateDumpKey::kSignalCode, &siginfo->si_code); WriteProperty(writer, IntermediateDumpKey::kSignalAddress, &siginfo->si_addr); + #if defined(ARCH_CPU_X86_64) - WriteProperty( - writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss); - WriteProperty( - writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__fs); + x86_thread_state64_t thread_state = context->uc_mcontext->__ss; + x86_float_state64_t float_state = context->uc_mcontext->__fs; #elif defined(ARCH_CPU_ARM64) - WriteProperty( - writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss); - WriteProperty( - writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__ns); + arm_thread_state64_t thread_state = context->uc_mcontext->__ss; + arm_neon_state64_t float_state = context->uc_mcontext->__ns; #else #error Port to your CPU architecture #endif + WriteProperty(writer, IntermediateDumpKey::kThreadState, &thread_state); + WriteProperty(writer, IntermediateDumpKey::kFloatState, &float_state); + CaptureMemoryPointedToByThreadState(writer, thread_state); // Thread ID. thread_identifier_info identifier_info; diff --git a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h index 83ebff227..1cf9180c7 100644 --- a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h +++ b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h @@ -56,8 +56,12 @@ class InProcessIntermediateDumpHandler final { //! \brief Write SystemSnapshot data to the intermediate dump. //! //! \param[in] writer The dump writer + //! \param[in] system_data An object containing various system data points. + //! \param[in] report_time Report creation time in nanoseconds as returned by + //! ClockMonotonicNanoseconds(). static void WriteSystemInfo(IOSIntermediateDumpWriter* writer, - const IOSSystemDataCollector& system_data); + const IOSSystemDataCollector& system_data, + uint64_t report_time_nanos); //! \brief Write ThreadSnapshot data to the intermediate dump. //! diff --git a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc index 3b007fcfa..27179bb3a 100644 --- a/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc +++ b/shared/sentry/src/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc @@ -61,8 +61,8 @@ class InProcessIntermediateDumpHandlerTest : public testing::Test { InProcessIntermediateDumpHandler::WriteHeader(writer_.get()); InProcessIntermediateDumpHandler::WriteProcessInfo( writer_.get(), {{"before_dump", "pre"}}); - InProcessIntermediateDumpHandler::WriteSystemInfo(writer_.get(), - system_data_); + InProcessIntermediateDumpHandler::WriteSystemInfo( + writer_.get(), system_data_, ClockMonotonicNanoseconds()); InProcessIntermediateDumpHandler::WriteThreadInfo(writer_.get(), 0, 0); InProcessIntermediateDumpHandler::WriteModuleInfo(writer_.get()); } @@ -89,6 +89,18 @@ class InProcessIntermediateDumpHandlerTest : public testing::Test { const auto& path() const { return path_; } auto writer() const { return writer_.get(); } +#if TARGET_OS_SIMULATOR + // macOS 14.0 is 23A344, macOS 13.6.5 is 22G621, so if the first two + // characters in the kern.osversion are > 22, this build will reproduce the + // simulator bug in crbug.com/328282286 + bool IsMacOSVersion143OrGreaterAndiOS16OrLess() { + if (__builtin_available(iOS 17, *)) { + return false; + } + return std::stoi(system_data_.Build().substr(0, 2)) > 22; + } +#endif + private: std::unique_ptr writer_; internal::IOSSystemDataCollector system_data_; @@ -125,6 +137,15 @@ TEST_F(InProcessIntermediateDumpHandlerTest, TestSystem) { } TEST_F(InProcessIntermediateDumpHandlerTest, TestAnnotations) { +#if TARGET_OS_SIMULATOR + // This test will fail on older ( all_annotations_simple_map; std::vector all_annotations; diff --git a/shared/sentry/src/external/crashpad/client/prune_crash_reports.cc b/shared/sentry/src/external/crashpad/client/prune_crash_reports.cc index 889f56716..6e4c7b9c6 100644 --- a/shared/sentry/src/external/crashpad/client/prune_crash_reports.cc +++ b/shared/sentry/src/external/crashpad/client/prune_crash_reports.cc @@ -123,7 +123,7 @@ bool BinaryPruneCondition::ShouldPruneReport( case OR: return lhs_->ShouldPruneReport(report) || rhs_->ShouldPruneReport(report); default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; } } diff --git a/shared/sentry/src/external/crashpad/client/settings.cc b/shared/sentry/src/external/crashpad/client/settings.cc index 5e4119e21..420fbfc95 100644 --- a/shared/sentry/src/external/crashpad/client/settings.cc +++ b/shared/sentry/src/external/crashpad/client/settings.cc @@ -292,7 +292,7 @@ FileHandle Settings::GetHandleFromOptions( return OpenFileForReadAndWrite( file_path, options.mode, options.permissions); } - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return kInvalidFileHandle; } diff --git a/shared/sentry/src/external/crashpad/crashpad-config.cmake.in b/shared/sentry/src/external/crashpad/crashpad-config.cmake.in index 88fa099c7..dcc37173f 100644 --- a/shared/sentry/src/external/crashpad/crashpad-config.cmake.in +++ b/shared/sentry/src/external/crashpad/crashpad-config.cmake.in @@ -1,12 +1,11 @@ -include("${CMAKE_CURRENT_LIST_DIR}/crashpad-targets.cmake") - -include(CMakeFindDependencyMacro) - if(@CRASHPAD_ZLIB_SYSTEM@) - find_dependency(ZLIB REQUIRED) + include(CMakeFindDependencyMacro) + find_dependency(ZLIB) target_include_directories(crashpad::zlib INTERFACE ${ZLIB_INCLUDE_DIRS}) target_compile_definitions(crashpad::zlib INTERFACE ${ZLIB_COMPILE_DEFINITIONS}) target_link_libraries(crashpad::zlib INTERFACE ${ZLIB_LIBRARIES}) endif() -@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/crashpad-targets.cmake") + +@PACKAGE_INIT@ \ No newline at end of file diff --git a/shared/sentry/src/external/crashpad/doc/appengine/README b/shared/sentry/src/external/crashpad/doc/appengine/README index ee0e319ad..7beefc616 100644 --- a/shared/sentry/src/external/crashpad/doc/appengine/README +++ b/shared/sentry/src/external/crashpad/doc/appengine/README @@ -9,26 +9,37 @@ To work on this app, obtain the following packages: instructions were tested with Go 1.14 locally but a Go 1.11 runtime when deployed), but if problems are encountered, it would be wise to use the same version for both local development and AppEngine deployment. - - The Google Cloud SDK from, https://cloud.google.com/sdk/docs. This is - necessary for both local development and for AppEngine deployment. Unpacking - this package produces a google-cloud-sdk directory, whose bin child directory - may be added to $PATH for convenience, although this is not strictly - necessary. + - The Google Cloud SDK (gcloud CLI) from + https://cloud.google.com/sdk/docs/install-sdk. This is necessary for both + local development and for AppEngine deployment. Unpacking this package + produces a google-cloud-sdk directory, whose bin child directory may be + added to $PATH for convenience, although this is not strictly necessary. The commands in this README are expected to be run from the directory containing -it. $GOPATH must also be set to include this directory: - -% export GOPATH="$(go env GOPATH):$(pwd)" +it. To test locally: -% go get -d crashpad-home -% …/google-cloud-sdk/bin/dev_appserver.py src/crashpad-home +% go get -d ./src/crashpad-home +% python3 …/google-cloud-sdk/bin/dev_appserver.py src/crashpad-home + +dev_appserver.py must be invoked using Python 3, but internally will use Python +2, and a Python 2 interpreter must be available in the PATH as python2. Look for the “Starting module "default" running at: http://localhost:8080” line, which tells you the URL of the local running instance of the app. Test -http://localhost:8080/ and http://localhost:8080/doxygen to ensure that they -work. +http://localhost:8080/ to ensure that it works. + +It would be good to test http://localhost:8080/doxygen as well, but it may fail +with HTTP status 500 and the following error returned as the HTTP response body +because memcache seems to not be available in the local dev_appserver +environment: + +service bridge HTTP failed: Post "http://appengine.googleapis.internal:10001/rpc_http": dial tcp: lookup appengine.googleapis.internal: no such host + +The /doxygen URL can be tested in a verison of the app that’s been deployed +before traffic has been migrated to it by visiting the staged deployed version +from the App Engine console. To deploy: diff --git a/shared/sentry/src/external/crashpad/doc/appengine/go.mod b/shared/sentry/src/external/crashpad/doc/appengine/go.mod new file mode 100644 index 000000000..398c008b2 --- /dev/null +++ b/shared/sentry/src/external/crashpad/doc/appengine/go.mod @@ -0,0 +1,10 @@ +module src/crashpad-home + +go 1.21.6 + +require google.golang.org/appengine/v2 v2.0.5 + +require ( + github.com/golang/protobuf v1.5.2 // indirect + google.golang.org/protobuf v1.30.0 // indirect +) diff --git a/shared/sentry/src/external/crashpad/doc/appengine/go.sum b/shared/sentry/src/external/crashpad/doc/appengine/go.sum new file mode 100644 index 000000000..987d3692b --- /dev/null +++ b/shared/sentry/src/external/crashpad/doc/appengine/go.sum @@ -0,0 +1,37 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine/v2 v2.0.5 h1:4C+F3Cd3L2nWEfSmFEZDPjQvDwL8T0YCeZBysZifP3k= +google.golang.org/appengine/v2 v2.0.5/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/app.yaml b/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/app.yaml index 9af1577b1..2966ba0d1 100644 --- a/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/app.yaml +++ b/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/app.yaml @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -runtime: go111 +runtime: go121 +app_engine_apis: true handlers: - url: /.* diff --git a/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/main.go b/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/main.go index d988af3ce..dced1e800 100644 --- a/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/main.go +++ b/shared/sentry/src/external/crashpad/doc/appengine/src/crashpad-home/main.go @@ -25,9 +25,9 @@ import ( "strings" "time" - "google.golang.org/appengine" - "google.golang.org/appengine/memcache" - "google.golang.org/appengine/urlfetch" + "google.golang.org/appengine/v2" + "google.golang.org/appengine/v2/memcache" + "google.golang.org/appengine/v2/urlfetch" ) func main() { diff --git a/shared/sentry/src/external/crashpad/handler/crash_report_upload_thread.cc b/shared/sentry/src/external/crashpad/handler/crash_report_upload_thread.cc index 7cea398e1..991013a26 100644 --- a/shared/sentry/src/external/crashpad/handler/crash_report_upload_thread.cc +++ b/shared/sentry/src/external/crashpad/handler/crash_report_upload_thread.cc @@ -239,7 +239,7 @@ void CrashReportUploadThread::ProcessPendingReport( return; case CrashReportDatabase::kCannotRequestUpload: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return; } diff --git a/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc index bfccd1f2e..25637408d 100644 --- a/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc +++ b/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc @@ -44,14 +44,13 @@ const std::string GetProcessNameFromPid(pid_t pid) { // Symlink to process binary is at /proc/###/exe. std::string link_path = "/proc/" + std::to_string(pid) + "/exe"; - constexpr int kMaxSize = 4096; - std::unique_ptr buf(new char[kMaxSize]); - ssize_t size = readlink(link_path.c_str(), buf.get(), kMaxSize); - std::string result; + std::string result(4096, '\0'); + ssize_t size = readlink(link_path.c_str(), result.data(), result.size()); if (size < 0) { PLOG(ERROR) << "Failed to readlink " << link_path; + result.clear(); } else { - result.assign(buf.get(), size); + result.resize(size); size_t last_slash_pos = result.rfind('/'); if (last_slash_pos != std::string::npos) { result = result.substr(last_slash_pos + 1); diff --git a/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.h b/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.h index 864de93c4..0ac0c29f8 100644 --- a/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.h +++ b/shared/sentry/src/external/crashpad/handler/linux/cros_crash_report_exception_handler.h @@ -15,6 +15,8 @@ #ifndef CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_ #define CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#include + #include #include diff --git a/shared/sentry/src/external/crashpad/handler/win/hanging_program.cc b/shared/sentry/src/external/crashpad/handler/win/hanging_program.cc index 72f903f21..00eaf1716 100644 --- a/shared/sentry/src/external/crashpad/handler/win/hanging_program.cc +++ b/shared/sentry/src/external/crashpad/handler/win/hanging_program.cc @@ -40,13 +40,13 @@ DWORD WINAPI Thread1(LPVOID context) { Sleep(INFINITE); - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; } DWORD WINAPI Thread2(LPVOID dummy) { Sleep(INFINITE); - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; } @@ -65,7 +65,7 @@ DWORD WINAPI Thread3(LPVOID context) { if (!FreeLibrary(dll)) PLOG(FATAL) << "FreeLibrary"; - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; } diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/commit-queue.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/commit-queue.cfg index 95b31824f..1a17e5c96 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/commit-queue.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/commit-queue.cfg @@ -2,7 +2,7 @@ # Do not modify manually. # # For the schema of this file, see Config message: -# https://luci-config.appspot.com/schemas/projects:commit-queue.cfg +# https://config.luci.app/schemas/projects:commit-queue.cfg cq_status_host: "chromium-cq-status.appspot.com" submit_options { diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/cr-buildbucket.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/cr-buildbucket.cfg index a22e2411d..0ab25244f 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/cr-buildbucket.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/cr-buildbucket.cfg @@ -2,7 +2,7 @@ # Do not modify manually. # # For the schema of this file, see BuildbucketCfg message: -# https://luci-config.appspot.com/schemas/projects:buildbucket.cfg +# https://config.luci.app/schemas/projects:buildbucket.cfg buckets { name: "ci" @@ -23,7 +23,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -57,7 +57,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -91,7 +91,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -124,7 +124,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -156,7 +156,7 @@ buckets { name: "crashpad_ios_arm64_dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -193,7 +193,7 @@ buckets { name: "crashpad_ios_arm64_rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -230,7 +230,7 @@ buckets { name: "crashpad_ios_x64_dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -266,7 +266,7 @@ buckets { name: "crashpad_ios_x64_rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -303,7 +303,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -336,7 +336,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -368,7 +368,7 @@ buckets { name: "crashpad_mac_x64_dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -404,7 +404,7 @@ buckets { name: "crashpad_mac_x64_rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.ci" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -451,7 +451,7 @@ buckets { properties: '{' ' "$depot_tools/windows_sdk": {' - ' "version": "uploaded:2021-04-28"' + ' "version": "uploaded:2024-01-11"' ' },' ' "$gatekeeper": {' ' "group": "client.crashpad"' @@ -487,7 +487,7 @@ buckets { properties: '{' ' "$depot_tools/windows_sdk": {' - ' "version": "uploaded:2021-04-28"' + ' "version": "uploaded:2024-01-11"' ' },' ' "$gatekeeper": {' ' "group": "client.crashpad"' @@ -537,7 +537,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -568,7 +568,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -599,7 +599,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -629,7 +629,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -658,7 +658,7 @@ buckets { name: "crashpad_ios_arm64_dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -692,7 +692,7 @@ buckets { name: "crashpad_ios_arm64_rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -726,7 +726,7 @@ buckets { name: "crashpad_ios_x64_dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -759,7 +759,7 @@ buckets { name: "crashpad_ios_x64_rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -793,7 +793,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -823,7 +823,7 @@ buckets { swarming_host: "chromium-swarm.appspot.com" dimensions: "cores:8" dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-18.04" + dimensions: "os:Ubuntu-22.04" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -852,7 +852,7 @@ buckets { name: "crashpad_mac_x64_dbg" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -885,7 +885,7 @@ buckets { name: "crashpad_mac_x64_rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "cpu:x86-64" - dimensions: "os:Mac-12" + dimensions: "os:Mac-13|Mac-14" dimensions: "pool:luci.flex.try" exe { cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" @@ -929,7 +929,7 @@ buckets { properties: '{' ' "$depot_tools/windows_sdk": {' - ' "version": "uploaded:2021-04-28"' + ' "version": "uploaded:2024-01-11"' ' },' ' "$kitchen": {' ' "devshell": true,' @@ -962,7 +962,7 @@ buckets { properties: '{' ' "$depot_tools/windows_sdk": {' - ' "version": "uploaded:2021-04-28"' + ' "version": "uploaded:2024-01-11"' ' },' ' "$kitchen": {' ' "devshell": true,' diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/luci-logdog.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/luci-logdog.cfg index adc75bef4..01a391261 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/luci-logdog.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/luci-logdog.cfg @@ -2,7 +2,7 @@ # Do not modify manually. # # For the schema of this file, see ProjectConfig message: -# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg +# https://config.luci.app/schemas/projects:luci-logdog.cfg reader_auth_groups: "all" writer_auth_groups: "luci-logdog-chromium-writers" diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/luci-milo.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/luci-milo.cfg index 6c891b14d..9a78f93fb 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/luci-milo.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/luci-milo.cfg @@ -2,7 +2,7 @@ # Do not modify manually. # # For the schema of this file, see Project message: -# https://luci-config.appspot.com/schemas/projects:luci-milo.cfg +# https://config.luci.app/schemas/projects:luci-milo.cfg consoles { id: "main" diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/luci-scheduler.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/luci-scheduler.cfg index a2251eb89..6e6e8e19c 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/luci-scheduler.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/luci-scheduler.cfg @@ -2,7 +2,7 @@ # Do not modify manually. # # For the schema of this file, see ProjectConfig message: -# https://luci-config.appspot.com/schemas/projects:luci-scheduler.cfg +# https://config.luci.app/schemas/projects:luci-scheduler.cfg job { id: "crashpad_fuchsia_arm64_dbg" diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/project.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/project.cfg index d40ae0db5..59369e183 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/project.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/project.cfg @@ -2,12 +2,12 @@ # Do not modify manually. # # For the schema of this file, see ProjectCfg message: -# https://luci-config.appspot.com/schemas/projects:project.cfg +# https://config.luci.app/schemas/projects:project.cfg name: "crashpad" access: "group:all" lucicfg { - version: "1.32.1" + version: "1.43.6" package_dir: ".." config_dir: "generated" entry_point: "main.star" diff --git a/shared/sentry/src/external/crashpad/infra/config/generated/realms.cfg b/shared/sentry/src/external/crashpad/infra/config/generated/realms.cfg index 8dc05f6b5..b7abb3eb3 100644 --- a/shared/sentry/src/external/crashpad/infra/config/generated/realms.cfg +++ b/shared/sentry/src/external/crashpad/infra/config/generated/realms.cfg @@ -2,7 +2,7 @@ # Do not modify manually. # # For the schema of this file, see RealmsCfg message: -# https://luci-config.appspot.com/schemas/projects:realms.cfg +# https://config.luci.app/schemas/projects:realms.cfg realms { name: "@root" diff --git a/shared/sentry/src/external/crashpad/infra/config/main.star b/shared/sentry/src/external/crashpad/infra/config/main.star index ae0e8fc3b..3c719e929 100755 --- a/shared/sentry/src/external/crashpad/infra/config/main.star +++ b/shared/sentry/src/external/crashpad/infra/config/main.star @@ -156,13 +156,13 @@ def crashpad_dimensions(platform, bucket): dimensions["pool"] = "luci.flex." + bucket if platform == "fuchsia": - dimensions["os"] = "Ubuntu-18.04" + dimensions["os"] = "Ubuntu-22.04" elif platform == "ios": - dimensions["os"] = "Mac-12" + dimensions["os"] = "Mac-13|Mac-14" elif platform == "linux": - dimensions["os"] = "Ubuntu-18.04" + dimensions["os"] = "Ubuntu-22.04" elif platform == "mac": - dimensions["os"] = "Mac-12" + dimensions["os"] = "Mac-13|Mac-14" elif platform == "win": dimensions["os"] = "Windows-10" @@ -184,7 +184,7 @@ def crashpad_properties(platform, cpu, config, bucket): if platform == "win": properties["$depot_tools/windows_sdk"] = { - "version": "uploaded:2021-04-28", + "version": "uploaded:2024-01-11", } if bucket == "ci": diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_context_writer.cc b/shared/sentry/src/external/crashpad/minidump/minidump_context_writer.cc index 12942c184..4a6130b63 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_context_writer.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_context_writer.cc @@ -367,36 +367,11 @@ size_t MinidumpContextAMD64Writer::ContextSize() const { bool MinidumpXSaveAMD64CetU::InitializeFromSnapshot( const CPUContextX86_64* context_snapshot) { -#ifdef SENTRY_DISABLED - DCHECK_EQ(context_snapshot->xstate.cet_u.cetmsr, 1ull); -#else - // TODO(supervacuus): this DCHECK led to multiple user inquiries because it - // ends up killing the crashpad_handler (when using a DEBUG build) which in - // turn keeps the crashpad client waiting indefinitely. - // - // It seems that crashpad devs put a DCHECK here because they already check - // at the call-site that the CET_U flag is enabled in the XSAVE feature set. - // However, that this flag is set, only means that the CET_U registers in - // XSAVE are valid, not necessarily that the SH_STK_EN bit is set. - // - // I couldn't find anything in the Intel (SDM 13.1) or AMD (PR 11.5.2, - // 18.11/12/13) CET/SS spec that would signal that SH_STK_EN(=cetmsr[0]) - // cannot be 0 at this point. Ideally, if SH_STK_EN is not set, then SSP - // should be set to 0 too (which means both are in their initial state). But - // even that should not lead to fatally exit the crashpad_handler (even in - // DEBUG), but rather produce a log and result in something that can be - // analysed in the backend. - // - // Any validation based on these register contents must check SH_STK_EN - // anyway or check SSP for !NULL and as a valid base like it is done here: - // https://chromium.googlesource.com/crashpad/crashpad/+/6278690abe6ef0dda047e67dc1d0c49ce7af3811/snapshot/win/thread_snapshot_win.cc#130 - if (!(context_snapshot->xstate.cet_u.cetmsr & 1ull)) { - LOG(WARNING) << "CET MSR enabled flag is not set (" - << context_snapshot->xstate.cet_u.cetmsr - << "); SSP = " - << context_snapshot->xstate.cet_u.ssp; - } -#endif + // Exception records do not carry CET registers but we have to provide the + // same shaped context for threads and exception contexts, so both 0 (no ssp + // present) and 1 (ssp present) are expected. + DCHECK(context_snapshot->xstate.cet_u.cetmsr == 0ull || + context_snapshot->xstate.cet_u.cetmsr == 1ull); cet_u_.cetmsr = context_snapshot->xstate.cet_u.cetmsr; cet_u_.ssp = context_snapshot->xstate.cet_u.ssp; return true; diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_context_writer_test.cc b/shared/sentry/src/external/crashpad/minidump/minidump_context_writer_test.cc index 9eebe81b2..8eaae8314 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_context_writer_test.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_context_writer_test.cc @@ -56,7 +56,7 @@ class TestTypeNames { if (std::is_same()) { return "RVA64"; } - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return ""; } }; diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.cc b/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.cc index 3057c6a74..cd3a139d9 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.cc @@ -34,13 +34,19 @@ MinidumpExceptionWriter::~MinidumpExceptionWriter() { void MinidumpExceptionWriter::InitializeFromSnapshot( const ExceptionSnapshot* exception_snapshot, - const MinidumpThreadIDMap& thread_id_map) { + const MinidumpThreadIDMap& thread_id_map, + bool allow_missing_thread_id_from_map) { DCHECK_EQ(state(), kStateMutable); DCHECK(!context_); auto thread_id_it = thread_id_map.find(exception_snapshot->ThreadID()); - DCHECK(thread_id_it != thread_id_map.end()); - SetThreadID(thread_id_it->second); + bool thread_id_missing = thread_id_it == thread_id_map.end(); + if (allow_missing_thread_id_from_map && thread_id_missing) { + SetThreadID(static_cast(exception_snapshot->ThreadID())); + } else { + DCHECK(!thread_id_missing); + SetThreadID(thread_id_it->second); + } SetExceptionCode(exception_snapshot->Exception()); SetExceptionFlags(exception_snapshot->ExceptionInfo()); diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.h b/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.h index 8e6847e98..4c7111272 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.h +++ b/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer.h @@ -50,12 +50,17 @@ class MinidumpExceptionWriter final : public internal::MinidumpStreamWriter { //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to //! determine the 32-bit minidump thread ID to use for the thread //! identified by \a exception_snapshot. + //! \param[in] allow_missing_thread_id_from_map Whether it is valid + //! for \a exception_snapshot->ThreadID() to be absent from the + //! \a thread_id_map, such as in an incomplete iOS intermediate dump. When + //! false a missing thread id is considered invalid and will DCHECK. //! //! \note Valid in #kStateMutable. No mutator methods may be called before //! this method, and it is not normally necessary to call any mutator //! methods after this method. void InitializeFromSnapshot(const ExceptionSnapshot* exception_snapshot, - const MinidumpThreadIDMap& thread_id_map); + const MinidumpThreadIDMap& thread_id_map, + bool allow_missing_thread_id_from_map); //! \brief Arranges for MINIDUMP_EXCEPTION_STREAM::ThreadContext to point to //! the CPU context to be written by \a context. diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer_test.cc b/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer_test.cc index 9b5e1f9f9..06a921eaa 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer_test.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_exception_writer_test.cc @@ -235,7 +235,10 @@ TEST(MinidumpExceptionWriter, InitializeFromSnapshot) { thread_id_map[kThreadID] = expect_exception.ThreadId; auto exception_writer = std::make_unique(); - exception_writer->InitializeFromSnapshot(&exception_snapshot, thread_id_map); + exception_writer->InitializeFromSnapshot( + &exception_snapshot, + thread_id_map, + /*allow_missing_thread_id_from_map=*/false); MinidumpFileWriter minidump_file_writer; ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer))); diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_file_writer.cc b/shared/sentry/src/external/crashpad/minidump/minidump_file_writer.cc index 29a9e5f0e..47235c708 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_file_writer.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_file_writer.cc @@ -18,6 +18,7 @@ #include "base/check_op.h" #include "base/logging.h" +#include "build/build_config.h" #include "minidump/minidump_crashpad_info_writer.h" #include "minidump/minidump_exception_writer.h" #include "minidump/minidump_handle_writer.h" @@ -117,7 +118,16 @@ void MinidumpFileWriter::InitializeFromSnapshot( const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception(); if (exception_snapshot) { auto exception = std::make_unique(); - exception->InitializeFromSnapshot(exception_snapshot, thread_id_map); +#if BUILDFLAG(IS_IOS) + // It's expected that iOS intermediate dumps can be written with missing + // information, but it's better to try and report as much as possible + // rather than drop the incomplete minidump. + constexpr bool allow_missing_thread_id_from_map = true; +#else + constexpr bool allow_missing_thread_id_from_map = false; +#endif + exception->InitializeFromSnapshot( + exception_snapshot, thread_id_map, allow_missing_thread_id_from_map); add_stream_result = AddStream(std::move(exception)); DCHECK(add_stream_result); } diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_string_writer_test.cc b/shared/sentry/src/external/crashpad/minidump/minidump_string_writer_test.cc index 1487b1386..573eac699 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_string_writer_test.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_string_writer_test.cc @@ -42,7 +42,7 @@ class TestTypeNames { if (std::is_same()) { return "RVA64"; } - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return ""; } }; diff --git a/shared/sentry/src/external/crashpad/minidump/minidump_system_info_writer.cc b/shared/sentry/src/external/crashpad/minidump/minidump_system_info_writer.cc index e2ab775ae..9fe3ac0e6 100644 --- a/shared/sentry/src/external/crashpad/minidump/minidump_system_info_writer.cc +++ b/shared/sentry/src/external/crashpad/minidump/minidump_system_info_writer.cc @@ -136,7 +136,7 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot( cpu_architecture = kMinidumpCPUArchitectureRISCV64Breakpad; break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); cpu_architecture = kMinidumpCPUArchitectureUnknown; break; } @@ -185,7 +185,7 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot( operating_system = kMinidumpOSIOS; break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); operating_system = kMinidumpOSUnknown; break; } diff --git a/shared/sentry/src/external/crashpad/snapshot/BUILD.gn b/shared/sentry/src/external/crashpad/snapshot/BUILD.gn index a364f9561..2ae944c3e 100644 --- a/shared/sentry/src/external/crashpad/snapshot/BUILD.gn +++ b/shared/sentry/src/external/crashpad/snapshot/BUILD.gn @@ -507,7 +507,8 @@ crashpad_loadable_module("crashpad_snapshot_test_module") { "../client", ] if (crashpad_is_in_fuchsia) { - # TODO(fxbug.dev/108368): Remove this once the underlying issue is addressed. + # TODO(fxbug.dev/42059784): Remove this once the underlying issue is + # addressed. exclude_toolchain_tags = [ "hwasan" ] } } @@ -526,7 +527,8 @@ crashpad_loadable_module("crashpad_snapshot_test_module_large") { deps += [ "$mini_chromium_source_parent:base" ] if (crashpad_is_in_fuchsia) { - # TODO(fxbug.dev/108368): Remove this once the underlying issue is addressed. + # TODO(fxbug.dev/42059784): Remove this once the underlying issue is + # addressed. exclude_toolchain_tags = [ "hwasan" ] } } @@ -545,7 +547,8 @@ crashpad_loadable_module("crashpad_snapshot_test_module_small") { deps += [ "$mini_chromium_source_parent:base" ] if (crashpad_is_in_fuchsia) { - # TODO(fxbug.dev/108368): Remove this once the underlying issue is addressed. + # TODO(fxbug.dev/42059784): Remove this once the underlying issue is + # addressed. exclude_toolchain_tags = [ "hwasan" ] } } @@ -560,7 +563,8 @@ if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) && ldflags = [ "-Wl,--hash-style=both" ] if (crashpad_is_in_fuchsia) { - # TODO(fxbug.dev/108368): Remove this once the underlying issue is addressed. + # TODO(fxbug.dev/42059784): Remove this once the underlying issue is + # addressed. exclude_toolchain_tags = [ "hwasan" ] } } diff --git a/shared/sentry/src/external/crashpad/snapshot/capture_memory.cc b/shared/sentry/src/external/crashpad/snapshot/capture_memory.cc index c1c6fba58..f2ff5d74b 100644 --- a/shared/sentry/src/external/crashpad/snapshot/capture_memory.cc +++ b/shared/sentry/src/external/crashpad/snapshot/capture_memory.cc @@ -22,9 +22,10 @@ #include #include -#include +#include "base/containers/heap_array.h" #include "base/logging.h" +#include "build/build_config.h" #include "snapshot/memory_snapshot.h" namespace crashpad { @@ -140,16 +141,16 @@ void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory, return; } - std::unique_ptr buffer(new uint8_t[memory.Size()]); - if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) { + auto buffer = base::HeapArray::Uninit(memory.Size()); + if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.data())) { LOG(ERROR) << "ReadMemory"; return; } if (delegate->Is64Bit()) - CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); + CaptureAtPointersInRange(buffer.data(), buffer.size(), delegate); else - CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); + CaptureAtPointersInRange(buffer.data(), buffer.size(), delegate); } } // namespace internal diff --git a/shared/sentry/src/external/crashpad/snapshot/cpu_context.cc b/shared/sentry/src/external/crashpad/snapshot/cpu_context.cc index 492a0f7f9..71bf4ace4 100644 --- a/shared/sentry/src/external/crashpad/snapshot/cpu_context.cc +++ b/shared/sentry/src/external/crashpad/snapshot/cpu_context.cc @@ -174,7 +174,7 @@ uint64_t CPUContext::InstructionPointer() const { case kCPUArchitectureRISCV64: return riscv64->pc; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return ~0ull; } } @@ -192,7 +192,7 @@ uint64_t CPUContext::StackPointer() const { case kCPUArchitectureRISCV64: return riscv64->regs[1]; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return ~0ull; } } @@ -202,12 +202,12 @@ uint64_t CPUContext::ShadowStackPointer() const { case kCPUArchitectureX86: case kCPUArchitectureARM: case kCPUArchitectureARM64: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; case kCPUArchitectureX86_64: return x86_64->xstate.cet_u.ssp; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return ~0ull; } } @@ -221,7 +221,7 @@ bool CPUContext::HasShadowStack() const { case kCPUArchitectureX86_64: return x86_64->xstate.cet_u.cetmsr != 0; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; } } @@ -238,7 +238,7 @@ bool CPUContext::Is64Bit() const { case kCPUArchitectureMIPSEL: return false; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; } } diff --git a/shared/sentry/src/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc b/shared/sentry/src/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc index be71ca22a..c595b92bd 100644 --- a/shared/sentry/src/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc +++ b/shared/sentry/src/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc @@ -74,7 +74,7 @@ bool ExceptionSnapshotFuchsia::Initialize( #if defined(ARCH_CPU_X86_64) context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_arch_; - // TODO(fxbug.dev/5496): Add vector context. + // TODO(fxbug.dev/42132536): Add vector context. InitializeCPUContextX86_64( t->general_registers, t->fp_registers, context_.x86_64); #elif defined(ARCH_CPU_ARM64) diff --git a/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc b/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc index 5bf2acf7d..8b10e2ccc 100644 --- a/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc +++ b/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc @@ -47,12 +47,12 @@ void GetStackRegions( #error Port #endif - // TODO(fxbug.dev/74897): make this work for stack overflows, e.g., by looking - // up using the initial stack pointer (sp) when the thread was created. Right - // now, it gets the stack by getting the mapping that contains the current sp. - // But in the case of stack overflows, the current sp is by definition outside - // of the stack so the mapping returned is not the stack and fails the type - // check, at least on arm64. + // TODO(fxbug.dev/42154629): make this work for stack overflows, e.g., by + // looking up using the initial stack pointer (sp) when the thread was + // created. Right now, it gets the stack by getting the mapping that contains + // the current sp. But in the case of stack overflows, the current sp is by + // definition outside of the stack so the mapping returned is not the stack + // and fails the type check, at least on arm64. zx_info_maps_t range_with_sp; if (!memory_map.FindMappingForAddress(sp, &range_with_sp)) { LOG(ERROR) << "stack pointer not found in mapping"; @@ -235,8 +235,8 @@ void ProcessReaderFuchsia::InitializeModules() { // Crashpad needs to use the same module name at run time for symbol // resolution to work properly. // - // TODO: https://fxbug.dev/6057 - once Crashpad switches to elf-search, the - // following overwrites won't be necessary as only shared libraries will + // TODO: https://fxbug.dev/42138764 - once Crashpad switches to elf-search, + // the following overwrites won't be necessary as only shared libraries will // have a soname at runtime, just like at build time. // // * For shared libraries, the soname is used as module name at build time, diff --git a/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc b/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc index f1a7e7b19..1f51a4c90 100644 --- a/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/shared/sentry/src/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -114,7 +114,8 @@ crashpad::ProcessID ProcessSnapshotFuchsia::ProcessID() const { crashpad::ProcessID ProcessSnapshotFuchsia::ParentProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // TODO(scottmg): https://crashpad.chromium.org/bug/196 + NOTREACHED_IN_MIGRATION(); return 0; } diff --git a/shared/sentry/src/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc b/shared/sentry/src/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc index 81a9d3018..a764e855a 100644 --- a/shared/sentry/src/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc +++ b/shared/sentry/src/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc @@ -75,7 +75,7 @@ uint32_t SystemSnapshotFuchsia::CPURevision() const { #if defined(ARCH_CPU_X86_64) return cpuid_.Revision(); #else - // TODO: https://fxbug.dev/5561 - Read actual revision. + // TODO: https://fxbug.dev/42133257 - Read actual revision. return 0; #endif } @@ -90,7 +90,7 @@ std::string SystemSnapshotFuchsia::CPUVendor() const { #if defined(ARCH_CPU_X86_64) return cpuid_.Vendor(); #else - // TODO: https://fxbug.dev/5561 - Read actual vendor. + // TODO: https://fxbug.dev/42133257 - Read actual vendor. return std::string(); #endif } @@ -108,7 +108,7 @@ uint32_t SystemSnapshotFuchsia::CPUX86Signature() const { #if defined(ARCH_CPU_X86_64) return cpuid_.Signature(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -118,7 +118,7 @@ uint64_t SystemSnapshotFuchsia::CPUX86Features() const { #if defined(ARCH_CPU_X86_64) return cpuid_.Features(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -128,7 +128,7 @@ uint64_t SystemSnapshotFuchsia::CPUX86ExtendedFeatures() const { #if defined(ARCH_CPU_X86_64) return cpuid_.ExtendedFeatures(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -137,7 +137,7 @@ uint32_t SystemSnapshotFuchsia::CPUX86Leaf7Features() const { #if defined(ARCH_CPU_X86_64) return cpuid_.Leaf7Features(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -147,7 +147,7 @@ bool SystemSnapshotFuchsia::CPUX86SupportsDAZ() const { #if defined(ARCH_CPU_X86_64) return cpuid_.SupportsDAZ(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; #endif } @@ -193,7 +193,7 @@ bool SystemSnapshotFuchsia::NXEnabled() const { #if defined(ARCH_CPU_X86_64) return cpuid_.NXEnabled(); #else - // TODO: https://fxbug.dev/5561 - Read actual NX bit value. + // TODO: https://fxbug.dev/42133257 - Read actual NX bit value. return false; #endif } diff --git a/shared/sentry/src/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/shared/sentry/src/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc index 759895254..587e6a91f 100644 --- a/shared/sentry/src/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc +++ b/shared/sentry/src/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc @@ -40,7 +40,7 @@ bool ThreadSnapshotFuchsia::Initialize( #if defined(ARCH_CPU_X86_64) context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_arch_; - // TODO(fxbug.dev/5496): Add vector context. + // TODO(fxbug.dev/42132536): Add vector context. InitializeCPUContextX86_64( thread.general_registers, thread.fp_registers, context_.x86_64); #elif defined(ARCH_CPU_ARM64) diff --git a/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc b/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc index e34784540..9f1852a61 100644 --- a/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc +++ b/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc @@ -149,6 +149,33 @@ bool ExceptionSnapshotIOSIntermediateDump::InitializeFromSignal( GetDataValueFromMap(exception_data, Key::kSignalCode, &code); codes_.push_back(code); + const IOSIntermediateDumpList* thread_context_memory_regions = + GetListFromMap(exception_data, Key::kThreadContextMemoryRegions); + if (thread_context_memory_regions) { + for (auto& region : *thread_context_memory_regions) { + vm_address_t address; + const IOSIntermediateDumpData* region_data = + region->GetAsData(Key::kThreadContextMemoryRegionData); + if (!region_data) + continue; + if (GetDataValueFromMap( + region.get(), Key::kThreadContextMemoryRegionAddress, &address)) { + const std::vector& bytes = region_data->bytes(); + vm_size_t data_size = bytes.size(); + if (data_size == 0) + continue; + + const vm_address_t data = + reinterpret_cast(bytes.data()); + + auto memory = + std::make_unique(); + memory->Initialize(address, data, data_size); + extra_memory_.push_back(std::move(memory)); + } + } + } + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } @@ -281,8 +308,11 @@ const std::vector& ExceptionSnapshotIOSIntermediateDump::Codes() std::vector ExceptionSnapshotIOSIntermediateDump::ExtraMemory() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); + std::vector extra_memory; + for (const auto& memory : extra_memory_) { + extra_memory.push_back(memory.get()); + } + return extra_memory; } void ExceptionSnapshotIOSIntermediateDump::LoadContextFromThread( diff --git a/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h b/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h index 90966df1f..dc9d5954d 100644 --- a/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h +++ b/shared/sentry/src/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h @@ -23,6 +23,7 @@ #include "build/build_config.h" #include "snapshot/cpu_context.h" #include "snapshot/exception_snapshot.h" +#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" #include "util/ios/ios_intermediate_dump_map.h" #include "util/mach/mach_extensions.h" #include "util/misc/initialization_state_dcheck.h" @@ -106,6 +107,8 @@ class ExceptionSnapshotIOSIntermediateDump final : public ExceptionSnapshot { uintptr_t exception_address_; uint32_t exception_; uint32_t exception_info_; + std::vector> + extra_memory_; InitializationStateDcheck initialized_; }; diff --git a/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc b/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc index c502b3c02..609ea654c 100644 --- a/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc +++ b/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc @@ -122,6 +122,9 @@ bool ProcessSnapshotIOSIntermediateDump::InitializeWithFileInterface( } system_.Initialize(system_info); + annotations_simple_map_["crashpad_uptime_ns"] = + std::to_string(system_.CrashpadUptime()); + // Threads const IOSIntermediateDumpList* thread_list = GetListFromMap(root_map, Key::kThreads); diff --git a/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc index 6d64b1d07..69cb43ed6 100644 --- a/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc +++ b/shared/sentry/src/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc @@ -160,6 +160,10 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { EXPECT_TRUE(writer->AddProperty(Key::kWired, &count)); EXPECT_TRUE(writer->AddProperty(Key::kFree, &count)); } + + uint64_t crashpad_report_time_nanos = 1234567890; + EXPECT_TRUE( + writer->AddProperty(Key::kCrashpadUptime, &crashpad_report_time_nanos)); } void WriteAnnotations(IOSIntermediateDumpWriter* writer, @@ -491,6 +495,9 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { ExpectModules( snapshot.Modules(), expect_module_path, expect_long_annotations); ExpectMachException(*snapshot.Exception()); + + auto map = snapshot.AnnotationsSimpleMap(); + EXPECT_EQ(map["crashpad_uptime_ns"], "1234567890"); } void CloseWriter() { EXPECT_TRUE(writer_->Close()); } @@ -570,6 +577,18 @@ TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptySignalDump) { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSignalException); uint64_t thread_id = 1; EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + { + IOSIntermediateDumpWriter::ScopedArray contextMemoryRegions( + writer(), Key::kThreadContextMemoryRegions); + IOSIntermediateDumpWriter::ScopedArrayMap memoryMap(writer()); + + std::string random_data("random_data"); + EXPECT_TRUE(writer()->AddProperty( + Key::kThreadContextMemoryRegionAddress, &thread_id)); + EXPECT_TRUE(writer()->AddProperty(Key::kThreadContextMemoryRegionData, + random_data.c_str(), + random_data.length())); + } } { IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), @@ -582,6 +601,12 @@ TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptySignalDump) { CloseWriter(); ProcessSnapshotIOSIntermediateDump process_snapshot; ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_EQ(process_snapshot.Exception()->ExtraMemory().size(), 1u); + ReadToString delegate; + for (auto memory : process_snapshot.Exception()->ExtraMemory()) { + memory->Read(&delegate); + EXPECT_STREQ(delegate.result.c_str(), "random_data"); + } EXPECT_FALSE(IsRegularFile(path())); EXPECT_TRUE(DumpSnapshot(process_snapshot)); } @@ -768,6 +793,22 @@ TEST_F(ProcessSnapshotIOSIntermediateDumpTest, FuzzTestCases) { EXPECT_TRUE(process_snapshot4.InitializeWithFilePath(fuzz_path, {})); } +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, WriteNoThreads) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + WriteMachException(writer()); + } + CloseWriter(); + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc b/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc index 15f66992b..6a9f2c1c5 100644 --- a/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc +++ b/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc @@ -119,6 +119,8 @@ void SystemSnapshotIOSIntermediateDump::Initialize( } } + GetDataValueFromMap(system_data, Key::kCrashpadUptime, &crashpad_uptime_ns_); + INITIALIZATION_STATE_SET_VALID(initialized_); } @@ -249,5 +251,10 @@ uint64_t SystemSnapshotIOSIntermediateDump::AddressMask() const { return address_mask_; } +uint64_t SystemSnapshotIOSIntermediateDump::CrashpadUptime() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return crashpad_uptime_ns_; +} + } // namespace internal } // namespace crashpad diff --git a/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h b/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h index 6cc09ac78..339139b7c 100644 --- a/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h +++ b/shared/sentry/src/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h @@ -75,6 +75,10 @@ class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot { std::string* daylight_name) const override; uint64_t AddressMask() const override; + //! \brief Returns the number of nanoseconds between Crashpad initialization + //! and snapshot generation. + uint64_t CrashpadUptime() const; + private: std::string os_version_build_; std::string machine_description_; @@ -93,6 +97,7 @@ class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot { std::string standard_name_; std::string daylight_name_; uint64_t address_mask_; + uint64_t crashpad_uptime_ns_; InitializationStateDcheck initialized_; }; diff --git a/shared/sentry/src/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc b/shared/sentry/src/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc index 94f45f1eb..b351ccaa7 100644 --- a/shared/sentry/src/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc +++ b/shared/sentry/src/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc @@ -66,8 +66,9 @@ void InitializeContext(NativeCPUContext* context) { void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { EXPECT_EQ(actual.architecture, kCPUArchitectureX86); - EXPECT_EQ(actual.x86->eax, - bit_cast(expected.ucontext.uc_mcontext.gregs[REG_EAX])); + EXPECT_EQ( + actual.x86->eax, + base::bit_cast(expected.ucontext.uc_mcontext.gregs[REG_EAX])); for (unsigned int byte_offset = 0; byte_offset < sizeof(actual.x86->fxsave); ++byte_offset) { SCOPED_TRACE(base::StringPrintf("byte offset = %u\n", byte_offset)); @@ -87,7 +88,7 @@ void InitializeContext(NativeCPUContext* context) { void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { EXPECT_EQ(actual.architecture, kCPUArchitectureX86_64); EXPECT_EQ(actual.x86_64->rax, - bit_cast(expected.uc_mcontext.gregs[REG_RAX])); + base::bit_cast(expected.uc_mcontext.gregs[REG_RAX])); for (unsigned int byte_offset = 0; byte_offset < sizeof(actual.x86_64->fxsave); ++byte_offset) { diff --git a/shared/sentry/src/external/crashpad/snapshot/linux/process_reader_linux_test.cc b/shared/sentry/src/external/crashpad/snapshot/linux/process_reader_linux_test.cc index a9cc11e1d..9c008edc1 100644 --- a/shared/sentry/src/external/crashpad/snapshot/linux/process_reader_linux_test.cc +++ b/shared/sentry/src/external/crashpad/snapshot/linux/process_reader_linux_test.cc @@ -52,6 +52,7 @@ #include "util/misc/address_sanitizer.h" #include "util/misc/from_pointer_cast.h" #include "util/misc/memory_sanitizer.h" +#include "util/posix/scoped_mmap.h" #include "util/synchronization/semaphore.h" #if BUILDFLAG(IS_ANDROID) @@ -180,14 +181,21 @@ class TestThreadPool { << ErrnoMessage("pthread_attr_init"); if (stack_size > 0) { - void* stack_ptr; - errno = posix_memalign(&stack_ptr, getpagesize(), stack_size); - ASSERT_EQ(errno, 0) << ErrnoMessage("posix_memalign"); - - thread->stack.reset(reinterpret_cast(stack_ptr)); - - ASSERT_EQ(pthread_attr_setstack(&attr, thread->stack.get(), stack_size), - 0) + const size_t page_size = getpagesize(); + DCHECK_EQ(stack_size % page_size, 0u); + size_t stack_alloc_size = 2 * page_size + stack_size; + + ASSERT_TRUE(thread->stack.ResetMmap(nullptr, + stack_alloc_size, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)); + char* stack_ptr = thread->stack.addr_as() + page_size; + ASSERT_EQ(mprotect(stack_ptr, stack_size, PROT_READ | PROT_WRITE), 0) + << "mprotect"; + + ASSERT_EQ(pthread_attr_setstack(&attr, stack_ptr, stack_size), 0) << ErrnoMessage("pthread_attr_setstack"); thread->expectation.max_stack_size = stack_size; } @@ -238,7 +246,7 @@ class TestThreadPool { pthread_t pthread; ThreadExpectation expectation; - std::unique_ptr stack; + ScopedMmap stack; Semaphore ready_semaphore; Semaphore exit_semaphore; pid_t tid; diff --git a/shared/sentry/src/external/crashpad/snapshot/linux/system_snapshot_linux.cc b/shared/sentry/src/external/crashpad/snapshot/linux/system_snapshot_linux.cc index b32f22d78..3eb91b4f4 100644 --- a/shared/sentry/src/external/crashpad/snapshot/linux/system_snapshot_linux.cc +++ b/shared/sentry/src/external/crashpad/snapshot/linux/system_snapshot_linux.cc @@ -276,7 +276,7 @@ uint32_t SystemSnapshotLinux::CPUX86Signature() const { #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Signature(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -286,7 +286,7 @@ uint64_t SystemSnapshotLinux::CPUX86Features() const { #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Features(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -296,7 +296,7 @@ uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const { #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.ExtendedFeatures(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -306,7 +306,7 @@ uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const { #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Leaf7Features(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -316,7 +316,7 @@ bool SystemSnapshotLinux::CPUX86SupportsDAZ() const { #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.SupportsDAZ(); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; #endif // ARCH_CPU_X86_FMAILY } diff --git a/shared/sentry/src/external/crashpad/snapshot/mac/cpu_context_mac.cc b/shared/sentry/src/external/crashpad/snapshot/mac/cpu_context_mac.cc index 69a54bafd..c8eddfcd3 100644 --- a/shared/sentry/src/external/crashpad/snapshot/mac/cpu_context_mac.cc +++ b/shared/sentry/src/external/crashpad/snapshot/mac/cpu_context_mac.cc @@ -196,7 +196,7 @@ thread_state_flavor_t InitializeCPUContextX86Flavor( } default: { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return THREAD_STATE_NONE; } } @@ -377,7 +377,7 @@ thread_state_flavor_t InitializeCPUContextX86_64Flavor( } default: { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return THREAD_STATE_NONE; } } @@ -553,7 +553,7 @@ thread_state_flavor_t InitializeCPUContextARM64Flavor( } default: { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return THREAD_STATE_NONE; } } diff --git a/shared/sentry/src/external/crashpad/snapshot/mac/process_types.cc b/shared/sentry/src/external/crashpad/snapshot/mac/process_types.cc index bc3031736..b977b78f3 100644 --- a/shared/sentry/src/external/crashpad/snapshot/mac/process_types.cc +++ b/shared/sentry/src/external/crashpad/snapshot/mac/process_types.cc @@ -19,8 +19,8 @@ #include #include -#include +#include "base/containers/heap_array.h" #include "snapshot/mac/process_types/internal.h" #include "util/process/process_memory_mac.h" @@ -245,8 +245,9 @@ inline void Assign(UInt64Array4* destination, mach_vm_address_t address, \ size_t count, \ struct_name* generic) { \ - std::unique_ptr specific(new T[count]); \ - if (!T::ReadArrayInto(process_reader, address, count, &specific[0])) { \ + auto specific = base::HeapArray::Uninit(count); \ + if (!T::ReadArrayInto( \ + process_reader, address, specific.size(), specific.data())) { \ return false; \ } \ for (size_t index = 0; index < count; ++index) { \ diff --git a/shared/sentry/src/external/crashpad/snapshot/mac/system_snapshot_mac.cc b/shared/sentry/src/external/crashpad/snapshot/mac/system_snapshot_mac.cc index 334627ffe..933626fc6 100644 --- a/shared/sentry/src/external/crashpad/snapshot/mac/system_snapshot_mac.cc +++ b/shared/sentry/src/external/crashpad/snapshot/mac/system_snapshot_mac.cc @@ -204,7 +204,7 @@ uint32_t SystemSnapshotMac::CPUX86Signature() const { #if defined(ARCH_CPU_X86_FAMILY) return ReadIntSysctlByName("machdep.cpu.signature", 0); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -215,7 +215,7 @@ uint64_t SystemSnapshotMac::CPUX86Features() const { #if defined(ARCH_CPU_X86_FAMILY) return ReadIntSysctlByName("machdep.cpu.feature_bits", 0); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -226,7 +226,7 @@ uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const { #if defined(ARCH_CPU_X86_FAMILY) return ReadIntSysctlByName("machdep.cpu.extfeature_bits", 0); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -251,7 +251,7 @@ uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const { CallCPUID(7, &eax, &ebx, &ecx, &edx); return ebx; #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -290,7 +290,7 @@ bool SystemSnapshotMac::CPUX86SupportsDAZ() const { // Test the DAZ bit. return fxsave.mxcsr_mask & (1 << 6); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; #endif } diff --git a/shared/sentry/src/external/crashpad/snapshot/memory_snapshot_generic.h b/shared/sentry/src/external/crashpad/snapshot/memory_snapshot_generic.h index 0187ada68..dd03bdbb8 100644 --- a/shared/sentry/src/external/crashpad/snapshot/memory_snapshot_generic.h +++ b/shared/sentry/src/external/crashpad/snapshot/memory_snapshot_generic.h @@ -18,6 +18,7 @@ #include #include +#include "base/containers/heap_array.h" #include "base/logging.h" #include "base/numerics/safe_math.h" #include "snapshot/memory_snapshot.h" @@ -79,11 +80,11 @@ class MemorySnapshotGeneric final : public MemorySnapshot { return delegate->MemorySnapshotDelegateRead(nullptr, size_); } - std::unique_ptr buffer(new uint8_t[size_]); - if (!process_memory_->Read(address_, size_, buffer.get())) { + auto buffer = base::HeapArray::Uninit(size_); + if (!process_memory_->Read(address_, buffer.size(), buffer.data())) { return false; } - return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); + return delegate->MemorySnapshotDelegateRead(buffer.data(), buffer.size()); } const MemorySnapshot* MergeWithOtherSnapshot( diff --git a/shared/sentry/src/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc b/shared/sentry/src/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc index 50e5ea859..8d461b357 100644 --- a/shared/sentry/src/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc +++ b/shared/sentry/src/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc @@ -228,14 +228,14 @@ std::vector ModuleSnapshotMinidump::AnnotationObjects() std::set> ModuleSnapshotMinidump::ExtraMemoryRanges() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return std::set>(); } std::vector ModuleSnapshotMinidump::CustomMinidumpStreams() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return std::vector(); } diff --git a/shared/sentry/src/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc b/shared/sentry/src/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc index 03f321719..472e02576 100644 --- a/shared/sentry/src/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc +++ b/shared/sentry/src/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc @@ -135,7 +135,7 @@ crashpad::ProcessID ProcessSnapshotMinidump::ProcessID() const { crashpad::ProcessID ProcessSnapshotMinidump::ParentProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return 0; } @@ -208,7 +208,7 @@ std::vector ProcessSnapshotMinidump::Modules() const { std::vector ProcessSnapshotMinidump::UnloadedModules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return unloaded_modules_; } @@ -229,7 +229,7 @@ std::vector ProcessSnapshotMinidump::MemoryMap() std::vector ProcessSnapshotMinidump::Handles() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return std::vector(); } diff --git a/shared/sentry/src/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc b/shared/sentry/src/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc index 58bd7b364..cfdedadf2 100644 --- a/shared/sentry/src/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc +++ b/shared/sentry/src/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc @@ -100,36 +100,36 @@ std::string SystemSnapshotMinidump::CPUVendor() const { void SystemSnapshotMinidump::CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 } uint32_t SystemSnapshotMinidump::CPUX86Signature() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return 0; } uint64_t SystemSnapshotMinidump::CPUX86Features() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return 0; } uint64_t SystemSnapshotMinidump::CPUX86ExtendedFeatures() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return 0; } uint32_t SystemSnapshotMinidump::CPUX86Leaf7Features() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return 0; } bool SystemSnapshotMinidump::CPUX86SupportsDAZ() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return false; } @@ -178,13 +178,13 @@ std::string SystemSnapshotMinidump::OSVersionFull() const { std::string SystemSnapshotMinidump::MachineDescription() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return std::string(); } bool SystemSnapshotMinidump::NXEnabled() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return false; } @@ -194,12 +194,12 @@ void SystemSnapshotMinidump::TimeZone(DaylightSavingTimeStatus* dst_status, std::string* standard_name, std::string* daylight_name) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 } uint64_t SystemSnapshotMinidump::AddressMask() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // https://crashpad.chromium.org/bug/10 + NOTREACHED_IN_MIGRATION(); // https://crashpad.chromium.org/bug/10 return 0; } diff --git a/shared/sentry/src/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc b/shared/sentry/src/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc index 0ad2ee975..c76722683 100644 --- a/shared/sentry/src/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc +++ b/shared/sentry/src/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc @@ -99,9 +99,11 @@ ModuleSnapshotSanitized::AnnotationsSimpleMap() const { std::map annotations = snapshot_->AnnotationsSimpleMap(); if (allowed_annotations_) { - for (auto kv = annotations.begin(); kv != annotations.end(); ++kv) { - if (!KeyIsAllowed(kv->first, *allowed_annotations_)) { - annotations.erase(kv); + for (auto kv = annotations.begin(); kv != annotations.end();) { + if (KeyIsAllowed(kv->first, *allowed_annotations_)) { + ++kv; + } else { + kv = annotations.erase(kv); } } } diff --git a/shared/sentry/src/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc b/shared/sentry/src/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc index 329c3c760..e9a835b1f 100644 --- a/shared/sentry/src/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc +++ b/shared/sentry/src/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc @@ -129,7 +129,7 @@ void ChildTestFunction() { CRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) { ChildTestFunction(); - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return EXIT_SUCCESS; } diff --git a/shared/sentry/src/external/crashpad/snapshot/snapshot_constants.h b/shared/sentry/src/external/crashpad/snapshot/snapshot_constants.h index d33d30a62..2006e7b5c 100644 --- a/shared/sentry/src/external/crashpad/snapshot/snapshot_constants.h +++ b/shared/sentry/src/external/crashpad/snapshot/snapshot_constants.h @@ -21,7 +21,7 @@ namespace crashpad { //! a client process. //! //! \note This maximum was chosen arbitrarily and may change in the future. -constexpr size_t kMaxNumberOfAnnotations = 200; +constexpr size_t kMaxNumberOfAnnotations = 400; } // namespace crashpad diff --git a/shared/sentry/src/external/crashpad/snapshot/win/pe_image_reader.cc b/shared/sentry/src/external/crashpad/snapshot/win/pe_image_reader.cc index e86c06e8c..5ca53f2f2 100644 --- a/shared/sentry/src/external/crashpad/snapshot/win/pe_image_reader.cc +++ b/shared/sentry/src/external/crashpad/snapshot/win/pe_image_reader.cc @@ -19,8 +19,8 @@ #include #include -#include +#include "base/containers/heap_array.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "client/crashpad_info.h" @@ -183,17 +183,17 @@ bool PEImageReader::DebugDirectoryInformation(UUID* uuid, continue; } - std::unique_ptr data(new char[debug_directory.SizeOfData]); + auto data = base::HeapArray::Uninit(debug_directory.SizeOfData); if (!module_subrange_reader_.ReadMemory( Address() + debug_directory.AddressOfRawData, - debug_directory.SizeOfData, - data.get())) { + data.size(), + data.data())) { LOG(WARNING) << "could not read debug directory from " << module_subrange_reader_.name(); return false; } - if (*reinterpret_cast(data.get()) != + if (*reinterpret_cast(data.data()) != CodeViewRecordPDB70::kSignature) { LOG(WARNING) << "encountered non-7.0 CodeView debug record in " << module_subrange_reader_.name(); @@ -201,7 +201,7 @@ bool PEImageReader::DebugDirectoryInformation(UUID* uuid, } CodeViewRecordPDB70* codeview = - reinterpret_cast(data.get()); + reinterpret_cast(data.data()); *uuid = codeview->uuid; *age = codeview->age; // This is a NUL-terminated string encoded in the codepage of the system diff --git a/shared/sentry/src/external/crashpad/snapshot/win/system_snapshot_win.cc b/shared/sentry/src/external/crashpad/snapshot/win/system_snapshot_win.cc index 1eaad8a96..65d06b27f 100644 --- a/shared/sentry/src/external/crashpad/snapshot/win/system_snapshot_win.cc +++ b/shared/sentry/src/external/crashpad/snapshot/win/system_snapshot_win.cc @@ -360,7 +360,7 @@ uint32_t SystemSnapshotWin::CPUX86Signature() const { __cpuid(cpu_info, 1); return cpu_info[0]; #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -375,7 +375,7 @@ uint64_t SystemSnapshotWin::CPUX86Features() const { return (static_cast(cpu_info[2]) << 32) | static_cast(cpu_info[3]); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -391,7 +391,7 @@ uint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const { return (static_cast(cpu_info[2]) << 32) | static_cast(cpu_info[3]); #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -410,7 +410,7 @@ uint32_t SystemSnapshotWin::CPUX86Leaf7Features() const { __cpuidex(cpu_info, 7, 0); return cpu_info[1]; #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } @@ -441,7 +441,7 @@ bool SystemSnapshotWin::CPUX86SupportsDAZ() const { // Test the DAZ bit. return (mxcsr_mask & (1 << 6)) != 0; #else - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return 0; #endif } diff --git a/shared/sentry/src/external/crashpad/test/ios/crash_type_xctest.mm b/shared/sentry/src/external/crashpad/test/ios/crash_type_xctest.mm index 8d4038db0..646c9dfd4 100644 --- a/shared/sentry/src/external/crashpad/test/ios/crash_type_xctest.mm +++ b/shared/sentry/src/external/crashpad/test/ios/crash_type_xctest.mm @@ -14,6 +14,7 @@ #import #include +#include #include @@ -21,9 +22,30 @@ #include "build/build_config.h" #include "client/length_delimited_ring_buffer.h" #import "test/ios/host/cptest_shared_object.h" +#include "util/mac/sysctl.h" #include "util/mach/exception_types.h" #include "util/mach/mach_extensions.h" +namespace crashpad { +namespace { + +#if TARGET_OS_SIMULATOR +// macOS 14.0 is 23A344, macOS 13.6.5 is 22G621, so if the first two characters +// in the kern.osversion are > 22, this build will reproduce the simulator bug +// in crbug.com/328282286 +bool IsMacOSVersion143OrGreaterAndiOS16OrLess() { + if (__builtin_available(iOS 17, *)) { + return false; + } + + std::string build = crashpad::ReadStringSysctlByName("kern.osversion", false); + return std::stoi(build.substr(0, 2)) > 22; +} +#endif + +} // namespace +} // namespace crashpad + @interface CPTestTestCase : XCTestCase { XCUIApplication* app_; CPTestSharedObject* rootObject_; @@ -148,7 +170,10 @@ - (void)testException { [rootObject_ crashException]; // After https://reviews.llvm.org/D141222 exceptions call // __libcpp_verbose_abort, which Chromium sets to `brk 0` in release. -#if defined(CRASHPAD_IS_IN_CHROMIUM) && defined(NDEBUG) + // After https://crrev.com/c/5375084, Chromium does not set `brk 0` for local + // release builds and official DCHECK builds. +#if defined(CRASHPAD_IS_IN_CHROMIUM) && defined(NDEBUG) && \ + defined(OFFICIAL_BUILD) && !defined(DCHECK_ALWAYS_ON) [self verifyCrashReportException:SIGABRT]; #else [self verifyCrashReportException:EXC_SOFT_SIGNAL]; @@ -317,6 +342,14 @@ - (void)testCrashWithDyldErrorString { #endif - (void)testCrashWithAnnotations { +#if TARGET_OS_SIMULATOR + // This test will fail on older (* threads) { // TODO(jperaza): Implement this if/when it's needed. - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; } ssize_t FakePtraceConnection::ReadUpTo(VMAddress address, size_t size, void* buffer) { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return false; } diff --git a/shared/sentry/src/external/crashpad/test/multiprocess_posix_test.cc b/shared/sentry/src/external/crashpad/test/multiprocess_posix_test.cc index c7015b857..02bdb7b29 100644 --- a/shared/sentry/src/external/crashpad/test/multiprocess_posix_test.cc +++ b/shared/sentry/src/external/crashpad/test/multiprocess_posix_test.cc @@ -157,7 +157,7 @@ class TestMultiprocessClosePipe final : public Multiprocess { who_closes_(who_closes), what_closes_(what_closes) { // Fails under "threadsafe" mode on macOS 10.11. - testing::GTEST_FLAG(death_test_style) = "fast"; + GTEST_FLAG_SET(death_test_style, "fast"); } TestMultiprocessClosePipe(const TestMultiprocessClosePipe&) = delete; diff --git a/shared/sentry/src/external/crashpad/test/scoped_guarded_page_test.cc b/shared/sentry/src/external/crashpad/test/scoped_guarded_page_test.cc index 2be2d51bd..d8fc6749d 100644 --- a/shared/sentry/src/external/crashpad/test/scoped_guarded_page_test.cc +++ b/shared/sentry/src/external/crashpad/test/scoped_guarded_page_test.cc @@ -23,7 +23,7 @@ namespace test { namespace { TEST(ScopedGuardedPage, BasicFunctionality) { - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + GTEST_FLAG_SET(death_test_style, "threadsafe"); ScopedGuardedPage page; char* address = (char*)page.Pointer(); diff --git a/shared/sentry/src/external/crashpad/test/win/win_child_process.cc b/shared/sentry/src/external/crashpad/test/win/win_child_process.cc index d4d9a462a..e31a977d0 100644 --- a/shared/sentry/src/external/crashpad/test/win/win_child_process.cc +++ b/shared/sentry/src/external/crashpad/test/win/win_child_process.cc @@ -44,7 +44,6 @@ bool GetSwitch(const char* switch_name, std::string* value) { ScopedLocalAlloc scoped_args(args); // Take ownership. if (!args) { PLOG(FATAL) << "CommandLineToArgvW"; - return false; } std::string switch_name_with_equals(switch_name); diff --git a/shared/sentry/src/external/crashpad/third_party/cpp-httplib/README.crashpad b/shared/sentry/src/external/crashpad/third_party/cpp-httplib/README.crashpad index a1ba93aaa..8246a7358 100644 --- a/shared/sentry/src/external/crashpad/third_party/cpp-httplib/README.crashpad +++ b/shared/sentry/src/external/crashpad/third_party/cpp-httplib/README.crashpad @@ -14,3 +14,4 @@ Local Modifications: - Exclude test/ and example/ subdirs. - Patch httplib.h to use #include "third_party/zlib/zlib_crashpad.h" instead of . +- Make `this` capture explicit to avoid errors in C++20. diff --git a/shared/sentry/src/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h b/shared/sentry/src/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h index dadab1d8e..1165e2601 100644 --- a/shared/sentry/src/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h +++ b/shared/sentry/src/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h @@ -1684,7 +1684,7 @@ inline bool Server::listen_internal() } // TODO: Use thread pool... - std::thread([=]() { + std::thread([=, this]() { { std::lock_guard guard(running_threads_mutex_); running_threads_++; @@ -1861,7 +1861,7 @@ inline bool Client::is_valid() const inline socket_t Client::create_client_socket() const { return detail::create_socket(host_.c_str(), port_, - [=](socket_t sock, struct addrinfo& ai) -> bool { + [this](socket_t sock, struct addrinfo& ai) -> bool { detail::set_nonblocking(sock, true); auto ret = connect(sock, ai.ai_addr, ai.ai_addrlen); diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/CMakeLists.txt b/shared/sentry/src/external/crashpad/third_party/mini_chromium/CMakeLists.txt index 48bfbe8df..5876937b3 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/CMakeLists.txt +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/CMakeLists.txt @@ -15,7 +15,10 @@ mc_append_sources( check.h check_op.h compiler_specific.h + containers/checked_iterators.h + containers/dynamic_extent.h containers/span.h + containers/util.h debug/alias.cc debug/alias.h files/file_path.cc @@ -24,15 +27,19 @@ mc_append_sources( files/scoped_file.cc files/scoped_file.h format_macros.h + immediate_crash.h logging.cc logging.h memory/free_deleter.h memory/page_size.h + memory/raw_ptr_exclusion.h memory/scoped_policy.h metrics/histogram_functions.h metrics/histogram_macros.h metrics/persistent_histogram_allocator.h notreached.h + numerics/basic_ops_impl.h + numerics/byte_conversions.h numerics/checked_math.h numerics/checked_math_impl.h numerics/clamped_math.h @@ -76,6 +83,7 @@ mc_append_sources( threading/thread_local_storage.cc threading/thread_local_storage.h types/cxx23_to_underlying.h + types/to_address.h ) if(NOT MINGW) @@ -138,7 +146,10 @@ else() endif() if(APPLE) - target_compile_options(mini_chromium PUBLIC -fobjc-arc -fno-objc-arc-exceptions) + target_compile_options(mini_chromium + PUBLIC "-fobjc-arc" "-fno-objc-arc-exceptions" + PRIVATE "-Wno-deprecated-declarations" + ) endif() if(APPLE AND NOT IOS) target_link_libraries(mini_chromium PUBLIC diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn index 690d70b08..73f84fe62 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn @@ -14,7 +14,10 @@ static_library("base") { "check.h", "check_op.h", "compiler_specific.h", + "containers/checked_iterators.h", + "containers/dynamic_extent.h", "containers/span.h", + "containers/util.h", "debug/alias.cc", "debug/alias.h", "files/file_path.cc", @@ -23,15 +26,19 @@ static_library("base") { "files/scoped_file.cc", "files/scoped_file.h", "format_macros.h", + "immediate_crash.h", "logging.cc", "logging.h", "memory/free_deleter.h", "memory/page_size.h", + "memory/raw_ptr_exclusion.h", "memory/scoped_policy.h", "metrics/histogram_functions.h", "metrics/histogram_macros.h", "metrics/persistent_histogram_allocator.h", "notreached.h", + "numerics/basic_ops_impl.h", + "numerics/byte_conversions.h", "numerics/checked_math.h", "numerics/checked_math_impl.h", "numerics/clamped_math.h", @@ -76,6 +83,7 @@ static_library("base") { "threading/thread_local_storage.cc", "threading/thread_local_storage.h", "types/cxx23_to_underlying.h", + "types/to_address.h", ] if (mini_chromium_is_posix || mini_chromium_is_fuchsia) { diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h index 2486fb770..88e2d2d8e 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h @@ -51,13 +51,7 @@ static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32), "incompatible 32-bit atomic layout"); inline void MemoryBarrier() { -#if defined(__GLIBCXX__) - // Work around libstdc++ bug 51038 where atomic_thread_fence was declared but - // not defined, leading to the linker complaining about undefined references. - __atomic_thread_fence(std::memory_order_seq_cst); -#else std::atomic_thread_fence(std::memory_order_seq_cst); -#endif } inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h index 960af382f..b461d7d0b 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h @@ -5,94 +5,41 @@ #ifndef MINI_CHROMIUM_BASE_BIT_CAST_H_ #define MINI_CHROMIUM_BASE_BIT_CAST_H_ -#include #include -#include "base/compiler_specific.h" -#include "build/build_config.h" +namespace base { -// bit_cast is a template function that implements the equivalent -// of "*reinterpret_cast(&source)". We need this in very low-level -// functions like the protobuf library and fast math support. +// This is an equivalent to C++20's std::bit_cast<>(), but with additional +// warnings. It morally does what `*reinterpret_cast(&source)` does, but +// the cast/deref pair is undefined behavior, while bit_cast<>() isn't. // -// float f = 3.14159265358979; -// int i = bit_cast(f); -// // i = 0x40490fdb -// -// The classical address-casting method is: -// -// // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG -// -// The address-casting method actually produces undefined behavior according to -// the ISO C++98 specification, section 3.10 ("basic.lval"), paragraph 15. -// (This did not substantially change in C++11.) Roughly, this section says: if -// an object in memory has one type, and a program accesses it with a different -// type, then the result is undefined behavior for most values of "different -// type". -// -// This is true for any cast syntax, either *(int*)&f or -// *reinterpret_cast(&f). And it is particularly true for conversions -// between integral lvalues and floating-point lvalues. -// -// The purpose of this paragraph is to allow optimizing compilers to assume that -// expressions with different types refer to different memory. Compilers are -// known to take advantage of this. So a non-conforming program quietly -// produces wildly incorrect output. -// -// The problem is not the use of reinterpret_cast. The problem is type punning: -// holding an object in memory of one type and reading its bits back using a -// different type. -// -// The C++ standard is more subtle and complex than this, but that is the basic -// idea. -// -// Anyways ... -// -// bit_cast<> calls memcpy() which is blessed by the standard, especially by the -// example in section 3.9 . Also, of course, bit_cast<> wraps up the nasty -// logic in one place. -// -// Fortunately memcpy() is very fast. In optimized mode, compilers replace -// calls to memcpy() with inline object code when the size argument is a -// compile-time constant. On a 32-bit system, memcpy(d,s,4) compiles to one -// load and one store, and memcpy(d,s,8) compiles to two loads and two stores. +// This is not a magic "get out of UB free" card. This must only be used on +// values, not on references or pointers. For pointers, use +// reinterpret_cast<>(), and then look at https://eel.is/c++draft/basic.lval#11 +// as that's probably UB also. template -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), - "bit_cast requires source and destination to be the same size"); - -#if (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1) || \ - defined(_LIBCPP_VERSION)) - // GCC 5.1 contains the first libstdc++ with is_trivially_copyable. - // Assume libc++ Just Works: is_trivially_copyable added on May 13th 2011. - static_assert(std::is_trivially_copyable::value, - "non-trivially-copyable bit_cast is undefined"); - static_assert(std::is_trivially_copyable::value, - "non-trivially-copyable bit_cast is undefined"); -#elif HAS_FEATURE(is_trivially_copyable) - // The compiler supports an equivalent intrinsic. - static_assert(__is_trivially_copyable(Dest), - "non-trivially-copyable bit_cast is undefined"); - static_assert(__is_trivially_copyable(Source), - "non-trivially-copyable bit_cast is undefined"); -#elif COMPILER_GCC - // Fallback to compiler intrinsic on GCC and clang (which pretends to be - // GCC). This isn't quite the same as is_trivially_copyable but it'll do for - // our purpose. - static_assert(__has_trivial_copy(Dest), - "non-trivially-copyable bit_cast is undefined"); - static_assert(__has_trivial_copy(Source), - "non-trivially-copyable bit_cast is undefined"); -#else - // Do nothing, let the bots handle it. -#endif - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; +constexpr Dest bit_cast(const Source& source) { + static_assert(!std::is_pointer_v, + "bit_cast must not be used on pointer types"); + static_assert(!std::is_pointer_v, + "bit_cast must not be used on pointer types"); + static_assert(!std::is_reference_v, + "bit_cast must not be used on reference types"); + static_assert(!std::is_reference_v, + "bit_cast must not be used on reference types"); + static_assert( + sizeof(Dest) == sizeof(Source), + "bit_cast requires source and destination types to be the same size"); + static_assert(std::is_trivially_copyable_v, + "bit_cast requires the source type to be trivially copyable"); + static_assert( + std::is_trivially_copyable_v, + "bit_cast requires the destination type to be trivially copyable"); + + return __builtin_bit_cast(Dest, source); } +} // namespace base + #endif // MINI_CHROMIUM_BASE_BIT_CAST_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h index dd25ad1fa..f419c4269 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h @@ -7,40 +7,229 @@ #include "build/build_config.h" +// This is a wrapper around `__has_cpp_attribute`, which can be used to test for +// the presence of an attribute. In case the compiler does not support this +// macro it will simply evaluate to 0. +// +// References: +// https://wg21.link/sd6#testing-for-the-presence-of-an-attribute-__has_cpp_attribute +// https://wg21.link/cpp.cond#:__has_cpp_attribute +#if defined(__has_cpp_attribute) +#define HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define HAS_CPP_ATTRIBUTE(x) 0 +#endif + +// A wrapper around `__has_attribute`, similar to HAS_CPP_ATTRIBUTE. +#if defined(__has_attribute) +#define HAS_ATTRIBUTE(x) __has_attribute(x) +#else +#define HAS_ATTRIBUTE(x) 0 +#endif + +// A wrapper around `__has_builtin`, similar to HAS_CPP_ATTRIBUTE. +#if defined(__has_builtin) +#define HAS_BUILTIN(x) __has_builtin(x) +#else +#define HAS_BUILTIN(x) 0 +#endif + +// Annotate a function indicating it should not be inlined. +// Use like: +// NOINLINE void DoStuff() { ... } +#if defined(__clang__) && HAS_ATTRIBUTE(noinline) +#define NOINLINE [[clang::noinline]] +#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(noinline) +#define NOINLINE __attribute__((noinline)) +#elif defined(COMPILER_MSVC) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif + +// Annotate a function indicating it should not be optimized. +#if defined(__clang__) && HAS_ATTRIBUTE(optnone) +#define NOOPT [[clang::optnone]] +#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(optimize) +#define NOOPT __attribute__((optimize(0))) +#else +#define NOOPT +#endif + +#if defined(__clang__) && defined(NDEBUG) && HAS_ATTRIBUTE(always_inline) +#define ALWAYS_INLINE [[clang::always_inline]] inline +#elif defined(COMPILER_GCC) && defined(NDEBUG) && HAS_ATTRIBUTE(always_inline) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif defined(COMPILER_MSVC) && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif + +// Annotate a function indicating it should never be tail called. Useful to make +// sure callers of the annotated function are never omitted from call-stacks. +// To provide the complementary behavior (prevent the annotated function from +// being omitted) look at NOINLINE. Also note that this doesn't prevent code +// folding of multiple identical caller functions into a single signature. To +// prevent code folding, see NO_CODE_FOLDING() in base/debug/alias.h. +// Use like: +// NOT_TAIL_CALLED void FooBar(); +#if defined(__clang__) && HAS_ATTRIBUTE(not_tail_called) +#define NOT_TAIL_CALLED [[clang::not_tail_called]] +#else +#define NOT_TAIL_CALLED +#endif + // Specify memory alignment for structs, classes, etc. // Use like: // class ALIGNAS(16) MyClass { ... } // ALIGNAS(16) int array[4]; -#if defined(COMPILER_MSVC) +// +// In most places you can use the C++11 keyword "alignas", which is preferred. +// +// Historically, compilers had trouble mixing __attribute__((...)) syntax with +// alignas(...) syntax. However, at least Clang is very accepting nowadays. It +// may be that this macro can be removed entirely. +#if defined(__clang__) +#define ALIGNAS(byte_alignment) alignas(byte_alignment) +#elif defined(COMPILER_MSVC) #define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) -#elif defined(COMPILER_GCC) +#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(aligned) #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) #endif -#if defined(COMPILER_MSVC) -#define PRINTF_FORMAT(format_param, dots_param) +// In case the compiler supports it NO_UNIQUE_ADDRESS evaluates to the C++20 +// attribute [[no_unique_address]]. This allows annotating data members so that +// they need not have an address distinct from all other non-static data members +// of its class. +// +// References: +// * https://en.cppreference.com/w/cpp/language/attributes/no_unique_address +// * https://wg21.link/dcl.attr.nouniqueaddr +#if defined(COMPILER_MSVC) && HAS_CPP_ATTRIBUTE(msvc::no_unique_address) +// Unfortunately MSVC ignores [[no_unique_address]] (see +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi), +// and clang-cl matches it for ABI compatibility reasons. We need to prefer +// [[msvc::no_unique_address]] when available if we actually want any effect. +#define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif HAS_CPP_ATTRIBUTE(no_unique_address) +#define NO_UNIQUE_ADDRESS [[no_unique_address]] #else +#define NO_UNIQUE_ADDRESS +#endif + +// Tells the compiler a function is using a printf-style format string. +// |format_param| is the one-based index of the format string parameter; +// |dots_param| is the one-based index of the "..." parameter. +// For v*printf functions (which take a va_list), pass 0 for dots_param. +// (This is undocumented but matches what the system C headers do.) +// For member functions, the implicit this parameter counts as index 1. +#if (defined(COMPILER_GCC) || defined(__clang__)) && HAS_ATTRIBUTE(format) #define PRINTF_FORMAT(format_param, dots_param) \ - __attribute__((format(printf, format_param, dots_param))) + __attribute__((format(printf, format_param, dots_param))) +#else +#define PRINTF_FORMAT(format_param, dots_param) #endif +// WPRINTF_FORMAT is the same, but for wide format strings. +// This doesn't appear to yet be implemented in any compiler. +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 . +#define WPRINTF_FORMAT(format_param, dots_param) +// If available, it would look like: +// __attribute__((format(wprintf, format_param, dots_param))) + // Sanitizers annotations. -#if defined(__has_attribute) -#if __has_attribute(no_sanitize) +#if HAS_ATTRIBUTE(no_sanitize) #define NO_SANITIZE(what) __attribute__((no_sanitize(what))) #endif -#endif #if !defined(NO_SANITIZE) #define NO_SANITIZE(what) #endif +// MemorySanitizer annotations. +#if defined(MEMORY_SANITIZER) +#include + +// Mark a memory region fully initialized. +// Use this to annotate code that deliberately reads uninitialized data, for +// example a GC scavenging root set pointers from the stack. +#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) + +// Check a memory region for initializedness, as if it was being used here. +// If any bits are uninitialized, crash with an MSan report. +// Use this to sanitize data which MSan won't be able to track, e.g. before +// passing data to another process via shared memory. +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ + __msan_check_mem_is_initialized(p, size) +#else // MEMORY_SANITIZER +#define MSAN_UNPOISON(p, size) +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) +#endif // MEMORY_SANITIZER + +// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons. +#if !defined(DISABLE_CFI_PERF) +#if defined(__clang__) && defined(OFFICIAL_BUILD) +#define DISABLE_CFI_PERF NO_SANITIZE("cfi") +#else +#define DISABLE_CFI_PERF +#endif +#endif + // DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks. +// Security Note: if you just need to allow calling of dlsym functions use +// DISABLE_CFI_DLSYM. +#if !defined(DISABLE_CFI_ICALL) #if BUILDFLAG(IS_WIN) // Windows also needs __declspec(guard(nocf)). #define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") __declspec(guard(nocf)) #else #define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") #endif +#endif +#if !defined(DISABLE_CFI_ICALL) +#define DISABLE_CFI_ICALL +#endif + +// DISABLE_CFI_DLSYM -- applies DISABLE_CFI_ICALL on platforms where dlsym +// functions must be called. Retains CFI checks on platforms where loaded +// modules participate in CFI (e.g. Windows). +#if !defined(DISABLE_CFI_DLSYM) +#if BUILDFLAG(IS_WIN) +// Windows modules register functions when loaded so can be checked by CFG. +#define DISABLE_CFI_DLSYM +#else +#define DISABLE_CFI_DLSYM DISABLE_CFI_ICALL +#endif +#endif +#if !defined(DISABLE_CFI_DLSYM) +#define DISABLE_CFI_DLSYM +#endif + +// Macro useful for writing cross-platform function pointers. +#if !defined(CDECL) +#if BUILDFLAG(IS_WIN) +#define CDECL __cdecl +#else // BUILDFLAG(IS_WIN) +#define CDECL +#endif // BUILDFLAG(IS_WIN) +#endif // !defined(CDECL) + +// Macro for hinting that an expression is likely to be false. +#if !defined(UNLIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define UNLIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(UNLIKELY) + +#if !defined(LIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define LIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(LIKELY) // Compiler feature-detection. // clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension @@ -50,4 +239,366 @@ #define HAS_FEATURE(FEATURE) 0 #endif +#if defined(COMPILER_GCC) +#define PRETTY_FUNCTION __PRETTY_FUNCTION__ +#elif defined(COMPILER_MSVC) +#define PRETTY_FUNCTION __FUNCSIG__ +#else +// See https://en.cppreference.com/w/c/language/function_definition#func +#define PRETTY_FUNCTION __func__ +#endif + +#if !defined(CPU_ARM_NEON) +#if defined(__arm__) +#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \ + !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID) +#error Chromium does not support middle endian architecture +#endif +#if defined(__ARM_NEON__) +#define CPU_ARM_NEON 1 +#endif +#endif // defined(__arm__) +#endif // !defined(CPU_ARM_NEON) + +#if !defined(HAVE_MIPS_MSA_INTRINSICS) +#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) +#define HAVE_MIPS_MSA_INTRINSICS 1 +#endif +#endif + +#if defined(__clang__) && HAS_ATTRIBUTE(uninitialized) +// Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for +// the specified variable. +// Library-wide alternative is +// 'configs -= [ "//build/config/compiler:default_init_stack_vars" ]' in .gn +// file. +// +// See "init_stack_vars" in build/config/compiler/BUILD.gn and +// http://crbug.com/977230 +// "init_stack_vars" is enabled for non-official builds and we hope to enable it +// in official build in 2020 as well. The flag writes fixed pattern into +// uninitialized parts of all local variables. In rare cases such initialization +// is undesirable and attribute can be used: +// 1. Degraded performance +// In most cases compiler is able to remove additional stores. E.g. if memory is +// never accessed or properly initialized later. Preserved stores mostly will +// not affect program performance. However if compiler failed on some +// performance critical code we can get a visible regression in a benchmark. +// 2. memset, memcpy calls +// Compiler may replaces some memory writes with memset or memcpy calls. This is +// not -ftrivial-auto-var-init specific, but it can happen more likely with the +// flag. It can be a problem if code is not linked with C run-time library. +// +// Note: The flag is security risk mitigation feature. So in future the +// attribute uses should be avoided when possible. However to enable this +// mitigation on the most of the code we need to be less strict now and minimize +// number of exceptions later. So if in doubt feel free to use attribute, but +// please document the problem for someone who is going to cleanup it later. +// E.g. platform, bot, benchmark or test name in patch description or next to +// the attribute. +#define STACK_UNINITIALIZED [[clang::uninitialized]] +#else +#define STACK_UNINITIALIZED +#endif + +// Attribute "no_stack_protector" disables -fstack-protector for the specified +// function. +// +// "stack_protector" is enabled on most POSIX builds. The flag adds a canary +// to each stack frame, which on function return is checked against a reference +// canary. If the canaries do not match, it's likely that a stack buffer +// overflow has occurred, so immediately crashing will prevent exploitation in +// many cases. +// +// In some cases it's desirable to remove this, e.g. on hot functions, or if +// we have purposely changed the reference canary. +#if defined(COMPILER_GCC) || defined(__clang__) +#if HAS_ATTRIBUTE(__no_stack_protector__) +#define NO_STACK_PROTECTOR __attribute__((__no_stack_protector__)) +#else +#define NO_STACK_PROTECTOR __attribute__((__optimize__("-fno-stack-protector"))) +#endif +#else +#define NO_STACK_PROTECTOR +#endif + +// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints +// to Clang which control what code paths are statically analyzed, +// and is meant to be used in conjunction with assert & assert-like functions. +// The expression is passed straight through if analysis isn't enabled. +// +// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current +// codepath and any other branching codepaths that might follow. +#if defined(__clang_analyzer__) + +inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { + return false; +} + +inline constexpr bool AnalyzerAssumeTrue(bool arg) { + // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is + // false. + return arg || AnalyzerNoReturn(); +} + +#define ANALYZER_ASSUME_TRUE(arg) ::AnalyzerAssumeTrue(!!(arg)) +#define ANALYZER_SKIP_THIS_PATH() static_cast(::AnalyzerNoReturn()) + +#else // !defined(__clang_analyzer__) + +#define ANALYZER_ASSUME_TRUE(arg) (arg) +#define ANALYZER_SKIP_THIS_PATH() + +#endif // defined(__clang_analyzer__) + +// Use nomerge attribute to disable optimization of merging multiple same calls. +#if defined(__clang__) && HAS_ATTRIBUTE(nomerge) +#define NOMERGE [[clang::nomerge]] +#else +#define NOMERGE +#endif + +// Marks a type as being eligible for the "trivial" ABI despite having a +// non-trivial destructor or copy/move constructor. Such types can be relocated +// after construction by simply copying their memory, which makes them eligible +// to be passed in registers. The canonical example is std::unique_ptr. +// +// Use with caution; this has some subtle effects on constructor/destructor +// ordering and will be very incorrect if the type relies on its address +// remaining constant. When used as a function argument (by value), the value +// may be constructed in the caller's stack frame, passed in a register, and +// then used and destructed in the callee's stack frame. A similar thing can +// occur when values are returned. +// +// TRIVIAL_ABI is not needed for types which have a trivial destructor and +// copy/move constructors, such as base::TimeTicks and other POD. +// +// It is also not likely to be effective on types too large to be passed in one +// or two registers on typical target ABIs. +// +// See also: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html +#if defined(__clang__) && HAS_ATTRIBUTE(trivial_abi) +#define TRIVIAL_ABI [[clang::trivial_abi]] +#else +#define TRIVIAL_ABI +#endif + +// Detect whether a type is trivially relocatable, ie. a move-and-destroy +// sequence can replaced with memmove(). This can be used to optimise the +// implementation of containers. This is automatically true for types that were +// defined with TRIVIAL_ABI such as scoped_refptr. +// +// See also: +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1144r8.html +// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable +#if defined(__clang__) && HAS_BUILTIN(__is_trivially_relocatable) +#define IS_TRIVIALLY_RELOCATABLE(t) __is_trivially_relocatable(t) +#else +#define IS_TRIVIALLY_RELOCATABLE(t) false +#endif + +// Marks a member function as reinitializing a moved-from variable. +// See also +// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/use-after-move.html#reinitialization +#if defined(__clang__) && HAS_ATTRIBUTE(reinitializes) +#define REINITIALIZES_AFTER_MOVE [[clang::reinitializes]] +#else +#define REINITIALIZES_AFTER_MOVE +#endif + +#if defined(__clang__) +#define GSL_OWNER [[gsl::Owner]] +#define GSL_POINTER [[gsl::Pointer]] +#else +#define GSL_OWNER +#define GSL_POINTER +#endif + +// Adds the "logically_const" tag to a symbol's mangled name. The "Mutable +// Constants" check [1] detects instances of constants that aren't in .rodata, +// e.g. due to a missing `const`. Using this tag suppresses the check for this +// symbol, allowing it to live outside .rodata without a warning. +// +// [1]: +// https://crsrc.org/c/docs/speed/binary_size/android_binary_size_trybot.md#Mutable-Constants +#if defined(COMPILER_GCC) || defined(__clang__) +#define LOGICALLY_CONST [[gnu::abi_tag("logically_const")]] +#else +#define LOGICALLY_CONST +#endif + +// preserve_most clang's calling convention. Reduces register pressure for the +// caller and as such can be used for cold calls. Support for the +// "preserve_most" attribute is limited: +// - 32-bit platforms do not implement it, +// - component builds fail because _dl_runtime_resolve() clobbers registers, +// - there are crashes on arm64 on Windows (https://crbug.com/v8/14065), which +// can hopefully be fixed in the future. +// Additionally, the initial implementation in clang <= 16 overwrote the return +// register(s) in the epilogue of a preserve_most function, so we only use +// preserve_most in clang >= 17 (see https://reviews.llvm.org/D143425). +// Clang only supports preserve_most on X86-64 and AArch64 for now. +// See https://clang.llvm.org/docs/AttributeReference.html#preserve-most for +// more details. +#if (defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_X86_64)) && \ + !(BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)) && \ + !defined(COMPONENT_BUILD) && defined(__clang__) && \ + __clang_major__ >= 17 && HAS_ATTRIBUTE(preserve_most) +#define PRESERVE_MOST __attribute__((preserve_most)) +#else +#define PRESERVE_MOST +#endif + +// Mark parameters or return types as having a lifetime attached to the class. +// +// When used to mark a method's pointer/reference parameter, the compiler is +// made aware that it will be stored internally in the class and the pointee +// must outlive the class. Typically used on constructor arguments. It should +// appear to the right of the parameter's variable name. +// +// Example: +// ``` +// struct S { +// S(int* p LIFETIME_BOUND) : ptr_(p) {} +// +// int* ptr_; +// }; +// ``` +// +// When used on a method with a return value, the compiler is made aware that +// the returned type is/has a pointer to the internals of the class, and must +// not outlive the class object. It should appear after any method qualifiers. +// +// Example: +// ``` +// struct S { +// int* GetPtr() const LIFETIME_BOUND { return i_; }; +// +// int i_; +// }; +// ``` +// +// This allows the compiler to warn in (a limited set of) cases where the +// pointer would otherwise be left dangling, especially in cases where the +// pointee would be a destroyed temporary. +// +// Docs: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if defined(__clang__) +#define LIFETIME_BOUND [[clang::lifetimebound]] +#else +#define LIFETIME_BOUND +#endif + +// Mark a function as pure, meaning that it does not have side effects, meaning +// that it does not write anything external to the function's local variables +// and return value. +// +// WARNING: If this attribute is mis-used it will result in UB and +// miscompilation, as the optimizator may fold multiple calls into one and +// reorder them inappropriately. This shouldn't appear outside of key vocabulary +// types. It allows callers to work with the vocab type directly, and call its +// methods without having to worry about caching things into local variables in +// hot code. +// +// This attribute must not appear on functions that make use of function +// pointers, virtual methods, or methods of templates (including operators like +// comparison), as the "pure" function can not know what those functions do and +// can not guarantee there will never be sideeffects. +#if defined(COMPILER_GCC) || defined(__clang__) +#define PURE_FUNCTION [[gnu::pure]] +#else +#define PURE_FUNCTION +#endif + +// Functions should be marked with UNSAFE_BUFFER_USAGE when they lead to +// out-of-bounds bugs when called with incorrect inputs. +// +// Ideally such functions should be paired with a safer version that works with +// safe primitives like `base::span`. Otherwise, another safer coding pattern +// should be documented along side the use of `UNSAFE_BUFFER_USAGE`. +// +// All functions marked with UNSAFE_BUFFER_USAGE should come with a safety +// comment that explains the requirements of the function to prevent any chance +// of an out-of-bounds bug. For example: +// ``` +// // Function to do things between `input` and `end`. +// // +// // # Safety +// // The `input` must point to an array with size at least 5. The `end` must +// // point within the same allocation of `input` and not come before `input`. +// ``` +#if defined(__clang__) && HAS_ATTRIBUTE(unsafe_buffer_usage) +#define UNSAFE_BUFFER_USAGE [[clang::unsafe_buffer_usage]] +#else +#define UNSAFE_BUFFER_USAGE +#endif + +// UNSAFE_BUFFERS() wraps code that violates the -Wunsafe-buffer-usage warning, +// such as: +// - pointer arithmetic, +// - pointer subscripting, and +// - calls to functions annotated with UNSAFE_BUFFER_USAGE. +// +// ** USE OF THIS MACRO SHOULD BE VERY RARE.** Reviewers should push back when +// it is not strictly necessary. Prefer to use `base::span` instead of pointers, +// or other safer coding patterns (like std containers) that avoid the +// opportunity for out-of-bounds bugs to creep into the code. Any use of +// UNSAFE_BUFFERS() can lead to a critical security bug if any assumptions are +// wrong, or ever become wrong in the future. +// +// The macro should be used to wrap the minimum necessary code, to make it clear +// what is unsafe, and prevent accidentally opting extra things out of the +// warning. +// +// All usage of UNSAFE_BUFFERS() should come with a `// SAFETY: ...` comment +// that explains how we have guaranteed (ideally directly above, with conditions +// or CHECKs) that the pointer usage can never go out-of-bounds, or that the +// requirements of the UNSAFE_BUFFER_USAGE function are met. If the safety +// explanation requires cooperation of code that is not fully encapsulated close +// to the UNSAFE_BUFFERS() usage, it should be rejected and replaced with safer +// coding patterns or stronger guarantees. +#if defined(__clang__) && HAS_ATTRIBUTE(unsafe_buffer_usage) +// clang-format off +// Formatting is off so that we can put each _Pragma on its own line, as +// recommended by the gcc docs. +#define UNSAFE_BUFFERS(...) \ + _Pragma("clang unsafe_buffer_usage begin") \ + __VA_ARGS__ \ + _Pragma("clang unsafe_buffer_usage end") +// clang-format on +#else +#define UNSAFE_BUFFERS(...) __VA_ARGS__ +#endif + +// Defines a condition for a function to be checked at compile time if the +// parameter's value is known at compile time. If the condition is failed, the +// function is omitted from the overload set resolution, much like `requires`. +// +// If the parameter is a runtime value, then the condition is unable to be +// checked and the function will be omitted from the overload set resolution. +// This ensures the function can only be called with values known at compile +// time. This is a clang extension. +// +// Example: +// ``` +// void f(int a) ENABLE_IF_ATTR(a > 0) {} +// f(1); // Ok. +// f(0); // Error: no valid f() found. +// ``` +// +// The `ENABLE_IF_ATTR` annotation is preferred over `consteval` with a check +// that breaks compile because metaprogramming does not observe such checks. So +// with `consteval`, the function looks callable to concepts/type_traits but is +// not and will fail to compile even though it reports it's usable. Whereas +// `ENABLE_IF_ATTR` interacts correctly with metaprogramming. This is especially +// painful for constructors. See also +// https://github.com/chromium/subspace/issues/266. +#if defined(__clang__) +#define ENABLE_IF_ATTR(cond, msg) __attribute__((enable_if(cond, msg))) +#else +#define ENABLE_IF_ATTR(cond, msg) +#endif + #endif // MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/checked_iterators.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/checked_iterators.h new file mode 100644 index 000000000..a735515d5 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/checked_iterators.h @@ -0,0 +1,233 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CONTAINERS_CHECKED_ITERATORS_H_ +#define MINI_CHROMIUM_BASE_CONTAINERS_CHECKED_ITERATORS_H_ + +#include +#include +#include +#include + +#include "base/check_op.h" +#include "base/compiler_specific.h" +#include "base/containers/util.h" +#include "base/memory/raw_ptr_exclusion.h" +#include "build/build_config.h" + +namespace base { + +template +class CheckedContiguousIterator { + public: + using difference_type = std::ptrdiff_t; + using value_type = std::remove_cv_t; + using pointer = T*; + using reference = T&; + using iterator_category = std::contiguous_iterator_tag; + using iterator_concept = std::contiguous_iterator_tag; + + // Required for converting constructor below. + template + friend class CheckedContiguousIterator; + + // Required to be able to get to the underlying pointer without triggering + // CHECK failures. + template + friend struct std::pointer_traits; + + constexpr CheckedContiguousIterator() = default; + + UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(T* start, + const T* end) + : CheckedContiguousIterator(start, start, end) {} + + UNSAFE_BUFFER_USAGE constexpr CheckedContiguousIterator(const T* start, + T* current, + const T* end) + : start_(start), current_(current), end_(end) { + CHECK_LE(start, current); + CHECK_LE(current, end); + } + + constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) = + default; + + // Converting constructor allowing conversions like CCI to CCI, + // but disallowing CCI to CCI or CCI to CCI, which + // are unsafe. Furthermore, this is the same condition as used by the + // converting constructors of std::span and std::unique_ptr. + // See https://wg21.link/n4042 for details. + template + constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) + requires(std::convertible_to) + : start_(other.start_), current_(other.current_), end_(other.end_) { + // We explicitly don't delegate to the 3-argument constructor here. Its + // CHECKs would be redundant, since we expect |other| to maintain its own + // invariant. However, DCHECKs never hurt anybody. Presumably. + DCHECK_LE(other.start_, other.current_); + DCHECK_LE(other.current_, other.end_); + } + + ~CheckedContiguousIterator() = default; + + constexpr CheckedContiguousIterator& operator=( + const CheckedContiguousIterator& other) = default; + + friend constexpr bool operator==(const CheckedContiguousIterator& lhs, + const CheckedContiguousIterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.current_ == rhs.current_; + } + + friend constexpr auto operator<=>(const CheckedContiguousIterator& lhs, + const CheckedContiguousIterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.current_ <=> rhs.current_; + } + + constexpr CheckedContiguousIterator& operator++() { + CHECK_NE(current_, end_); + ++current_; + return *this; + } + + constexpr CheckedContiguousIterator operator++(int) { + CheckedContiguousIterator old = *this; + ++*this; + return old; + } + + constexpr CheckedContiguousIterator& operator--() { + CHECK_NE(current_, start_); + --current_; + return *this; + } + + constexpr CheckedContiguousIterator operator--(int) { + CheckedContiguousIterator old = *this; + --*this; + return old; + } + + constexpr CheckedContiguousIterator& operator+=(difference_type rhs) { + if (rhs > 0) { + CHECK_LE(rhs, end_ - current_); + } else { + CHECK_LE(-rhs, current_ - start_); + } + current_ += rhs; + return *this; + } + + constexpr CheckedContiguousIterator operator+(difference_type rhs) const { + CheckedContiguousIterator it = *this; + it += rhs; + return it; + } + + constexpr friend CheckedContiguousIterator operator+( + difference_type lhs, + const CheckedContiguousIterator& rhs) { + return rhs + lhs; + } + + constexpr CheckedContiguousIterator& operator-=(difference_type rhs) { + if (rhs < 0) { + CHECK_LE(-rhs, end_ - current_); + } else { + CHECK_LE(rhs, current_ - start_); + } + current_ -= rhs; + return *this; + } + + constexpr CheckedContiguousIterator operator-(difference_type rhs) const { + CheckedContiguousIterator it = *this; + it -= rhs; + return it; + } + + constexpr friend difference_type operator-( + const CheckedContiguousIterator& lhs, + const CheckedContiguousIterator& rhs) { + lhs.CheckComparable(rhs); + return lhs.current_ - rhs.current_; + } + + constexpr reference operator*() const { + CHECK_NE(current_, end_); + return *current_; + } + + constexpr pointer operator->() const { + CHECK_NE(current_, end_); + return current_; + } + + constexpr reference operator[](difference_type rhs) const { + CHECK_GE(rhs, 0); + CHECK_LT(rhs, end_ - current_); + return current_[rhs]; + } + + [[nodiscard]] static bool IsRangeMoveSafe( + const CheckedContiguousIterator& from_begin, + const CheckedContiguousIterator& from_end, + const CheckedContiguousIterator& to) { + if (from_end < from_begin) + return false; + const auto from_begin_uintptr = get_uintptr(from_begin.current_); + const auto from_end_uintptr = get_uintptr(from_end.current_); + const auto to_begin_uintptr = get_uintptr(to.current_); + const auto to_end_uintptr = + get_uintptr((to + std::distance(from_begin, from_end)).current_); + + return to_begin_uintptr >= from_end_uintptr || + to_end_uintptr <= from_begin_uintptr; + } + + private: + constexpr void CheckComparable(const CheckedContiguousIterator& other) const { + CHECK_EQ(start_, other.start_); + CHECK_EQ(end_, other.end_); + } + + // RAW_PTR_EXCLUSION: T can be a STACK_ALLOCATED class. + RAW_PTR_EXCLUSION const T* start_ = nullptr; + RAW_PTR_EXCLUSION T* current_ = nullptr; + RAW_PTR_EXCLUSION const T* end_ = nullptr; +}; + +template +using CheckedContiguousConstIterator = CheckedContiguousIterator; + +} // namespace base + +// Specialize std::pointer_traits so that we can obtain the underlying raw +// pointer without resulting in CHECK failures. The important bit is the +// `to_address(pointer)` overload, which is the standard blessed way to +// customize `std::to_address(pointer)` in C++20 [1]. +// +// [1] https://wg21.link/pointer.traits.optmem + +template +struct std::pointer_traits<::base::CheckedContiguousIterator> { + using pointer = ::base::CheckedContiguousIterator; + using element_type = T; + using difference_type = ptrdiff_t; + + template + using rebind = ::base::CheckedContiguousIterator; + + static constexpr pointer pointer_to(element_type& r) noexcept { + return pointer(&r, &r); + } + + static constexpr element_type* to_address(pointer p) noexcept { + return p.current_; + } +}; + +#endif // MINI_CHROMIUM_BASE_CONTAINERS_CHECKED_ITERATORS_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/dynamic_extent.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/dynamic_extent.h new file mode 100644 index 000000000..53e7f5ad6 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/dynamic_extent.h @@ -0,0 +1,18 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CONTAINERS_DYNAMIC_EXTENT_H_ +#define MINI_CHROMIUM_BASE_CONTAINERS_DYNAMIC_EXTENT_H_ + +#include +#include + +namespace base { + +// [views.constants] +inline constexpr size_t dynamic_extent = std::numeric_limits::max(); + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_CONTAINERS_DYNAMIC_EXTENT_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/heap_array.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/heap_array.h new file mode 100644 index 000000000..7a225e464 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/heap_array.h @@ -0,0 +1,222 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CONTAINERS_HEAP_ARRAY_H_ +#define MINI_CHROMIUM_BASE_CONTAINERS_HEAP_ARRAY_H_ + +#include + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/containers/span.h" + +// No absl in mini_chromium +#if !defined(ABSL_ATTRIBUTE_LIFETIME_BOUND) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif + +namespace base { + +// HeapArray is a replacement for std::unique_ptr that keeps track +// of its size. It is intended to provide easy conversion to span for most +// usage, but it also provides bounds-checked indexing. +// +// By default, elements in the array are either value-initialized (i.e. zeroed +// for primitive types) when the array is created using the WithSize() +// static method, or uninitialized when the array is created via the Uninit() +// static method. +template +class TRIVIAL_ABI GSL_OWNER HeapArray { + public: + static_assert(!std::is_const_v, "HeapArray cannot hold const types"); + static_assert(!std::is_reference_v, + "HeapArray cannot hold reference types"); + + using iterator = typename base::span::iterator; + using const_iterator = typename base::span::iterator; + // We don't put this default value in the template parameter list to allow the + // static_assert on is_reference_v to give a nicer error message. + using deleter_type = std:: + conditional_t, std::default_delete, Deleter>; + + // Allocates initialized memory capable of holding `size` elements. No memory + // is allocated for zero-sized arrays. + static HeapArray WithSize(size_t size) + requires(std::constructible_from) + { + if (!size) { + return HeapArray(); + } + return HeapArray(std::unique_ptr(new T[size]()), size); + } + + // Allocates uninitialized memory capable of holding `size` elements. T must + // be trivially constructible and destructible. No memory is allocated for + // zero-sized arrays. + static HeapArray Uninit(size_t size) + requires(std::is_trivially_constructible_v && + std::is_trivially_destructible_v) + { + if (!size) { + return HeapArray(); + } + return HeapArray(std::unique_ptr(new T[size]), size); + } + + static HeapArray CopiedFrom(base::span that) { + auto result = HeapArray::Uninit(that.size()); + result.copy_from(that); + return result; + } + + // Constructs a HeapArray from an existing pointer, taking ownership of the + // pointer. + // + // # Safety + // The pointer must be correctly aligned for type `T` and able to be deleted + // through the `deleter_type`, which defaults to the `delete[]` operation. The + // `ptr` must point to an array of at least `size` many elements. If these are + // not met, then Undefined Behaviour can result. + // + // # Checks + // When the `size` is zero, the `ptr` must be null. + UNSAFE_BUFFER_USAGE static HeapArray FromOwningPointer(T* ptr, size_t size) { + if (!size) { + CHECK_EQ(ptr, nullptr); + return HeapArray(); + } + return HeapArray(std::unique_ptr(ptr), size); + } + + // Constructs an empty array and does not allocate any memory. + HeapArray() + requires(std::constructible_from) + = default; + + // Move-only type since the memory is owned. + HeapArray(const HeapArray&) = delete; + HeapArray& operator=(const HeapArray&) = delete; + + // Move-construction leaves the moved-from object empty and containing + // no allocated memory. + HeapArray(HeapArray&& that) + : data_(std::move(that.data_)), size_(std::exchange(that.size_, 0u)) {} + + // Move-assigment leaves the moved-from object empty and containing + // no allocated memory. + HeapArray& operator=(HeapArray&& that) { + data_ = std::move(that.data_); + size_ = std::exchange(that.size_, 0u); + return *this; + } + ~HeapArray() = default; + + bool empty() const { return size_ == 0u; } + size_t size() const { return size_; } + + // Prefer span-based methods below over data() where possible. The data() + // method exists primarily to allow implicit constructions of spans. + // Returns nullptr for a zero-sized (or moved-from) array. + T* data() ABSL_ATTRIBUTE_LIFETIME_BOUND { return data_.get(); } + const T* data() const ABSL_ATTRIBUTE_LIFETIME_BOUND { return data_.get(); } + + iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return as_span().begin(); } + const_iterator begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().begin(); + } + + iterator end() ABSL_ATTRIBUTE_LIFETIME_BOUND { return as_span().end(); } + const_iterator end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().end(); + } + + T& operator[](size_t idx) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span()[idx]; + } + const T& operator[](size_t idx) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span()[idx]; + } + + // Access the HeapArray via spans. Note that span is implicilty + // constructible from HeapArray, so an explicit call to .as_span() is + // most useful, say, when the compiler can't deduce a template + // argument type. + base::span as_span() ABSL_ATTRIBUTE_LIFETIME_BOUND { + return base::span(data_.get(), size_); + } + base::span as_span() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return base::span(data_.get(), size_); + } + + // Convenience method to copy the contents of the entire array from a + // span<>. Hard CHECK occurs in span<>::copy_from() if the HeapArray and + // the span have different sizes. + void copy_from(base::span other) { as_span().copy_from(other); } + + // Convenience methods to slice the vector into spans. + // Returns a span over the HeapArray starting at `offset` of `count` elements. + // If `count` is unspecified, all remaining elements are included. A CHECK() + // occurs if any of the parameters results in an out-of-range position in + // the HeapArray. + base::span subspan(size_t offset, size_t count = base::dynamic_extent) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().subspan(offset, count); + } + base::span subspan(size_t offset, + size_t count = base::dynamic_extent) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().subspan(offset, count); + } + + // Returns a span over the first `count` elements of the HeapArray. A CHECK() + // occurs if the `count` is larger than size of the HeapArray. + base::span first(size_t count) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().first(count); + } + base::span first(size_t count) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().first(count); + } + + // Returns a span over the last `count` elements of the HeapArray. A CHECK() + // occurs if the `count` is larger than size of the HeapArray. + base::span last(size_t count) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().last(count); + } + base::span last(size_t count) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return as_span().last(count); + } + + // Leaks the memory in the HeapArray so that it will never be freed, and + // consumes the HeapArray, returning an unowning span that points to the + // memory. + base::span leak() && { + HeapArray dropped = std::move(*this); + T* leaked = dropped.data_.release(); + return make_span(leaked, dropped.size_); + } + + // Delete the memory previously obtained from leak(). Argument is a pointer + // rather than a span to facilitate use by callers that have lost track of + // size information, as may happen when being passed through a C-style + // function callback. The void* argument type makes its signature compatible + // with typical void (*cb)(void*) C-style deletion callback. + static void DeleteLeakedData(void* ptr) { + // Memory is freed by unique ptr going out of scope. + std::unique_ptr deleter(static_cast(ptr)); + } + + private: + HeapArray(std::unique_ptr data, size_t size) + : data_(std::move(data)), size_(size) {} + + std::unique_ptr data_; + size_t size_ = 0u; +}; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_CONTAINERS_HEAP_ARRAY_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/span.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/span.h index 7b6074c2a..c7ed4c376 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/span.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/span.h @@ -2,27 +2,36 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_CONTAINERS_SPAN_H_ -#define BASE_CONTAINERS_SPAN_H_ +#ifndef MINI_CHROMIUM_BASE_CONTAINERS_SPAN_H_ +#define MINI_CHROMIUM_BASE_CONTAINERS_SPAN_H_ #include #include +#include #include +#include #include #include +#include +#include #include #include #include "base/check.h" #include "base/compiler_specific.h" +#include "base/containers/checked_iterators.h" +#include "base/containers/dynamic_extent.h" #include "base/numerics/safe_conversions.h" #include "base/template_util.h" +#include "base/types/to_address.h" -namespace base { +// No absl in mini_chromium. +#if !defined(ABSL_ATTRIBUTE_LIFETIME_BOUND) +#define ABSL_ATTRIBUTE_LIFETIME_BOUND +#endif -// [views.constants] -constexpr size_t dynamic_extent = std::numeric_limits::max(); +namespace base { template +concept LegalDataConversion = + std::convertible_to (*)[], + std::remove_reference_t (*)[]>; + +template +concept CompatibleIter = std::contiguous_iterator && + LegalDataConversion, T>; + +template +concept CompatibleRange = + std::ranges::contiguous_range && std::ranges::sized_range && + LegalDataConversion, T> && + (std::ranges::borrowed_range || std::is_const_v); + +// NOTE: Ideally we'd just use `CompatibleRange`, however this currently breaks +// code that was written prior to C++20 being standardized and assumes providing +// .data() and .size() is sufficient. +// TODO: https://crbug.com/1504998 - Remove in favor of CompatibleRange and fix +// callsites. +template +concept LegacyCompatibleRange = requires(R& r) { + { *std::ranges::data(r) } -> LegalDataConversion; + std::ranges::size(r); +}; + template using size_constant = std::integral_constant; @@ -47,91 +82,10 @@ template struct ExtentImpl> : size_constant {}; template -using Extent = ExtentImpl>; - -template -struct IsSpanImpl : std::false_type {}; - -template -struct IsSpanImpl> : std::true_type {}; - -template -using IsNotSpan = std::negation>>; - -template -struct IsStdArrayImpl : std::false_type {}; - -template -struct IsStdArrayImpl> : std::true_type {}; - -template -using IsNotStdArray = std::negation>>; +using Extent = ExtentImpl>; template -using IsNotCArray = std::negation>>; - -template -using IsLegalDataConversion = std::is_convertible; - -template -using ContainerHasConvertibleData = IsLegalDataConversion< - std::remove_pointer_t()))>, - T>; - -template -using ContainerHasIntegralSize = - std::is_integral()))>; - -template -using EnableIfLegalSpanConversion = - std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) && - IsLegalDataConversion::value>; - -// SFINAE check if Array can be converted to a span. -template -using EnableIfSpanCompatibleArray = - std::enable_if_t<(Extent == dynamic_extent || - Extent == internal::Extent::value) && - ContainerHasConvertibleData::value>; - -// SFINAE check if Container can be converted to a span. -template -using IsSpanCompatibleContainer = - std::conjunction, - IsNotStdArray, - IsNotCArray, - ContainerHasConvertibleData, - ContainerHasIntegralSize>; - -template -using EnableIfSpanCompatibleContainer = - std::enable_if_t::value>; - -template -using EnableIfSpanCompatibleContainerAndSpanIsDynamic = - std::enable_if_t::value && - Extent == dynamic_extent>; - -// A helper template for storing the size of a span. Spans with static extents -// don't require additional storage, since the extent itself is specified in the -// template parameter. -template -class ExtentStorage { - public: - constexpr explicit ExtentStorage(size_t size) noexcept {} - constexpr size_t size() const noexcept { return Extent; } -}; - -// Specialization of ExtentStorage for dynamic extents, which do require -// explicit storage for the size. -template <> -struct ExtentStorage { - constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {} - constexpr size_t size() const noexcept { return size_; } - - private: - size_t size_; -}; +inline constexpr size_t ExtentV = Extent::value; // must_not_be_dynamic_extent prevents |dynamic_extent| from being returned in a // constexpr context. @@ -143,6 +97,11 @@ constexpr size_t must_not_be_dynamic_extent() { return kExtent; } +template + requires((N == M || N == dynamic_extent || M == dynamic_extent) && + std::equality_comparable_with) +constexpr bool span_cmp(span l, span r); + } // namespace internal // A span is a value type that represents an array of elements of type T. Since @@ -152,8 +111,8 @@ constexpr size_t must_not_be_dynamic_extent() { // own the underlying memory, so care must be taken to ensure that a span does // not outlive the backing store. // -// span is somewhat analogous to StringPiece, but with arbitrary element types, -// allowing mutation if T is non-const. +// span is somewhat analogous to std::string_view, but with arbitrary element +// types, allowing mutation if T is non-const. // // span is implicitly convertible from C++ arrays, as well as most [1] // container-like types that provide a data() and size() method (such as @@ -190,6 +149,29 @@ constexpr size_t must_not_be_dynamic_extent() { // char str_buffer[100]; // SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14); // +// Dynamic vs Fixed size spans +// --------------------------- +// +// Normally spans have a dynamic size, which is represented as a type as +// `span`. However it is possible to encode the size of the span into the +// type as a second parameter such as `span`. When working with fixed-size +// spans, the compiler will check the size of operations and prevent compilation +// when an invalid size is used for an operation such as assignment or +// `copy_from()`. However operations that produce a new span will make a +// dynamic-sized span by default. See below for how to prevent that. +// +// Fixed-size spans implicitly convert to a dynamic-size span, throwing away the +// compile-time size information from the type signature. So most code should +// work with dynamic-sized `span` types and not worry about the existence of +// fixed-size spans. +// +// It is possible to convert from a dynamic-size to a fixed-size span (or to +// move from a fixed-size span to another fixed-size span) but it requires +// writing an the size explicitly in the code. Methods like `first` can be +// passed a size as a template argument, such as `first()` to generate a +// fixed-size span. And the `make_span` function can be given a compile-time +// size in a similar way with `make_span()`. +// // Spans with "const" and pointers // ------------------------------- // @@ -200,34 +182,66 @@ constexpr size_t must_not_be_dynamic_extent() { // std::vector => base::span // const std::vector => base::span // -// Differences from the C++20 draft -// -------------------------------- +// Differences from the C++ standard +// --------------------------------- // -// http://eel.is/c++draft/views contains the latest C++20 draft of std::span. +// http://eel.is/c++draft/views.span contains the latest C++ draft of std::span. // Chromium tries to follow the draft as close as possible. Differences between // the draft and the implementation are documented in subsections below. // +// Differences from [span.overview]: +// - Dynamic spans are implemented as a partial specialization of the regular +// class template. This leads to significantly simpler checks involving the +// extent, at the expense of some duplicated code. The same strategy is used +// by libc++. +// // Differences from [span.objectrep]: // - as_bytes() and as_writable_bytes() return spans of uint8_t instead of -// std::byte (std::byte is a C++17 feature) +// std::byte. // // Differences from [span.cons]: -// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic -// sized container (e.g. std::vector) requires an explicit conversion (in the -// C++20 draft this is simply UB) +// - The constructors from a contiguous range apart from a C array are folded +// into a single one, using a construct similarly to the one proposed +// (but not standardized) in https://wg21.link/P1419. +// The C array constructor is kept so that a span can be constructed from +// an init list like {{1, 2, 3}}. +// TODO: https://crbug.com/828324 - Consider adding C++26's constructor from +// a std::initializer_list instead. +// - The conversion constructors from a contiguous range into a dynamic span +// don't check for the range concept, but rather whether std::ranges::data +// and std::ranges::size are well formed. This is due to legacy reasons and +// should be fixed. +// +// Differences from [span.deduct]: +// - The deduction guides from a contiguous range are folded into a single one, +// and treat borrowed ranges correctly. +// - Add deduction guide from rvalue array. +// +// Other differences: +// - Using StrictNumeric instead of size_t where possible. +// +// Additions beyond the C++ standard draft +// - as_chars() function. +// - as_writable_chars() function. +// - as_byte_span() function. +// - as_writable_byte_span() function. +// - copy_from() method. +// - span_from_ref() function. +// - byte_span_from_ref() function. +// - span_from_cstring() function. +// - byte_span_from_cstring() function. +// - split_at() method. +// - operator==() comparator function. // // Furthermore, all constructors and methods are marked noexcept due to the lack // of exceptions in Chromium. // // Due to the lack of class template argument deduction guides in C++14 -// appropriate make_span() utility functions are provided. +// appropriate make_span() utility functions are provided for historic reasons. // [span], class template span -template -class span : public internal::ExtentStorage { - private: - using ExtentStorage = internal::ExtentStorage; - +template +class GSL_POINTER span { public: using element_type = T; using value_type = std::remove_cv_t; @@ -237,170 +251,698 @@ class span : public internal::ExtentStorage { using const_pointer = const T*; using reference = T&; using const_reference = const T&; - using iterator = T*; + using iterator = CheckedContiguousIterator; using reverse_iterator = std::reverse_iterator; - static constexpr size_t extent = Extent; + static constexpr size_t extent = N; // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : ExtentStorage(0), data_(nullptr) { - static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); + constexpr span() noexcept + requires(N == 0) + = default; + + // Constructs a span from a contiguous iterator and a size. + // + // # Checks + // The function CHECKs that `count` matches the template parameter `N` and + // will terminate otherwise. + // + // # Safety + // The iterator must point to the first of at least `count` many elements, or + // Undefined Behaviour can result as the span will allow access beyond the + // valid range of the collection pointed to by the iterator. + template + requires(internal::CompatibleIter) + UNSAFE_BUFFER_USAGE explicit constexpr span( + It first, + StrictNumeric count) noexcept + : // The use of to_address() here is to handle the case where the + // iterator `first` is pointing to the container's `end()`. In that + // case we can not use the address returned from the iterator, or + // dereference it through the iterator's `operator*`, but we can store + // it. We must assume in this case that `count` is 0, since the + // iterator does not point to valid data. Future hardening of iterators + // may disallow pulling the address from `end()`, as demonstrated by + // asserts() in libstdc++: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93960. + // + // The span API dictates that the `data()` is accessible when size is + // 0, since the pointer may be valid, so we cannot prevent storing and + // giving out an invalid pointer here without breaking API + // compatibility and our unit tests. Thus protecting against this can + // likely only be successful from inside iterators themselves, where + // the context about the pointer is known. + // + // We can not protect here generally against an invalid iterator/count + // being passed in, since we have no context to determine if the + // iterator or count are valid. + data_(base::to_address(first)) { + // Guarantees that the N in the type signature is correct. + CHECK(N == count); } - constexpr span(T* data, size_t size) noexcept - : ExtentStorage(size), data_(data) { - CHECK(Extent == dynamic_extent || Extent == size); + // Constructs a span from a contiguous iterator and a size. + // + // # Checks + // The function CHECKs that `it <= end` and will terminate otherwise. + // + // # Safety + // The begin and end iterators must be for the same allocation or Undefined + // Behaviour can result as the span will allow access beyond the valid range + // of the collection pointed to by `begin`. + template + requires(internal::CompatibleIter && + std::sized_sentinel_for && + !std::convertible_to) + UNSAFE_BUFFER_USAGE explicit constexpr span(It begin, End end) noexcept + // SAFETY: The caller must guarantee that the iterator and end sentinel + // are part of the same allocation, in which case it is the number of + // elements between the iterators and thus a valid size for the pointer to + // the element at `begin`. + // + // We CHECK that `end - begin` did not underflow below. Normally checking + // correctness afterward is flawed, however underflow is not UB and the + // size is not converted to an invalid pointer (which would be UB) before + // we CHECK for underflow. + : UNSAFE_BUFFERS(span(begin, static_cast(end - begin))) { + // Verify `end - begin` did not underflow. + CHECK(begin <= end); } - // Artificially templatized to break ambiguity for span(ptr, 0). - template - constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr span(T (&arr)[N]) noexcept + // SAFETY: The std::ranges::size() function gives the number of elements + // pointed to by the std::ranges::data() function, which meets the + // requirement of span. + : UNSAFE_BUFFERS(span(std::ranges::data(arr), std::ranges::size(arr))) {} + + template > + requires(internal::CompatibleRange && (X == N || X == dynamic_extent)) + // NOLINTNEXTLINE(google-explicit-constructor) + explicit(X == dynamic_extent) constexpr span(R&& range) noexcept + // SAFETY: The std::ranges::size() function gives the number of elements + // pointed to by the std::ranges::data() function, which meets the + // requirement of span. + : UNSAFE_BUFFERS( + span(std::ranges::data(range), std::ranges::size(range))) {} + + // [span.sub], span subviews + template + constexpr span first() const noexcept + requires(Count <= N) + { + // SAFETY: span provides that data() points to at least `N` many elements. + // `Count` is non-negative by its type and `Count <= N` from the requires + // condition. So `Count` is a valid new size for `data()`. + return UNSAFE_BUFFERS(span(data(), Count)); + } + + template + constexpr span last() const noexcept + requires(Count <= N) + { + // SAFETY: span provides that data() points to at least `N` many elements. + // `Count` is non-negative by its type and `Count <= N` from the requires + // condition. So `0 <= N - Count <= N`, meaning `N - Count` is a valid new + // size for `data()` and it will point to `Count` many elements.` + return UNSAFE_BUFFERS(span(data() + (N - Count), Count)); + } + + // Returns a span over the first `count` elements. + // + // # Checks + // The function CHECKs that the span contains at least `count` elements and + // will terminate otherwise. + constexpr span first(StrictNumeric count) const noexcept { + CHECK_LE(size_t{count}, size()); + // SAFETY: span provides that data() points to at least `N` many elements. + // `count` is non-negative by its type and `count <= N` from the CHECK + // above. So `count` is a valid new size for `data()`. + return UNSAFE_BUFFERS({data(), count}); + } + + // Returns a span over the last `count` elements. + // + // # Checks + // The function CHECKs that the span contains at least `count` elements and + // will terminate otherwise. + constexpr span last(StrictNumeric count) const noexcept { + CHECK_LE(size_t{count}, N); + // SAFETY: span provides that data() points to at least `N` many elements. + // `count` is non-negative by its type and `count <= N` from the CHECK + // above. So `0 <= N - count <= N`, meaning `N - count` is a valid new size + // for `data()` and it will point to `count` many elements. + return UNSAFE_BUFFERS({data() + (N - size_t{count}), count}); + } + + template + constexpr auto subspan() const noexcept + requires(Offset <= N && (Count == dynamic_extent || Count <= N - Offset)) + { + constexpr size_t kExtent = Count != dynamic_extent ? Count : N - Offset; + // SAFETY: span provides that data() points to at least `N` many elements. + // + // If Count is dynamic_extent, kExtent becomes `N - Offset`. Since `Offset + // <= N` from the requires condition, then `Offset` is a valid offset for + // data(), and `Offset + kExtent = Offset + N - Offset = N >= Offset` is + // also a valid offset that is not before `Offset`. This makes a span at + // `Offset` with size `kExtent` valid. + // + // Otherwise `Count <= N - Offset` and `0 <= Offset <= N` by the requires + // condition, so `Offset <= N - Count` and `N - Count` can not underflow. + // Then `Offset` is a valid offset for data() and `kExtent` is `Count <= N - + // Offset`, so `Offset + kExtent <= Offset + N - Offset = N` which makes + // both `Offset` and `Offset + kExtent` valid offsets for data(), and since + // `kExtent` is non-negative, `Offset + kExtent` is not before `Offset` so + // `kExtent` is a valid size for the span at `data() + Offset`. + return UNSAFE_BUFFERS(span(data() + Offset, kExtent)); + } + + // Returns a span over the first `count` elements starting at the given + // `offset` from the start of the span. + // + // # Checks + // The function CHECKs that the span contains at least `offset + count` + // elements, or at least `offset` elements if `count` is not specified, and + // will terminate otherwise. + constexpr span subspan(size_t offset, + size_t count = dynamic_extent) const noexcept { + CHECK_LE(offset, N); + CHECK(count == dynamic_extent || count <= N - offset); + const size_t new_extent = count != dynamic_extent ? count : N - offset; + // SAFETY: span provides that data() points to at least `N` many elements. + // + // If Count is dynamic_extent, `new_extent` becomes `N - offset`. Since + // `offset <= N` from the requires condition, then `offset` is a valid + // offset for data(), and `offset + new_extent = offset + N - offset = N >= + // offset` is also a valid offset that is not before `offset`. This makes a + // span at `offset` with size `new_extent` valid. + // + // Otherwise `count <= N - offset` and `0 <= offset <= N` by the requires + // condition, so `offset <= N - count` and `N - count` can not underflow. + // Then `offset` is a valid offset for data() and `new_extent` is `count <= + // N - offset`, so `offset + new_extent <= offset + N - offset = N` which + // makes both `offset` and `offset + new_extent` valid offsets for data(), + // and since `new_extent` is non-negative, `offset + new_extent` is not + // before `offset` so `new_extent` is a valid size for the span at `data() + + // offset`. + return UNSAFE_BUFFERS({data() + offset, new_extent}); + } + + // Splits a span into two at the given `offset`, returning two spans that + // cover the full range of the original span. + // + // Similar to calling subspan() with the `offset` as the length on the first + // call, and then the `offset` as the offset in the second. + // + // The split_at() overload allows construction of a fixed-size span from a + // compile-time constant. If the input span is fixed-size, both output output + // spans will be. Otherwise, the first will be fixed-size and the second will + // be dynamic-size. + // + // This is a non-std extension that is inspired by the Rust slice::split_at() + // and split_at_mut() methods. + // + // # Checks + // The function CHECKs that the span contains at least `offset` elements and + // will terminate otherwise. + constexpr std::pair, span> split_at(size_t offset) const noexcept { + return {first(offset), subspan(offset)}; + } + + template + requires(Offset <= N) + constexpr std::pair, span> split_at() + const noexcept { + return {first(), subspan()}; + } + + // [span.obs], span observers + constexpr size_t size() const noexcept { return N; } + constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } + [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + // + // # Checks + // The function CHECKs that the `idx` is inside the span and will terminate + // otherwise. + constexpr T& operator[](size_t idx) const noexcept { + CHECK_LT(idx, size()); + // SAFETY: Since data() always points to at least `N` elements, the check + // above ensures `idx < N` and is thus in range for data(). + return UNSAFE_BUFFERS(data()[idx]); + } + + constexpr T& front() const noexcept + requires(N > 0) + { + // SAFETY: Since data() always points to at least `N` elements, the requires + // constraint above ensures `0 < N` and is thus in range for data(). + return UNSAFE_BUFFERS(data()[0]); + } + + constexpr T& back() const noexcept + requires(N > 0) + { + // SAFETY: Since data() always points to at least `N` elements, the requires + // constraint above ensures `N > 0` and thus `N - 1` does not underflow and + // is in range for data(). + return UNSAFE_BUFFERS(data()[N - 1]); + } + + // Returns a pointer to the first element in the span. If the span is empty + // (`size()` is 0), the returned pointer may or may not be null, and it must + // not be dereferenced. + // + // It is always valid to add `size()` to the the pointer in C++ code, though + // it may be invalid in C code when the span is empty. + constexpr T* data() const noexcept { return data_; } + + // [span.iter], span iterator support + constexpr iterator begin() const noexcept { + // SAFETY: span provides that data() points to at least `size()` many + // elements, and size() is non-negative. So data() + size() is a valid + // pointer for the data() allocation. + return UNSAFE_BUFFERS(iterator(data(), data() + size())); + } + + constexpr iterator end() const noexcept { + // SAFETY: span provides that data() points to at least `size()` many + // elements, and size() is non-negative. So data() + size() is a valid + // pointer for the data() allocation. + return UNSAFE_BUFFERS(iterator(data(), data() + size(), data() + size())); + } + + constexpr reverse_iterator rbegin() const noexcept { + return reverse_iterator(end()); + } + + constexpr reverse_iterator rend() const noexcept { + return reverse_iterator(begin()); + } + + // Bounds-checked copy from a non-overlapping span. The spans must be the + // exact same size or a hard CHECK() occurs. If the two spans overlap, + // Undefined Behaviour occurs. + // + // This is a non-std extension that is inspired by the Rust + // slice::copy_from_slice() method. + // + // # Checks + // The function CHECKs that the `other` span has the same size as itself and + // will terminate otherwise. + constexpr void copy_from(span other) + requires(!std::is_const_v) + { + CHECK_EQ(size_bytes(), other.size_bytes()); + // Verify non-overlapping in developer builds. + // + // SAFETY: span provides that data() points to at least size() many + // elements, so adding size() to the data() pointer is well-defined. + DCHECK(UNSAFE_BUFFERS(data() + size()) <= other.data() || + data() >= UNSAFE_BUFFERS(other.data() + other.size())); + // When compiling with -Oz, std::ranges::copy() does not get inlined, which + // makes copy_from() very expensive compared to memcpy for small sizes (up + // to around 4x slower). We observe that this is because ranges::copy() uses + // begin()/end() and span's iterators are checked iterators, not just + // pointers. This additional complexity prevents inlining and breaks the + // ability for the compiler to eliminate code. + // + // See also https://crbug.com/1396134. + // + // We also see std::copy() (with pointer arguments! not iterators) optimize + // and inline better than memcpy() since memcpy() needs to rely on + // size_bytes(), which while computable at compile time when `other` has a + // fixed size, the optimizer stumbles on with -Oz. + // + // SAFETY: The copy() here does not check bounds, but we have verified that + // `this` and `other` have the same bounds above (and are pointers of the + // same type), so `data()` and `other.data()` both have at least + // `other.size()` elements. + UNSAFE_BUFFERS( + std::copy(other.data(), other.data() + other.size(), data())); + } + + // Implicit conversion from std::span to base::span. + // + // We get other conversions for free from std::span's constructors, but it + // does not deduce N on its range constructor. + span(std::span, N> other) + : // SAFETY: std::span contains a valid data pointer and size such + // that pointer+size remains valid. + UNSAFE_BUFFERS( + span(std::ranges::data(other), std::ranges::size(other))) {} + span(std::span other) + requires(std::is_const_v) + : // SAFETY: std::span contains a valid data pointer and size such + // that pointer+size remains valid. + UNSAFE_BUFFERS( + span(std::ranges::data(other), std::ranges::size(other))) {} + + // Implicit conversion from base::span to std::span. + // + // We get other conversions for free from std::span's constructors, but it + // does not deduce N on its range constructor. + operator std::span() const { return std::span(*this); } + operator std::span() const + requires(!std::is_const_v) + { + return std::span(*this); + } + + // Compares two spans for equality by comparing the objects pointed to by the + // spans. The operation is defined for spans of different types as long as the + // types are themselves comparable. + // + // For primitive types, this replaces the less safe `memcmp` function, where + // `memcmp(a.data(), b.data(), a.size())` can be written as `a == b` and can + // no longer go outside the bounds of `b`. Otherwise, it replaced std::equal + // or std::ranges::equal when working with spans, and when no projection is + // needed. + // + // If the spans are of different sizes, they are not equal. If both spans are + // empty, they are always equal (even though their data pointers may differ). + // + // # Implementation note + // The non-template overloads allow implicit conversions to span for + // comparison. + friend constexpr bool operator==(span lhs, span rhs) + requires(std::equality_comparable) + { + return internal::span_cmp(span(lhs), span(rhs)); + } + friend constexpr bool operator==(span lhs, span rhs) + requires(!std::is_const_v && std::equality_comparable) + { + return internal::span_cmp(span(lhs), span(rhs)); + } + template + requires((N == M || M == dynamic_extent) && + std::equality_comparable_with) + friend constexpr bool operator==(span lhs, span rhs) { + return internal::span_cmp(span(lhs), span(rhs)); + } + + private: + // This field is not a raw_ptr<> since span is mostly used for stack + // variables. Use `raw_span` instead for class fields, which does use + // raw_ptr<> internally. + InternalPtrType data_ = nullptr; +}; + +// [span], class template span +template +class GSL_POINTER span { + public: + using element_type = T; + using value_type = std::remove_cv_t; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = CheckedContiguousIterator; + using reverse_iterator = std::reverse_iterator; + static constexpr size_t extent = dynamic_extent; + + constexpr span() noexcept = default; + + // Constructs a span from a contiguous iterator and a size. + // + // # Safety + // The iterator must point to the first of at least `count` many elements, or + // Undefined Behaviour can result as the span will allow access beyond the + // valid range of the collection pointed to by the iterator. + template + requires(internal::CompatibleIter) + UNSAFE_BUFFER_USAGE constexpr span(It first, + StrictNumeric count) noexcept + // The use of to_address() here is to handle the case where the iterator + // `first` is pointing to the container's `end()`. In that case we can + // not use the address returned from the iterator, or dereference it + // through the iterator's `operator*`, but we can store it. We must + // assume in this case that `count` is 0, since the iterator does not + // point to valid data. Future hardening of iterators may disallow + // pulling the address from `end()`, as demonstrated by asserts() in + // libstdc++: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93960. + // + // The span API dictates that the `data()` is accessible when size is 0, + // since the pointer may be valid, so we cannot prevent storing and + // giving out an invalid pointer here without breaking API compatibility + // and our unit tests. Thus protecting against this can likely only be + // successful from inside iterators themselves, where the context about + // the pointer is known. + // + // We can not protect here generally against an invalid iterator/count + // being passed in, since we have no context to determine if the + // iterator or count are valid. + : data_(base::to_address(first)), size_(count) {} + + // Constructs a span from a contiguous iterator and a size. + // + // # Safety + // The begin and end iterators must be for the same allocation, and `begin <= + // end` or Undefined Behaviour can result as the span will allow access beyond + // the valid range of the collection pointed to by `begin`. + template + requires(internal::CompatibleIter && + std::sized_sentinel_for && + !std::convertible_to) + UNSAFE_BUFFER_USAGE constexpr span(It begin, End end) noexcept + // SAFETY: The caller must guarantee that the iterator and end sentinel + // are part of the same allocation, in which case it is the number of + // elements between the iterators and thus a valid size for the pointer to + // the element at `begin`. + // + // We CHECK that `end - begin` did not underflow below. Normally checking + // correctness afterward is flawed, however underflow is not UB and the + // size is not converted to an invalid pointer (which would be UB) before + // we CHECK for underflow. + : UNSAFE_BUFFERS(span(begin, static_cast(end - begin))) { + // Verify `end - begin` did not underflow. CHECK(begin <= end); } - template < - size_t N, - typename = internal::EnableIfSpanCompatibleArray> - constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {} - - template < - typename U, - size_t N, - typename = - internal::EnableIfSpanCompatibleArray&, T, Extent>> - constexpr span(std::array& array) noexcept - : span(std::data(array), N) {} - - template &, T, Extent>> - constexpr span(const std::array& array) noexcept - : span(std::data(array), N) {} - - // Conversion from a container that has compatible std::data() and integral - // std::size(). - template < - typename Container, - typename = - internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic> - constexpr span(Container& container) noexcept - : span(std::data(container), std::size(container)) {} - - template < - typename Container, - typename = internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic< - const Container&, - T, - Extent>> - constexpr span(const Container& container) noexcept - : span(std::data(container), std::size(container)) {} - - constexpr span(const span& other) noexcept = default; - - // Conversions from spans of compatible types and extents: this allows a - // span to be seamlessly used as a span, but not the other way - // around. If extent is not dynamic, OtherExtent has to be equal to Extent. - template < - typename U, - size_t OtherExtent, - typename = - internal::EnableIfLegalSpanConversion> - constexpr span(const span& other) - : span(other.data(), other.size()) {} - - constexpr span& operator=(const span& other) noexcept = default; - ~span() noexcept = default; + template + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr span(T (&arr)[N]) noexcept + // SAFETY: The std::ranges::size() function gives the number of elements + // pointed to by the std::ranges::data() function, which meets the + // requirement of span. + : UNSAFE_BUFFERS(span(std::ranges::data(arr), std::ranges::size(arr))) {} + + template + requires(internal::LegacyCompatibleRange) + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr span(R&& range) noexcept + // SAFETY: The std::ranges::size() function gives the number of elements + // pointed to by the std::ranges::data() function, which meets the + // requirement of span. + : UNSAFE_BUFFERS( + span(std::ranges::data(range), std::ranges::size(range))) {} // [span.sub], span subviews template constexpr span first() const noexcept { - static_assert(Count <= Extent, "Count must not exceed Extent"); - CHECK(Extent != dynamic_extent || Count <= size()); - return {data(), Count}; + CHECK_LE(Count, size()); + // SAFETY: span provides that data() points to at least `size()` many + // elements. `Count` is non-negative by its type and `Count <= size()` from + // the CHECK above. So `Count` is a valid new size for `data()`. + return UNSAFE_BUFFERS(span(data(), Count)); } template constexpr span last() const noexcept { - static_assert(Count <= Extent, "Count must not exceed Extent"); - CHECK(Extent != dynamic_extent || Count <= size()); - return {data() + (size() - Count), Count}; + CHECK_LE(Count, size()); + // SAFETY: span provides that data() points to at least `size()` many + // elements. `Count` is non-negative by its type and `Count <= size()` from + // the check above. So `0 <= size() - Count <= size()`, meaning + // `size() - Count` is a valid new size for `data()` and it will point to + // `Count` many elements. + return UNSAFE_BUFFERS(span(data() + (size() - Count), Count)); + } + + // Returns a span over the first `count` elements. + // + // # Checks + // The function CHECKs that the span contains at least `count` elements and + // will terminate otherwise. + constexpr span first(StrictNumeric count) const noexcept { + CHECK_LE(size_t{count}, size()); + // SAFETY: span provides that data() points to at least `size()` many + // elements. `count` is non-negative by its type and `count <= size()` from + // the CHECK above. So `count` is a valid new size for `data()`. + return UNSAFE_BUFFERS({data(), count}); + } + + // Returns a span over the last `count` elements. + // + // # Checks + // The function CHECKs that the span contains at least `count` elements and + // will terminate otherwise. + constexpr span last(StrictNumeric count) const noexcept { + CHECK_LE(size_t{count}, size()); + // SAFETY: span provides that data() points to at least `size()` many + // elements. `count` is non-negative by its type and `count <= size()` from + // the CHECK above. So `0 <= size() - count <= size()`, meaning + // `size() - count` is a valid new size for `data()` and it will point to + // `count` many elements. + return UNSAFE_BUFFERS({data() + (size() - size_t{count}), count}); } template - constexpr span - subspan() const noexcept { - static_assert(Offset <= Extent, "Offset must not exceed Extent"); - static_assert(Count == dynamic_extent || Count <= Extent - Offset, - "Count must not exceed Extent - Offset"); - CHECK(Extent != dynamic_extent || Offset <= size()); - CHECK(Extent != dynamic_extent || Count == dynamic_extent || - Count <= size() - Offset); - return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset}; - } - - constexpr span first(size_t count) const noexcept { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(count <= size()); - return {data(), count}; - } - - constexpr span last(size_t count) const noexcept { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(count <= size()); - return {data() + (size() - count), count}; - } - - constexpr span subspan(size_t offset, - size_t count = dynamic_extent) const - noexcept { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(offset <= size()); + constexpr span subspan() const noexcept { + CHECK_LE(Offset, size()); + CHECK(Count == dynamic_extent || Count <= size() - Offset); + const size_t new_extent = Count != dynamic_extent ? Count : size() - Offset; + // SAFETY: span provides that data() points to at least `size()` many + // elements. + // + // If Count is dynamic_extent, `new_extent` becomes `size() - Offset`. Since + // `Offset <= size()` from the check above, then `Offset` is a valid offset + // for data(), and `Offset + new_extent = Offset + size() - Offset = size() + // >= Offset` is also a valid offset that is not before `Offset`. This makes + // a span at `Offset` with size `new_extent` valid. + // + // Otherwise `Count <= size() - Offset` and `0 <= Offset <= size()` by the + // check above, so `Offset <= size() - Count` and `size() - Count` can not + // underflow. Then `Offset` is a valid offset for data() and `new_extent` is + // `Count <= size() - Offset`, so `Offset + extent <= Offset + size() - + // Offset = size()` which makes both `Offset` and `Offset + new_extent` + // valid offsets for data(), and since `new_extent` is non-negative, `Offset + // + new_extent` is not before `Offset` so `new_extent` is a valid size for + // the span at `data() + Offset`. + return UNSAFE_BUFFERS(span(data() + Offset, new_extent)); + } + + // Returns a span over the first `count` elements starting at the given + // `offset` from the start of the span. + // + // # Checks + // The function CHECKs that the span contains at least `offset + count` + // elements, or at least `offset` elements if `count` is not specified, and + // will terminate otherwise. + constexpr span subspan(size_t offset, + size_t count = dynamic_extent) const noexcept { + CHECK_LE(offset, size()); CHECK(count == dynamic_extent || count <= size() - offset); - return {data() + offset, count != dynamic_extent ? count : size() - offset}; + const size_t new_extent = count != dynamic_extent ? count : size() - offset; + // SAFETY: span provides that data() points to at least `size()` many + // elements. + // + // If count is dynamic_extent, `new_extent` becomes `size() - offset`. Since + // `offset <= size()` from the check above, then `offset` is a valid offset + // for data(), and `offset + new_extent = offset + size() - offset = size() + // >= offset` is also a valid offset that is not before `offset`. This makes + // a span at `offset` with size `new_extent` valid. + // + // Otherwise `count <= size() - offset` and `0 <= offset <= size()` by the + // checks above, so `offset <= size() - count` and `size() - count` can not + // underflow. Then `offset` is a valid offset for data() and `new_extent` is + // `count <= size() - offset`, so `offset + new_extent <= offset + size() - + // offset = size()` which makes both `offset` and `offset + new_extent` + // valid offsets for data(), and since `new_extent` is non-negative, `offset + // + new_extent` is not before `offset` so `new_extent` is a valid size for + // the span at `data() + offset`. + return UNSAFE_BUFFERS({data() + offset, new_extent}); + } + + // Splits a span into two at the given `offset`, returning two spans that + // cover the full range of the original span. + // + // Similar to calling subspan() with the `offset` as the length on the first + // call, and then the `offset` as the offset in the second. + // + // The split_at() overload allows construction of a fixed-size span from a + // compile-time constant. If the input span is fixed-size, both output output + // spans will be. Otherwise, the first will be fixed-size and the second will + // be dynamic-size. + // + // This is a non-std extension that is inspired by the Rust slice::split_at() + // and split_at_mut() methods. + // + // # Checks + // The function CHECKs that the span contains at least `offset` elements and + // will terminate otherwise. + constexpr std::pair, span> split_at(size_t offset) const noexcept { + return {first(offset), subspan(offset)}; + } + + // An overload of `split_at` which returns a fixed-size span. + // + // # Checks + // The function CHECKs that the span contains at least `Offset` elements and + // will terminate otherwise. + template + constexpr std::pair, span> split_at() const noexcept { + CHECK_LE(Offset, size()); + return {first(), subspan(Offset)}; } // [span.obs], span observers - constexpr size_t size() const noexcept { return ExtentStorage::size(); } + constexpr size_t size() const noexcept { return size_; } constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } // [span.elem], span element access + // + // # Checks + // The function CHECKs that the `idx` is inside the span and will terminate + // otherwise. constexpr T& operator[](size_t idx) const noexcept { - // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. - CHECK(idx < size()); - return *(data() + idx); + CHECK_LT(idx, size()); + // SAFETY: Since data() always points to at least `size()` elements, the + // check above ensures `idx < size()` and is thus in range for data(). + return UNSAFE_BUFFERS(data()[idx]); } + // Returns a reference to the first element in the span. + // + // # Checks + // The function CHECKs that the span is not empty and will terminate + // otherwise. constexpr T& front() const noexcept { - static_assert(Extent == dynamic_extent || Extent > 0, - "Extent must not be 0"); - CHECK(Extent != dynamic_extent || !empty()); - return *data(); + CHECK(!empty()); + // SAFETY: Since data() always points to at least `size()` elements, the + // check above above ensures `0 < size()` and is thus in range for data(). + return UNSAFE_BUFFERS(data()[0]); } + // Returns a reference to the last element in the span. + // + // # Checks + // The function CHECKs that the span is not empty and will terminate + // otherwise. constexpr T& back() const noexcept { - static_assert(Extent == dynamic_extent || Extent > 0, - "Extent must not be 0"); - CHECK(Extent != dynamic_extent || !empty()); - return *(data() + size() - 1); + CHECK(!empty()); + // SAFETY: Since data() always points to at least `size()` elements, the + // check above above ensures `size() > 0` and thus `size() - 1` does not + // underflow and is in range for data(). + return UNSAFE_BUFFERS(data()[size() - 1]); } + // Returns a pointer to the first element in the span. If the span is empty + // (`size()` is 0), the returned pointer may or may not be null, and it must + // not be dereferenced. + // + // It is always valid to add `size()` to the the pointer in C++ code, though + // it may be invalid in C code when the span is empty. constexpr T* data() const noexcept { return data_; } // [span.iter], span iterator support constexpr iterator begin() const noexcept { - return data(); + // SAFETY: span provides that data() points to at least `size()` many + // elements, and size() is non-negative. So data() + size() is a valid + // pointer for the data() allocation. + return UNSAFE_BUFFERS(iterator(data(), data() + size())); } constexpr iterator end() const noexcept { - return data() + size(); + // SAFETY: span provides that data() points to at least `size()` many + // elements, and size() is non-negative. So data() + size() is a valid + // pointer for the data() allocation. + return UNSAFE_BUFFERS(iterator(data(), data() + size(), data() + size())); } constexpr reverse_iterator rbegin() const noexcept { @@ -411,66 +953,222 @@ class span : public internal::ExtentStorage { return reverse_iterator(begin()); } - private: - // This field is not a raw_ptr<> because it was filtered by the rewriter - // for: #constexpr-ctor-field-initializer, #global-scope, #union - InternalPtrType data_; -}; + // Bounds-checked copy from a non-overlapping span. The spans must be the + // exact same size or a hard CHECK() occurs. If the two spans overlap, + // Undefined Behaviour occurs. + // + // This is a non-std extension that is inspired by the Rust + // slice::copy_from_slice() method. + // + // # Checks + // The function CHECKs that the `other` span has the same size as itself and + // will terminate otherwise. + constexpr void copy_from(span other) + requires(!std::is_const_v) + { + CHECK_EQ(size_bytes(), other.size_bytes()); + // Verify non-overlapping in developer builds. + // + // SAFETY: span provides that data() points to at least size() many + // elements, so adding size() to the data() pointer is well-defined. + DCHECK(UNSAFE_BUFFERS(data() + size()) <= other.data() || + data() >= UNSAFE_BUFFERS(other.data() + other.size())); + // When compiling with -Oz, std::ranges::copy() does not get inlined, which + // makes copy_from() very expensive compared to memcpy for small sizes (up + // to around 4x slower). We observe that this is because ranges::copy() uses + // begin()/end() and span's iterators are checked iterators, not just + // pointers. This additional complexity prevents inlining and breaks the + // ability for the compiler to eliminate code. + // + // See also https://crbug.com/1396134. + // + // We also see std::copy() (with pointer arguments! not iterators) optimize + // and inline better than memcpy() since memcpy() needs to rely on + // size_bytes(), which while computable at compile time when `other` has a + // fixed size, the optimizer stumbles on with -Oz. + // + // SAFETY: The copy() here does not check bounds, but we have verified that + // `this` and `other` have the same bounds above (and are pointers of the + // same type), so `data()` and `other.data()` both have at least + // `other.size()` elements. + UNSAFE_BUFFERS( + std::copy(other.data(), other.data() + other.size(), data())); + } -// span::extent can not be declared inline prior to C++17, hence this -// definition is required. -template -constexpr size_t span::extent; + // Compares two spans for equality by comparing the objects pointed to by the + // spans. The operation is defined for spans of different types as long as the + // types are themselves comparable. + // + // For primitive types, this replaces the less safe `memcmp` function, where + // `memcmp(a.data(), b.data(), a.size())` can be written as `a == b` and can + // no longer go outside the bounds of `b`. Otherwise, it replaced std::equal + // or std::ranges::equal when working with spans, and when no projection is + // needed. + // + // If the spans are of different sizes, they are not equal. If both spans are + // empty, they are always equal (even though their data pointers may differ). + // + // # Implementation note + // The non-template overloads allow implicit conversions to span for + // comparison. + friend constexpr bool operator==(span lhs, span rhs) + requires(std::equality_comparable) + { + return internal::span_cmp(span(lhs), span(rhs)); + } + friend constexpr bool operator==(span lhs, span rhs) + requires(!std::is_const_v && std::equality_comparable) + { + return internal::span_cmp(span(lhs), span(rhs)); + } + template + requires(std::equality_comparable_with) + friend constexpr bool operator==(span lhs, span rhs) { + return internal::span_cmp(span(lhs), span(rhs)); + } -template >> -span(It, StrictNumeric) -> span; + private: + // This field is not a raw_ptr<> since span is mostly used for stack + // variables. Use `raw_span` instead for class fields, which does use + // raw_ptr<> internally. + InternalPtrType data_ = nullptr; + size_t size_ = 0; +}; -template >, - typename T = std::remove_reference_t>> -span(It, End) -> span; +// [span.deduct], deduction guides. +template + requires(std::contiguous_iterator) +span(It, EndOrSize) -> span>>; -template -span(T (&)[N]) -> span; +template < + typename R, + typename T = std::remove_reference_t>> + requires(std::ranges::contiguous_range) +span(R&&) + -> span, T, const T>, + internal::ExtentV>; template -span(std::array&) -> span; +span(const T (&)[N]) -> span; -template -span(const std::array&) -> span; +// [span.objectrep], views of object representation +template +constexpr auto as_bytes(span s) noexcept { + constexpr size_t N = X == dynamic_extent ? dynamic_extent : sizeof(T) * X; + // SAFETY: span provides that data() points to at least size_bytes() many + // bytes. So since `uint8_t` has a size of 1 byte, the size_bytes() value is + // a valid size for a span at data() when viewed as `uint8_t*`. + // + // The reinterpret_cast is valid as the alignment of uint8_t (which is 1) is + // always less-than or equal to the alignment of T. + return UNSAFE_BUFFERS(span( + reinterpret_cast(s.data()), s.size_bytes())); +} -template ()))>, - size_t X = internal::Extent::value> -span(Container&&) -> span; +template + requires(!std::is_const_v) +constexpr auto as_writable_bytes(span s) noexcept { + constexpr size_t N = X == dynamic_extent ? dynamic_extent : sizeof(T) * X; + // SAFETY: span provides that data() points to at least size_bytes() many + // bytes. So since `uint8_t` has a size of 1 byte, the size_bytes() value is a + // valid size for a span at data() when viewed as `uint8_t*`. + // + // The reinterpret_cast is valid as the alignment of uint8_t (which is 1) is + // always less-than or equal to the alignment of T. + return UNSAFE_BUFFERS( + span(reinterpret_cast(s.data()), s.size_bytes())); +} -// [span.objectrep], views of object representation +// as_chars() is the equivalent of as_bytes(), except that it returns a +// span of const char rather than const uint8_t. This non-std function is +// added since chrome still represents many things as char arrays which +// rightfully should be uint8_t. template -span -as_bytes(span s) noexcept { - return {reinterpret_cast(s.data()), s.size_bytes()}; +constexpr auto as_chars(span s) noexcept { + constexpr size_t N = X == dynamic_extent ? dynamic_extent : sizeof(T) * X; + // SAFETY: span provides that data() points to at least size_bytes() many + // bytes. So since `char` has a size of 1 byte, the size_bytes() value is a + // valid size for a span at data() when viewed as `char*`. + // + // The reinterpret_cast is valid as the alignment of char (which is 1) is + // always less-than or equal to the alignment of T. + return UNSAFE_BUFFERS(span( + reinterpret_cast(s.data()), s.size_bytes())); } -template >> -span -as_writable_bytes(span s) noexcept { - return {reinterpret_cast(s.data()), s.size_bytes()}; +// as_string_view() converts a span over byte-sized primitives (holding chars or +// uint8_t) into a std::string_view, where each byte is represented as a char. +// It also accepts any type that can implicitly convert to a span, such as +// arrays. +// +// If you want to view an arbitrary span type as a string, first explicitly +// convert it to bytes via `base::as_bytes()`. +// +// For spans over byte-sized primitives, this is sugar for: +// ``` +// std::string_view(as_chars(span).begin(), as_chars(span).end()) +// ``` +constexpr std::string_view as_string_view(span s) noexcept { + return std::string_view(s.begin(), s.end()); +} +constexpr std::string_view as_string_view( + span s) noexcept { + const auto c = as_chars(s); + return std::string_view(c.begin(), c.end()); +} + +// as_writable_chars() is the equivalent of as_writable_bytes(), except that +// it returns a span of char rather than uint8_t. This non-std function is +// added since chrome still represents many things as char arrays which +// rightfully should be uint8_t. +template + requires(!std::is_const_v) +auto as_writable_chars(span s) noexcept { + constexpr size_t N = X == dynamic_extent ? dynamic_extent : sizeof(T) * X; + // SAFETY: span provides that data() points to at least size_bytes() many + // bytes. So since `char` has a size of 1 byte, the size_bytes() value is + // a valid size for a span at data() when viewed as `char*`. + // + // The reinterpret_cast is valid as the alignment of char (which is 1) is + // always less-than or equal to the alignment of T. + return UNSAFE_BUFFERS( + span(reinterpret_cast(s.data()), s.size_bytes())); } -// Type-deducing helpers for constructing a span. -template -constexpr span make_span(T* data, size_t size) noexcept { - return {data, size}; +// Type-deducing helper for constructing a span. +// +// # Safety +// The contiguous iterator `it` must point to the first element of at least +// `size` many elements or Undefined Behaviour may result as the span may give +// access beyond the bounds of the collection pointed to by `it`. +template +UNSAFE_BUFFER_USAGE constexpr auto make_span( + It it, + StrictNumeric size) noexcept { + using T = std::remove_reference_t>; + // SAFETY: The caller guarantees that `it` is the first of at least `size` + // many elements. + return UNSAFE_BUFFERS(span(it, size)); } -template -constexpr span make_span(T* begin, T* end) noexcept { - return {begin, end}; +// Type-deducing helper for constructing a span. +// +// # Checks +// The function CHECKs that `it <= end` and will terminate otherwise. +// +// # Safety +// The contiguous iterator `it` and its end sentinel `end` must be for the same +// allocation or Undefined Behaviour may result as the span may give access +// beyond the bounds of the collection pointed to by `it`. +template >> +UNSAFE_BUFFER_USAGE constexpr auto make_span(It it, End end) noexcept { + using T = std::remove_reference_t>; + // SAFETY: The caller guarantees that `it` and `end` are iterators of the + // same allocation. + return UNSAFE_BUFFERS(span(it, end)); } // make_span utility function that deduces both the span's value_type and extent @@ -485,23 +1183,217 @@ constexpr auto make_span(Container&& container) noexcept { return span(std::forward(container)); } -// make_span utility functions that allow callers to explicit specify the span's +// make_span utility function that allows callers to explicit specify the span's +// extent, the value_type is deduced automatically. This is useful when passing +// a dynamically sized container to a method expecting static spans, when the +// container is known to have the correct size. +// +// Note: This will CHECK that N indeed matches size(container). +// +// # Usage +// As this function is unsafe, the caller must guarantee that the size is +// correct for the iterator, and will not allow the span to reach out of bounds. +// ``` +// // SAFETY: . +// auto static_span = UNSAFE_BUFFERS(base::make_span(it, size)); +// ``` +// +// # Safety +// The contiguous iterator `it` must point to the first element of at least +// `size` many elements or Undefined Behaviour may result as the span may give +// access beyond the bounds of the collection pointed to by `it`. +template +UNSAFE_BUFFER_USAGE constexpr auto make_span( + It it, + StrictNumeric size) noexcept { + using T = std::remove_reference_t>; + // SAFETY: The caller guarantees that `it` is the first of at least `size` + // many elements. + return UNSAFE_BUFFERS(span(it, size)); +} + +// make_span utility function that allows callers to explicit specify the span's // extent, the value_type is deduced automatically. This is useful when passing // a dynamically sized container to a method expecting static spans, when the // container is known to have the correct size. // // Note: This will CHECK that N indeed matches size(container). // -// Usage: auto static_span = base::make_span(...); +// # Usage +// As this function is unsafe, the caller must guarantee that the `end` is from +// the same allocation as the `it` iterator. +// ``` +// // SAFETY: . +// auto static_span = UNSAFE_BUFFERS(base::make_span(it, end)); +// ``` +// +// # Checks +// The function CHECKs that `it <= end` and will terminate otherwise. +// +// # Safety +// The contiguous iterator `it` and its end sentinel `end` must be for the same +// allocation or Undefined Behaviour may result as the span may give access +// beyond the bounds of the collection pointed to by `it`. +template >> +UNSAFE_BUFFER_USAGE constexpr auto make_span(It it, End end) noexcept { + using T = std::remove_reference_t>; + // SAFETY: The caller guarantees that `it` and `end` are iterators of the + // same allocation. + return UNSAFE_BUFFERS(span(it, end)); +} + template constexpr auto make_span(Container&& container) noexcept { using T = std::remove_pointer_t()))>; - return span(std::data(container), std::size(container)); + // SAFETY: The std::size() function gives the number of elements pointed to by + // the std::data() function, which meets the requirement of span. + return UNSAFE_BUFFERS(span(std::data(container), std::size(container))); +} + +// `span_from_ref` converts a reference to T into a span of length 1. This is a +// non-std helper that is inspired by the `std::slice::from_ref()` function from +// Rust. +template +constexpr span span_from_ref( + T& single_object ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { + // SAFETY: Given a valid reference to `single_object` the span of size 1 will + // be a valid span that points to the `single_object`. + return UNSAFE_BUFFERS(span(std::addressof(single_object), 1u)); +} + +// `byte_span_from_ref` converts a reference to T into a span of uint8_t of +// length sizeof(T). This is a non-std helper that is a sugar for +// `as_writable_bytes(span_from_ref(x))`. +// +// Const references are turned into a `span` while mutable +// references are turned into a `span`. +template +constexpr span byte_span_from_ref( + const T& single_object ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { + return as_bytes(span_from_ref(single_object)); +} +template +constexpr span byte_span_from_ref( + T& single_object ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept { + return as_writable_bytes(span_from_ref(single_object)); +} + +// Converts a string literal (such as `"hello"`) to a span of `char` while +// omitting the terminating NUL character. These two are equivalent: +// ``` +// base::span s1 = base::span_from_cstring("hello"); +// base::span s2 = base::span(std::string_view("hello")); +// ``` +// +// If you want to include the NUL terminator, then use the span constructor +// directly, such as: +// ``` +// base::span s = base::span("hello"); +// ``` +template +constexpr span span_from_cstring( + const char (&lit ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) { + return span(lit).template first(); +} + +// Converts a string literal (such as `"hello"`) to a span of `uint8_t` while +// omitting the terminating NUL character. These two are equivalent: +// ``` +// base::span s1 = base::byte_span_from_cstring("hello"); +// base::span s2 = base::as_byte_span(std::string_view("hello")); +// ``` +// +// If you want to include the NUL terminator, then use the span constructor +// directly, such as: +// ``` +// base::span s = base::as_bytes(base::span("hello")); +// ``` +template +constexpr span byte_span_from_cstring( + const char (&lit ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) { + return as_bytes(span(lit).template first()); } +// Convenience function for converting an object which is itself convertible +// to span into a span of bytes (i.e. span of const uint8_t). Typically used +// to convert std::string or string-objects holding chars, or std::vector +// or vector-like objects holding other scalar types, prior to passing them +// into an API that requires byte spans. +template + requires requires(const T& arg) { + requires !std::is_array_v>; + make_span(arg); + } +constexpr span as_byte_span(const T& arg) { + return as_bytes(make_span(arg)); +} + +// This overload for arrays preserves the compile-time size N of the array in +// the span type signature span. +template +constexpr span as_byte_span( + const T (&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) { + return as_bytes(make_span(arr)); +} + +// Convenience function for converting an object which is itself convertible +// to span into a span of mutable bytes (i.e. span of uint8_t). Typically used +// to convert std::string or string-objects holding chars, or std::vector +// or vector-like objects holding other scalar types, prior to passing them +// into an API that requires mutable byte spans. +template + requires requires(T&& arg) { + requires !std::is_array_v>; + make_span(arg); + requires !std::is_const_v; + } +constexpr span as_writable_byte_span(T&& arg) { + return as_writable_bytes(make_span(arg)); +} + +// This overload for arrays preserves the compile-time size N of the array in +// the span type signature span. +template + requires(!std::is_const_v) +constexpr span as_writable_byte_span( + T (&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) { + return as_writable_bytes(make_span(arr)); +} +template + requires(!std::is_const_v) +constexpr span as_writable_byte_span( + T (&&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) { + return as_writable_bytes(make_span(arr)); +} + +namespace internal { + +// Template helper for implementing operator==. +template + requires((N == M || N == dynamic_extent || M == dynamic_extent) && + std::equality_comparable_with) +constexpr bool span_cmp(span l, span r) { + return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); +} + +} // namespace internal + } // namespace base +template +inline constexpr bool + std::ranges::enable_borrowed_range> = true; + +template +inline constexpr bool std::ranges::enable_view> = true; + // EXTENT returns the size of any type that can be converted to a |base::span| // with definite extent, i.e. everything that is a contiguous storage of some // sort with static size. Specifically, this works for std::array in a constexpr @@ -509,8 +1401,8 @@ constexpr auto make_span(Container&& container) noexcept { // * |std::size| should be preferred for plain arrays. // * In run-time contexts, functions such as |std::array::size| should be // preferred. -#define EXTENT(x) \ - ::base::internal::must_not_be_dynamic_extent() +#define EXTENT(x) \ + ::base::internal::must_not_be_dynamic_extent() -#endif // BASE_CONTAINERS_SPAN_H_ +#endif // MINI_CHROMIUM_BASE_CONTAINERS_SPAN_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/util.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/util.h new file mode 100644 index 000000000..8201b0836 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/containers/util.h @@ -0,0 +1,21 @@ +// Copyright 2018 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CONTAINERS_UTIL_H_ +#define MINI_CHROMIUM_BASE_CONTAINERS_UTIL_H_ + +#include + +namespace base { + +// TODO(crbug.com/817982): What we really need is for checked_math.h to be +// able to do checked arithmetic on pointers. +template +inline uintptr_t get_uintptr(const T* t) { + return reinterpret_cast(t); +} + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_CONTAINERS_UTIL_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/immediate_crash.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/immediate_crash.h new file mode 100644 index 000000000..6b9e4a253 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/immediate_crash.h @@ -0,0 +1,50 @@ +// Copyright 2006-2008 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_IMMEDIATE_CRASH_H_ +#define MINI_CHROMIUM_BASE_IMMEDIATE_CRASH_H_ + +#include "build/build_config.h" + +#if BUILDFLAG(IS_WIN) +#include +#endif // BUILDFLAG(IS_WIN) + +#if defined(COMPILER_GCC) +#define IMMEDIATE_CRASH_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif defined(COMPILER_MSVC) +#define IMMEDIATE_CRASH_ALWAYS_INLINE __forceinline +#else +#define IMMEDIATE_CRASH_ALWAYS_INLINE inline +#endif + +namespace base { + +[[noreturn]] IMMEDIATE_CRASH_ALWAYS_INLINE void ImmediateCrash() { +#if defined(COMPILER_MSVC) + __debugbreak(); +#if defined(ARCH_CPU_X86_FAMILY) + __ud2(); +#elif defined(ARCH_CPU_ARM64) + __hlt(0); +#else +#error Unsupported Windows Arch +#endif +#elif defined(ARCH_CPU_X86_FAMILY) + asm("int3; ud2;"); +#elif defined(ARCH_CPU_ARMEL) + asm("bkpt #0; udf #0;"); +#elif defined(ARCH_CPU_ARM64) + asm("brk #0; hlt #0;"); +#else + __builtin_trap(); +#endif +#if defined(__clang__) || defined(COMPILER_GCC) + __builtin_unreachable(); +#endif // defined(__clang__) || defined(COMPILER_GCC) +} + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_IMMEDIATE_CRASH_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc index f9eaa8bd7..959b64fa2 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc @@ -46,7 +46,6 @@ #include #include #elif BUILDFLAG(IS_WIN) -#include #include #elif BUILDFLAG(IS_ANDROID) #include @@ -55,6 +54,7 @@ #endif #include "base/check_op.h" +#include "base/immediate_crash.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -146,6 +146,10 @@ LogMessage::LogMessage(const char* function, } LogMessage::~LogMessage() { + Flush(); +} + +void LogMessage::Flush() { stream_ << std::endl; std::string str_newline(stream_.str()); @@ -372,24 +376,7 @@ LogMessage::~LogMessage() { } if (severity_ == LOG_FATAL) { -#if defined(COMPILER_MSVC) - __debugbreak(); -#if defined(ARCH_CPU_X86_FAMILY) - __ud2(); -#elif defined(ARCH_CPU_ARM64) - __hlt(0); -#else -#error Unsupported Windows Arch -#endif -#elif defined(ARCH_CPU_X86_FAMILY) - asm("int3; ud2;"); -#elif defined(ARCH_CPU_ARMEL) - asm("bkpt #0; udf #0;"); -#elif defined(ARCH_CPU_ARM64) - asm("brk #0; hlt #0;"); -#else - __builtin_trap(); -#endif + base::ImmediateCrash(); } } diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h index fedb1dbdf..30fb2d691 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h @@ -106,10 +106,13 @@ class LogMessage { LogMessage(const LogMessage&) = delete; LogMessage& operator=(const LogMessage&) = delete; - ~LogMessage(); + virtual ~LogMessage(); std::ostream& stream() { return stream_; } + protected: + void Flush(); + private: void Init(const char* function); @@ -198,6 +201,9 @@ class ErrnoLogMessage : public LogMessage { COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) #define COMPACT_GOOGLE_LOG_ERROR_REPORT \ COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage) +// TODO(crbug.com/40254046): Make LOG(FATAL) understood as [[noreturn]]. See +// Chromium or absl implementations for LogMessageFatal subclasses where the +// destructor is annotated as [[noreturn]]. #define COMPACT_GOOGLE_LOG_FATAL \ COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) #define COMPACT_GOOGLE_LOG_DFATAL \ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/raw_ptr_exclusion.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/raw_ptr_exclusion.h new file mode 100644 index 000000000..d18a75efe --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/raw_ptr_exclusion.h @@ -0,0 +1,13 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MEMORY_RAW_PTR_EXCLUSION_H_ +#define MINI_CHROMIUM_BASE_MEMORY_RAW_PTR_EXCLUSION_H_ + +// Not provided in mini_chromuim, as it's part of PartitionAlloc. +#ifndef RAW_PTR_EXCLUSION +#define RAW_PTR_EXCLUSION +#endif + +#endif // MINI_CHROMIUM_BASE_MEMORY_RAW_PTR_EXCLUSION_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h index 220f1b175..49cf86263 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h @@ -7,6 +7,16 @@ #include "base/check.h" +// TODO(crbug.com/40580068): Redefine NOTREACHED() to be [[noreturn]] once +// Crashpad and Chromium have migrated off of the non-noreturn version. This is +// easiest done by defining it as std::abort() as Crashpad currently doesn't +// stream arguments to it. For a more complete implementation we should use +// LOG(FATAL) but that is currently not annotated as [[noreturn]] because +// ~LogMessage is not. See TODO in base/logging.h #define NOTREACHED() DCHECK(false) +// TODO(crbug.com/40580068): Remove this once the NotReachedIsFatal experiment +// has been rolled out in Chromium. +#define NOTREACHED_IN_MIGRATION() DCHECK(false) + #endif // MINI_CHROMIUM_BASE_NOTREACHED_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/basic_ops_impl.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/basic_ops_impl.h new file mode 100644 index 000000000..5db9b2d2d --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/basic_ops_impl.h @@ -0,0 +1,142 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_NUMERICS_BASIC_OPS_IMPL_H_ +#define MINI_CHROMIUM_BASE_NUMERICS_BASIC_OPS_IMPL_H_ + +#include +#include +#include +#include +#include +#include + +namespace base::numerics::internal { + +// The correct type to perform math operations on given values of type `T`. This +// may be a larger type than `T` to avoid promotion to `int` which involves sign +// conversion! +template + requires(std::is_integral_v) +using MathType = std::conditional_t< + sizeof(T) >= sizeof(int), + T, + std::conditional_t, int, unsigned int>>; + +// Reverses the byte order of the integer. +template + requires(std::is_unsigned_v && std::is_integral_v) +inline constexpr T SwapBytes(T value) { + // MSVC intrinsics are not constexpr, so we provide our own constexpr + // implementation. We provide it unconditionally so we can test it on all + // platforms for correctness. + if (std::is_constant_evaluated()) { + if constexpr (sizeof(T) == 1u) { + return value; + } else if constexpr (sizeof(T) == 2u) { + MathType a = (MathType(value) >> 0) & MathType{0xff}; + MathType b = (MathType(value) >> 8) & MathType{0xff}; + return static_cast((a << 8) | (b << 0)); + } else if constexpr (sizeof(T) == 4u) { + T a = (value >> 0) & T{0xff}; + T b = (value >> 8) & T{0xff}; + T c = (value >> 16) & T{0xff}; + T d = (value >> 24) & T{0xff}; + return (a << 24) | (b << 16) | (c << 8) | (d << 0); + } else { + static_assert(sizeof(T) == 8u); + T a = (value >> 0) & T{0xff}; + T b = (value >> 8) & T{0xff}; + T c = (value >> 16) & T{0xff}; + T d = (value >> 24) & T{0xff}; + T e = (value >> 32) & T{0xff}; + T f = (value >> 40) & T{0xff}; + T g = (value >> 48) & T{0xff}; + T h = (value >> 56) & T{0xff}; + return (a << 56) | (b << 48) | (c << 40) | (d << 32) | // + (e << 24) | (f << 16) | (g << 8) | (h << 0); + } + } + +#if _MSC_VER + if constexpr (sizeof(T) == 1u) { + return value; + } else if constexpr (sizeof(T) == sizeof(unsigned short)) { + using U = unsigned short; + return _byteswap_ushort(U{value}); + } else if constexpr (sizeof(T) == sizeof(unsigned long)) { + using U = unsigned long; + return _byteswap_ulong(U{value}); + } else { + static_assert(sizeof(T) == 8u); + return _byteswap_uint64(value); + } +#else + if constexpr (sizeof(T) == 1u) { + return value; + } else if constexpr (sizeof(T) == 2u) { + return __builtin_bswap16(uint16_t{value}); + } else if constexpr (sizeof(T) == 4u) { + return __builtin_bswap32(value); + } else { + static_assert(sizeof(T) == 8u); + return __builtin_bswap64(value); + } +#endif +} + +// Signed values are byte-swapped as unsigned values. +template + requires(std::is_signed_v && std::is_integral_v) +inline constexpr T SwapBytes(T value) { + return static_cast(SwapBytes(static_cast>(value))); +} + +// Converts from a byte array to an integer. +template + requires(std::is_unsigned_v && std::is_integral_v) +inline constexpr T FromLittleEndian(std::span bytes) { + T val; + if (std::is_constant_evaluated()) { + val = T{0}; + for (size_t i = 0u; i < sizeof(T); i += 1u) { + // SAFETY: `i < sizeof(T)` (the number of bytes in T), so `(8 * i)` is + // less than the number of bits in T. + val |= MathType(bytes[i]) << (8u * i); + } + } else { + // SAFETY: `bytes` has sizeof(T) bytes, and `val` is of type `T` so has + // sizeof(T) bytes, and the two can not alias as `val` is a stack variable. + memcpy(&val, bytes.data(), sizeof(T)); + } + return val; +} + +// Converts to a byte array from an integer. +template + requires(std::is_unsigned_v && std::is_integral_v) +inline constexpr std::array ToLittleEndian(T val) { + auto bytes = std::array(); + if (std::is_constant_evaluated()) { + for (size_t i = 0u; i < sizeof(T); i += 1u) { + const auto last_byte = static_cast(val & 0xff); + // The low bytes go to the front of the array in little endian. + bytes[i] = last_byte; + // If `val` is one byte, this shift would be UB. But it's also not needed + // since the loop will not run again. + if constexpr (sizeof(T) > 1u) { + val >>= 8u; + } + } + } else { + // SAFETY: `bytes` has sizeof(T) bytes, and `val` is of type `T` so has + // sizeof(T) bytes, and the two can not alias as `val` is a stack variable. + memcpy(bytes.data(), &val, sizeof(T)); + } + return bytes; +} + +} // namespace base::numerics::internal + +#endif // MINI_CHROMIUM_BASE_NUMERICS_BASIC_OPS_IMPL_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/byte_conversions.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/byte_conversions.h new file mode 100644 index 000000000..3e3e01050 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/byte_conversions.h @@ -0,0 +1,442 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_NUMERICS_BYTE_CONVERSIONS_H_ +#define MINI_CHROMIUM_BASE_NUMERICS_BYTE_CONVERSIONS_H_ + +#include +#include +#include +#include +#include +#include + +#include "base/numerics/basic_ops_impl.h" +#include "build/build_config.h" + +// Chromium only builds and runs on Little Endian machines. +static_assert(ARCH_CPU_LITTLE_ENDIAN); + +namespace base::numerics { + +// Returns a value with all bytes in |x| swapped, i.e. reverses the endianness. +// TODO(pkasting): Once C++23 is available, replace with std::byteswap. +template + requires(std::is_integral_v) +inline constexpr T ByteSwap(T value) { + return numerics::internal::SwapBytes(value); +} + +// Returns a uint8_t with the value in `bytes` interpreted as the native endian +// encoding of the integer for the machine. +// +// This is suitable for decoding integers that were always kept in native +// encoding, such as when stored in shared-memory (or through IPC) as a byte +// buffer. Prefer an explicit little endian when storing and reading data from +// storage, and explicit big endian for network order. +// +// Note that since a single byte can have only one ordering, this just copies +// the byte out of the span. This provides a consistent function for the +// operation nonetheless. +inline constexpr uint8_t U8FromNativeEndian( + std::span bytes) { + return bytes[0]; +} +// Returns a uint16_t with the value in `bytes` interpreted as the native endian +// encoding of the integer for the machine. +// +// This is suitable for decoding integers that were always kept in native +// encoding, such as when stored in shared-memory (or through IPC) as a byte +// buffer. Prefer an explicit little endian when storing and reading data from +// storage, and explicit big endian for network order. +inline constexpr uint16_t U16FromNativeEndian( + std::span bytes) { + return internal::FromLittleEndian(bytes); +} +// Returns a uint32_t with the value in `bytes` interpreted as the native endian +// encoding of the integer for the machine. +// +// This is suitable for decoding integers that were always kept in native +// encoding, such as when stored in shared-memory (or through IPC) as a byte +// buffer. Prefer an explicit little endian when storing and reading data from +// storage, and explicit big endian for network order. +inline constexpr uint32_t U32FromNativeEndian( + std::span bytes) { + return internal::FromLittleEndian(bytes); +} +// Returns a uint64_t with the value in `bytes` interpreted as the native endian +// encoding of the integer for the machine. +// +// This is suitable for decoding integers that were always kept in native +// encoding, such as when stored in shared-memory (or through IPC) as a byte +// buffer. Prefer an explicit little endian when storing and reading data from +// storage, and explicit big endian for network order. +inline constexpr uint64_t U64FromNativeEndian( + std::span bytes) { + return internal::FromLittleEndian(bytes); +} +// Returns a float with the value in `bytes` interpreted as the native endian +// encoding of the number for the machine. +// +// This is suitable for decoding numbers that were always kept in native +// encoding, such as when stored in shared-memory (or through IPC) as a byte +// buffer. Prefer an explicit little endian when storing and reading data from +// storage, and explicit big endian for network order. +inline constexpr float FloatFromNativeEndian( + std::span bytes) { + return std::bit_cast(U32FromNativeEndian(bytes)); +} +// Returns a double with the value in `bytes` interpreted as the native endian +// encoding of the number for the machine. +// +// This is suitable for decoding numbers that were always kept in native +// encoding, such as when stored in shared-memory (or through IPC) as a byte +// buffer. Prefer an explicit little endian when storing and reading data from +// storage, and explicit big endian for network order. +inline constexpr double DoubleFromNativeEndian( + std::span bytes) { + return std::bit_cast(U64FromNativeEndian(bytes)); +} + +// Returns a uint8_t with the value in `bytes` interpreted as a little-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in little endian, +// which is a good practice with storing and reading data from storage. Use +// the native-endian versions when working with values that were always in +// memory, such as when stored in shared-memory (or through IPC) as a byte +// buffer. +// +// Note that since a single byte can have only one ordering, this just copies +// the byte out of the span. This provides a consistent function for the +// operation nonetheless. +inline constexpr uint8_t U8FromLittleEndian( + std::span bytes) { + return bytes[0]; +} +// Returns a uint16_t with the value in `bytes` interpreted as a little-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in little endian, +// which is a good practice with storing and reading data from storage. Use +// the native-endian versions when working with values that were always in +// memory, such as when stored in shared-memory (or through IPC) as a byte +// buffer. +inline constexpr uint16_t U16FromLittleEndian( + std::span bytes) { + return internal::FromLittleEndian(bytes); +} +// Returns a uint32_t with the value in `bytes` interpreted as a little-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in little endian, +// which is a good practice with storing and reading data from storage. Use +// the native-endian versions when working with values that were always in +// memory, such as when stored in shared-memory (or through IPC) as a byte +// buffer. +inline constexpr uint32_t U32FromLittleEndian( + std::span bytes) { + return internal::FromLittleEndian(bytes); +} +// Returns a uint64_t with the value in `bytes` interpreted as a little-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in little endian, +// which is a good practice with storing and reading data from storage. Use +// the native-endian versions when working with values that were always in +// memory, such as when stored in shared-memory (or through IPC) as a byte +// buffer. +inline constexpr uint64_t U64FromLittleEndian( + std::span bytes) { + return internal::FromLittleEndian(bytes); +} +// Returns a float with the value in `bytes` interpreted as a little-endian +// encoding of the integer. +// +// This is suitable for decoding numbers encoded explicitly in little endian, +// which is a good practice with storing and reading data from storage. Use +// the native-endian versions when working with values that were always in +// memory, such as when stored in shared-memory (or through IPC) as a byte +// buffer. +inline constexpr float FloatFromLittleEndian( + std::span bytes) { + return std::bit_cast(U32FromLittleEndian(bytes)); +} +// Returns a double with the value in `bytes` interpreted as a little-endian +// encoding of the integer. +// +// This is suitable for decoding numbers encoded explicitly in little endian, +// which is a good practice with storing and reading data from storage. Use +// the native-endian versions when working with values that were always in +// memory, such as when stored in shared-memory (or through IPC) as a byte +// buffer. +inline constexpr double DoubleFromLittleEndian( + std::span bytes) { + return std::bit_cast(U64FromLittleEndian(bytes)); +} + +// Returns a uint8_t with the value in `bytes` interpreted as a big-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in big endian, such +// as for network order. Use the native-endian versions when working with values +// that were always in memory, such as when stored in shared-memory (or through +// IPC) as a byte buffer. +// +// Note that since a single byte can have only one ordering, this just copies +// the byte out of the span. This provides a consistent function for the +// operation nonetheless. +inline constexpr uint8_t U8FromBigEndian(std::span bytes) { + return bytes[0]; +} +// Returns a uint16_t with the value in `bytes` interpreted as a big-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in big endian, such +// as for network order. Use the native-endian versions when working with values +// that were always in memory, such as when stored in shared-memory (or through +// IPC) as a byte buffer. +inline constexpr uint16_t U16FromBigEndian(std::span bytes) { + return ByteSwap(internal::FromLittleEndian(bytes)); +} +// Returns a uint32_t with the value in `bytes` interpreted as a big-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in big endian, such +// as for network order. Use the native-endian versions when working with values +// that were always in memory, such as when stored in shared-memory (or through +// IPC) as a byte buffer. +inline constexpr uint32_t U32FromBigEndian(std::span bytes) { + return ByteSwap(internal::FromLittleEndian(bytes)); +} +// Returns a uint64_t with the value in `bytes` interpreted as a big-endian +// encoding of the integer. +// +// This is suitable for decoding integers encoded explicitly in big endian, such +// as for network order. Use the native-endian versions when working with values +// that were always in memory, such as when stored in shared-memory (or through +// IPC) as a byte buffer. +inline constexpr uint64_t U64FromBigEndian(std::span bytes) { + return ByteSwap(internal::FromLittleEndian(bytes)); +} +// Returns a float with the value in `bytes` interpreted as a big-endian +// encoding of the integer. +// +// This is suitable for decoding numbers encoded explicitly in big endian, such +// as for network order. Use the native-endian versions when working with values +// that were always in memory, such as when stored in shared-memory (or through +// IPC) as a byte buffer. +inline constexpr float FloatFromBigEndian(std::span bytes) { + return std::bit_cast(U32FromBigEndian(bytes)); +} +// Returns a double with the value in `bytes` interpreted as a big-endian +// encoding of the integer. +// +// This is suitable for decoding numbers encoded explicitly in big endian, such +// as for network order. Use the native-endian versions when working with values +// that were always in memory, such as when stored in shared-memory (or through +// IPC) as a byte buffer. +inline constexpr double DoubleFromBigEndian( + std::span bytes) { + return std::bit_cast(U64FromBigEndian(bytes)); +} + +// Returns a byte array holding the value of a uint8_t encoded as the native +// endian encoding of the integer for the machine. +// +// This is suitable for encoding integers that will always be kept in native +// encoding, such as for storing in shared-memory (or sending through IPC) as a +// byte buffer. Prefer an explicit little endian when storing data into external +// storage, and explicit big endian for network order. +inline constexpr std::array U8ToNativeEndian(uint8_t val) { + return {val}; +} +// Returns a byte array holding the value of a uint16_t encoded as the native +// endian encoding of the integer for the machine. +// +// This is suitable for encoding integers that will always be kept in native +// encoding, such as for storing in shared-memory (or sending through IPC) as a +// byte buffer. Prefer an explicit little endian when storing data into external +// storage, and explicit big endian for network order. +inline constexpr std::array U16ToNativeEndian(uint16_t val) { + return internal::ToLittleEndian(val); +} +// Returns a byte array holding the value of a uint32_t encoded as the native +// endian encoding of the integer for the machine. +// +// This is suitable for encoding integers that will always be kept in native +// encoding, such as for storing in shared-memory (or sending through IPC) as a +// byte buffer. Prefer an explicit little endian when storing data into external +// storage, and explicit big endian for network order. +inline constexpr std::array U32ToNativeEndian(uint32_t val) { + return internal::ToLittleEndian(val); +} +// Returns a byte array holding the value of a uint64_t encoded as the native +// endian encoding of the integer for the machine. +// +// This is suitable for encoding integers that will always be kept in native +// encoding, such as for storing in shared-memory (or sending through IPC) as a +// byte buffer. Prefer an explicit little endian when storing data into external +// storage, and explicit big endian for network order. +inline constexpr std::array U64ToNativeEndian(uint64_t val) { + return internal::ToLittleEndian(val); +} +// Returns a byte array holding the value of a float encoded as the native +// endian encoding of the number for the machine. +// +// This is suitable for encoding numbers that will always be kept in native +// encoding, such as for storing in shared-memory (or sending through IPC) as a +// byte buffer. Prefer an explicit little endian when storing data into external +// storage, and explicit big endian for network order. +inline constexpr std::array FloatToNativeEndian(float val) { + return U32ToNativeEndian(std::bit_cast(val)); +} +// Returns a byte array holding the value of a double encoded as the native +// endian encoding of the number for the machine. +// +// This is suitable for encoding numbers that will always be kept in native +// encoding, such as for storing in shared-memory (or sending through IPC) as a +// byte buffer. Prefer an explicit little endian when storing data into external +// storage, and explicit big endian for network order. +inline constexpr std::array DoubleToNativeEndian(double val) { + return U64ToNativeEndian(std::bit_cast(val)); +} + +// Returns a byte array holding the value of a uint8_t encoded as the +// little-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in little endian, which is +// a good practice with storing and reading data from storage. Use the +// native-endian versions when working with values that will always be in +// memory, such as when stored in shared-memory (or passed through IPC) as a +// byte buffer. +inline constexpr std::array U8ToLittleEndian(uint8_t val) { + return {val}; +} +// Returns a byte array holding the value of a uint16_t encoded as the +// little-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in little endian, which is +// a good practice with storing and reading data from storage. Use the +// native-endian versions when working with values that will always be in +// memory, such as when stored in shared-memory (or passed through IPC) as a +// byte buffer. +inline constexpr std::array U16ToLittleEndian(uint16_t val) { + return internal::ToLittleEndian(val); +} +// Returns a byte array holding the value of a uint32_t encoded as the +// little-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in little endian, which is +// a good practice with storing and reading data from storage. Use the +// native-endian versions when working with values that will always be in +// memory, such as when stored in shared-memory (or passed through IPC) as a +// byte buffer. +inline constexpr std::array U32ToLittleEndian(uint32_t val) { + return internal::ToLittleEndian(val); +} +// Returns a byte array holding the value of a uint64_t encoded as the +// little-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in little endian, which is +// a good practice with storing and reading data from storage. Use the +// native-endian versions when working with values that will always be in +// memory, such as when stored in shared-memory (or passed through IPC) as a +// byte buffer. +inline constexpr std::array U64ToLittleEndian(uint64_t val) { + return internal::ToLittleEndian(val); +} +// Returns a byte array holding the value of a float encoded as the +// little-endian encoding of the number. +// +// This is suitable for encoding numbers explicitly in little endian, which is +// a good practice with storing and reading data from storage. Use the +// native-endian versions when working with values that will always be in +// memory, such as when stored in shared-memory (or passed through IPC) as a +// byte buffer. +inline constexpr std::array FloatToLittleEndian(float val) { + return internal::ToLittleEndian(std::bit_cast(val)); +} +// Returns a byte array holding the value of a double encoded as the +// little-endian encoding of the number. +// +// This is suitable for encoding numbers explicitly in little endian, which is +// a good practice with storing and reading data from storage. Use the +// native-endian versions when working with values that will always be in +// memory, such as when stored in shared-memory (or passed through IPC) as a +// byte buffer. +inline constexpr std::array DoubleToLittleEndian(double val) { + return internal::ToLittleEndian(std::bit_cast(val)); +} + +// Returns a byte array holding the value of a uint8_t encoded as the big-endian +// encoding of the integer. +// +// This is suitable for encoding integers explicitly in big endian, such as for +// network order. Use the native-endian versions when working with values that +// are always in memory, such as when stored in shared-memory (or passed through +// IPC) as a byte buffer. Use the little-endian encoding for storing and reading +// from storage. +inline constexpr std::array U8ToBigEndian(uint8_t val) { + return {val}; +} +// Returns a byte array holding the value of a uint16_t encoded as the +// big-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in big endian, such as for +// network order. Use the native-endian versions when working with values that +// are always in memory, such as when stored in shared-memory (or passed through +// IPC) as a byte buffer. Use the little-endian encoding for storing and reading +// from storage. +inline constexpr std::array U16ToBigEndian(uint16_t val) { + return internal::ToLittleEndian(ByteSwap(val)); +} +// Returns a byte array holding the value of a uint32_t encoded as the +// big-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in big endian, such as for +// network order. Use the native-endian versions when working with values that +// are always in memory, such as when stored in shared-memory (or passed through +// IPC) as a byte buffer. Use the little-endian encoding for storing and reading +// from storage. +inline constexpr std::array U32ToBigEndian(uint32_t val) { + return internal::ToLittleEndian(ByteSwap(val)); +} +// Returns a byte array holding the value of a uint64_t encoded as the +// big-endian encoding of the integer. +// +// This is suitable for encoding integers explicitly in big endian, such as for +// network order. Use the native-endian versions when working with values that +// are always in memory, such as when stored in shared-memory (or passed through +// IPC) as a byte buffer. Use the little-endian encoding for storing and reading +// from storage. +inline constexpr std::array U64ToBigEndian(uint64_t val) { + return internal::ToLittleEndian(ByteSwap(val)); +} +// Returns a byte array holding the value of a float encoded as the big-endian +// encoding of the number. +// +// This is suitable for encoding numbers explicitly in big endian, such as for +// network order. Use the native-endian versions when working with values that +// are always in memory, such as when stored in shared-memory (or passed through +// IPC) as a byte buffer. Use the little-endian encoding for storing and reading +// from storage. +inline constexpr std::array FloatToBigEndian(float val) { + return internal::ToLittleEndian(ByteSwap(std::bit_cast(val))); +} +// Returns a byte array holding the value of a double encoded as the big-endian +// encoding of the number. +// +// This is suitable for encoding numbers explicitly in big endian, such as for +// network order. Use the native-endian versions when working with values that +// are always in memory, such as when stored in shared-memory (or passed through +// IPC) as a byte buffer. Use the little-endian encoding for storing and reading +// from storage. +inline constexpr std::array DoubleToBigEndian(double val) { + return internal::ToLittleEndian(ByteSwap(std::bit_cast(val))); +} + +} // namespace base::numerics + +#endif // MINI_CHROMIUM_BASE_NUMERICS_BYTE_CONVERSIONS_H_ diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.cc b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.cc index f5e0d31a5..08086b6d4 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.cc +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.cc @@ -55,7 +55,7 @@ namespace base { uint64_t RandUint64() { uint64_t number; - RandBytes(&number, sizeof(number)); + RandBytes(byte_span_from_ref(number)); return number; } @@ -104,27 +104,26 @@ double RandDouble() { return result; } -void RandBytes(void* output, size_t output_length) { - if (output_length == 0) { +void RandBytes(span output) { + if (output.empty()) { return; } #if BUILDFLAG(IS_FUCHSIA) - zx_cprng_draw(output, output_length); + zx_cprng_draw(output.data(), output.size()); #elif BUILDFLAG(IS_POSIX) int fd = GetUrandomFD(); - bool success = ReadFromFD(fd, static_cast(output), output_length); + bool success = + ReadFromFD(fd, reinterpret_cast(output.data()), output.size()); CHECK(success); #elif BUILDFLAG(IS_WIN) - char* output_ptr = static_cast(output); - while (output_length > 0) { + while (!output.empty()) { const ULONG output_bytes_this_pass = static_cast(std::min( - output_length, static_cast(std::numeric_limits::max()))); + output.size(), static_cast(std::numeric_limits::max()))); const bool success = - RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + RtlGenRandom(output.data(), output_bytes_this_pass) != FALSE; CHECK(success); - output_length -= output_bytes_this_pass; - output_ptr += output_bytes_this_pass; + output = output.subspan(output_bytes_this_pass); } #endif } @@ -135,7 +134,7 @@ std::string RandBytesAsString(size_t length) { } std::string result(length, std::string::value_type()); - RandBytes(&result[0], length); + RandBytes(as_writable_byte_span(result)); return result; } diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.h index f7af4e4e4..c4225214d 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.h +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.h @@ -9,6 +9,8 @@ #include +#include "base/containers/span.h" + namespace base { uint64_t RandUint64(); @@ -19,7 +21,7 @@ uint64_t RandGenerator(uint64_t range); double RandDouble(); -void RandBytes(void* output, size_t output_length); +void RandBytes(span output); std::string RandBytesAsString(size_t length); } // namespace base diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage.cc b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage.cc index 047f2fc88..8aa6c1171 100644 --- a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage.cc +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage.cc @@ -161,7 +161,7 @@ void OnThreadExitInternal(void* value) { need_to_scan_destructors = true; } if (--remaining_attempts <= 0) { - NOTREACHED(); // Destructors might not have been called. + NOTREACHED_IN_MIGRATION(); // Destructors might not have been called. break; } } diff --git a/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/types/to_address.h b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/types/to_address.h new file mode 100644 index 000000000..5823b5d06 --- /dev/null +++ b/shared/sentry/src/external/crashpad/third_party/mini_chromium/mini_chromium/base/types/to_address.h @@ -0,0 +1,40 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_TYPES_TO_ADDRESS_H_ +#define MINI_CHROMIUM_BASE_TYPES_TO_ADDRESS_H_ + +#include +#include + +// SFINAE-compatible wrapper for `std::to_address()`. +// +// The standard does not require `std::to_address()` to be SFINAE-compatible +// when code attempts instantiation with non-pointer-like types, and libstdc++'s +// implementation hard errors. For the sake of templated code that wants simple, +// unified handling, Chromium instead uses this wrapper, which provides that +// guarantee. This allows code to use "`to_address()` would be valid here" as a +// constraint to detect pointer-like types. +namespace base { + +// Note that calling `std::to_address()` with a function pointer renders the +// program ill-formed. +template + requires(!std::is_function_v) +constexpr T* to_address(T* p) noexcept { + return p; +} + +// These constraints cover the cases where `std::to_address()`'s fancy pointer +// overload is well-specified. +template + requires requires(const P& p) { std::pointer_traits

::to_address(p); } || + requires(const P& p) { p.operator->(); } +constexpr auto to_address(const P& p) noexcept { + return std::to_address(p); +} + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_TYPES_TO_ADDRESS_H_ diff --git a/shared/sentry/src/external/crashpad/tools/tool_support.cc b/shared/sentry/src/external/crashpad/tools/tool_support.cc index 61d4bf854..cd83366ad 100644 --- a/shared/sentry/src/external/crashpad/tools/tool_support.cc +++ b/shared/sentry/src/external/crashpad/tools/tool_support.cc @@ -16,9 +16,9 @@ #include -#include #include +#include "base/containers/heap_array.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -77,7 +77,7 @@ void ToolSupport::UsageHint(const std::string& me, const char* hint) { // static int ToolSupport::Wmain(int argc, wchar_t* argv[], int (*entry)(int, char* [])) { - std::unique_ptr argv_as_utf8(new char*[argc + 1]); + auto argv_as_utf8 = base::HeapArray::Uninit(argc + 1); std::vector storage; storage.reserve(argc); for (int i = 0; i < argc; ++i) { @@ -85,7 +85,7 @@ int ToolSupport::Wmain(int argc, wchar_t* argv[], int (*entry)(int, char* [])) { argv_as_utf8[i] = &storage[i][0]; } argv_as_utf8[argc] = nullptr; - return entry(argc, argv_as_utf8.get()); + return entry(argc, argv_as_utf8.data()); } #endif // BUILDFLAG(IS_WIN) diff --git a/shared/sentry/src/external/crashpad/util/BUILD.gn b/shared/sentry/src/external/crashpad/util/BUILD.gn index 7e06fcdac..d4f4b2d01 100644 --- a/shared/sentry/src/external/crashpad/util/BUILD.gn +++ b/shared/sentry/src/external/crashpad/util/BUILD.gn @@ -33,12 +33,6 @@ if (crashpad_is_apple) { "mach/mig_gen.py", ] - if (crashpad_is_in_fuchsia) { - # TODO(https://fxbug.dev/68780): Remove suppression when fixed. - hermetic_deps = false - all_outputs_fresh = false - } - if (crashpad_is_mac) { sources = [ "$sysroot/usr/include/mach/exc.defs", @@ -319,6 +313,8 @@ crashpad_static_library("util") { if (crashpad_is_apple) { sources += [ + "mac/sysctl.cc", + "mac/sysctl.h", "mac/xattr.cc", "mac/xattr.h", "mach/composite_mach_message_server.cc", @@ -355,8 +351,6 @@ crashpad_static_library("util") { "mac/mac_util.h", "mac/service_management.cc", "mac/service_management.h", - "mac/sysctl.cc", - "mac/sysctl.h", "mach/bootstrap.cc", "mach/bootstrap.h", "mach/child_port_handshake.cc", @@ -603,6 +597,10 @@ crashpad_static_library("util") { ] } + if (crashpad_is_ios) { + configs += [ "../build:crashpad_is_ios_app_extension" ] + } + if (crashpad_is_win) { libs = [ "user32.lib", diff --git a/shared/sentry/src/external/crashpad/util/CMakeLists.txt b/shared/sentry/src/external/crashpad/util/CMakeLists.txt index a260f5d6c..8b3787461 100644 --- a/shared/sentry/src/external/crashpad/util/CMakeLists.txt +++ b/shared/sentry/src/external/crashpad/util/CMakeLists.txt @@ -249,7 +249,7 @@ endif() if(LINUX OR ANDROID) if (LINUX) - if(NOT CURL_FOUND) # Some other lib might bring libcurl already + if(NOT TARGET CURL::libcurl) # Some other lib might bring libcurl already find_package(CURL REQUIRED) endif() @@ -507,6 +507,8 @@ if(APPLE) "-framework UIKit" ) endif() + + target_compile_options(crashpad_util PRIVATE "-Wno-deprecated-declarations") endif() if(LINUX) diff --git a/shared/sentry/src/external/crashpad/util/file/file_io_posix.cc b/shared/sentry/src/external/crashpad/util/file/file_io_posix.cc index 6d02462bd..37e923ac5 100644 --- a/shared/sentry/src/external/crashpad/util/file/file_io_posix.cc +++ b/shared/sentry/src/external/crashpad/util/file/file_io_posix.cc @@ -276,7 +276,7 @@ FileHandle StdioFileHandle(StdioStream stdio_stream) { return STDERR_FILENO; } - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return kInvalidFileHandle; } diff --git a/shared/sentry/src/external/crashpad/util/file/file_io_win.cc b/shared/sentry/src/external/crashpad/util/file/file_io_win.cc index cb9ea1474..876f57835 100644 --- a/shared/sentry/src/external/crashpad/util/file/file_io_win.cc +++ b/shared/sentry/src/external/crashpad/util/file/file_io_win.cc @@ -230,7 +230,7 @@ FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) { method = FILE_END; break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); break; } @@ -283,7 +283,7 @@ FileHandle StdioFileHandle(StdioStream stdio_stream) { standard_handle = STD_ERROR_HANDLE; break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return INVALID_HANDLE_VALUE; } diff --git a/shared/sentry/src/external/crashpad/util/file/output_stream_file_writer.cc b/shared/sentry/src/external/crashpad/util/file/output_stream_file_writer.cc index 9cab03db1..8d9cb976e 100644 --- a/shared/sentry/src/external/crashpad/util/file/output_stream_file_writer.cc +++ b/shared/sentry/src/external/crashpad/util/file/output_stream_file_writer.cc @@ -56,7 +56,7 @@ bool OutputStreamFileWriter::WriteIoVec(std::vector* iovecs) { } FileOffset OutputStreamFileWriter::Seek(FileOffset offset, int whence) { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return -1; } diff --git a/shared/sentry/src/external/crashpad/util/ios/ios_intermediate_dump_format.h b/shared/sentry/src/external/crashpad/util/ios/ios_intermediate_dump_format.h index 9fe7ccf9a..2f0a89807 100644 --- a/shared/sentry/src/external/crashpad/util/ios/ios_intermediate_dump_format.h +++ b/shared/sentry/src/external/crashpad/util/ios/ios_intermediate_dump_format.h @@ -86,6 +86,7 @@ namespace internal { TD(kInactive, 5018) \ TD(kWired, 5019) \ TD(kAddressMask, 5020) \ + TD(kCrashpadUptime, 5021) \ TD(kThreads, 6000) \ TD(kDebugState, 6001) \ TD(kFloatState, 6002) \ diff --git a/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.h b/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.h index 2dfc373a1..7bef9a2e5 100644 --- a/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.h +++ b/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.h @@ -44,6 +44,7 @@ class IOSSystemDataCollector { const std::string& DaylightName() const { return daylight_name_; } bool IsApplicationActive() const { return active_; } uint64_t AddressMask() const { return address_mask_; } + uint64_t InitializationTime() const { return initialization_time_ns_; } // Currently unused by minidump. int Orientation() const { return orientation_; } @@ -82,6 +83,12 @@ class IOSSystemDataCollector { std::string daylight_name_; ActiveApplicationCallback active_application_callback_; uint64_t address_mask_; + + // Time in nanoseconds as returned by ClockMonotonicNanoseconds() to store the + // crashpad start time. This clock increments monotonically but pauses while + // the system is asleep. It should not be compared to other system time + // sources. + uint64_t initialization_time_ns_; }; } // namespace internal diff --git a/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.mm b/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.mm index 28ede1824..104f3cd51 100644 --- a/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.mm +++ b/shared/sentry/src/external/crashpad/util/ios/ios_system_data_collector.mm @@ -22,33 +22,16 @@ #import #include "base/apple/mach_logging.h" +#include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "build/build_config.h" +#include "util/mac/sysctl.h" +#include "util/misc/clock.h" namespace { -std::string ReadStringSysctlByName(const char* name) { - size_t buf_len; - if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) { - PLOG(WARNING) << "sysctlbyname (size) " << name; - return std::string(); - } - - if (buf_len == 0) { - return std::string(); - } - - std::string value(buf_len - 1, '\0'); - if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) { - PLOG(WARNING) << "sysctlbyname " << name; - return std::string(); - } - - return value; -} - template void AddObserver(CFStringRef notification_name, T* observer) { CFNotificationCenterAddObserver( @@ -86,7 +69,8 @@ void AddObserver(CFStringRef notification_name, T* observer) { standard_offset_seconds_(0), daylight_offset_seconds_(0), standard_name_(), - daylight_name_() { + daylight_name_(), + initialization_time_ns_(ClockMonotonicNanoseconds()) { NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; major_version_ = base::saturated_cast(version.majorVersion); @@ -94,13 +78,20 @@ void AddObserver(CFStringRef notification_name, T* observer) { patch_version_ = base::saturated_cast(version.patchVersion); processor_count_ = base::saturated_cast([[NSProcessInfo processInfo] processorCount]); - build_ = ReadStringSysctlByName("kern.osversion"); + build_ = ReadStringSysctlByName("kern.osversion", false); bundle_identifier_ = base::SysNSStringToUTF8([[NSBundle mainBundle] bundleIdentifier]); +// If CRASHPAD_IS_IOS_APP_EXTENSION is defined, then the code is compiled with +// -fapplication-extension and can only be used in an app extension. Otherwise +// check at runtime whether the code is executing in an app extension or not. +#if defined(CRASHPAD_IS_IOS_APP_EXTENSION) + is_extension_ = true; +#else is_extension_ = [[NSBundle mainBundle].bundlePath hasSuffix:@"appex"]; +#endif #if defined(ARCH_CPU_X86_64) - cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor"); + cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor", false); #endif uint32_t addressable_bits = 0; size_t len = sizeof(uint32_t); @@ -170,6 +161,7 @@ void AddObserver(CFStringRef notification_name, T* observer) { (__bridge CFStringRef)UIDeviceOrientationDidChangeNotification, this); OrientationDidChangeNotification(); +#if !defined(CRASHPAD_IS_IOS_APP_EXTENSION) // Foreground/Background. Extensions shouldn't use UIApplication*. if (!is_extension_) { AddObserver< @@ -183,6 +175,7 @@ void AddObserver(CFStringRef notification_name, T* observer) { this); ApplicationDidChangeActiveNotification(); } +#endif } void IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() { @@ -226,6 +219,9 @@ void AddObserver(CFStringRef notification_name, T* observer) { } void IOSSystemDataCollector::ApplicationDidChangeActiveNotification() { +#if defined(CRASHPAD_IS_IOS_APP_EXTENSION) + NOTREACHED_NORETURN(); +#else dispatch_assert_queue_debug(dispatch_get_main_queue()); bool old_active = active_; active_ = [UIApplication sharedApplication].applicationState == @@ -233,6 +229,7 @@ void AddObserver(CFStringRef notification_name, T* observer) { if (active_ != old_active && active_application_callback_) { active_application_callback_(active_); } +#endif } } // namespace internal diff --git a/shared/sentry/src/external/crashpad/util/linux/auxiliary_vector_test.cc b/shared/sentry/src/external/crashpad/util/linux/auxiliary_vector_test.cc index 0c97781fa..f171ef84c 100644 --- a/shared/sentry/src/external/crashpad/util/linux/auxiliary_vector_test.cc +++ b/shared/sentry/src/external/crashpad/util/linux/auxiliary_vector_test.cc @@ -187,13 +187,13 @@ TEST(AuxiliaryVector, SignedBit) { constexpr uint64_t type = 0x0000000012345678; constexpr int32_t neg1_32 = -1; - aux.Insert(type, bit_cast(neg1_32)); + aux.Insert(type, base::bit_cast(neg1_32)); int32_t outval32s; ASSERT_TRUE(aux.GetValue(type, &outval32s)); EXPECT_EQ(outval32s, neg1_32); constexpr int32_t int32_max = std::numeric_limits::max(); - aux.Insert(type, bit_cast(int32_max)); + aux.Insert(type, base::bit_cast(int32_max)); ASSERT_TRUE(aux.GetValue(type, &outval32s)); EXPECT_EQ(outval32s, int32_max); @@ -204,13 +204,13 @@ TEST(AuxiliaryVector, SignedBit) { EXPECT_EQ(outval32u, uint32_max); constexpr int64_t neg1_64 = -1; - aux.Insert(type, bit_cast(neg1_64)); + aux.Insert(type, base::bit_cast(neg1_64)); int64_t outval64s; ASSERT_TRUE(aux.GetValue(type, &outval64s)); EXPECT_EQ(outval64s, neg1_64); constexpr int64_t int64_max = std::numeric_limits::max(); - aux.Insert(type, bit_cast(int64_max)); + aux.Insert(type, base::bit_cast(int64_max)); ASSERT_TRUE(aux.GetValue(type, &outval64s)); EXPECT_EQ(outval64s, int64_max); diff --git a/shared/sentry/src/external/crashpad/util/linux/memory_map.cc b/shared/sentry/src/external/crashpad/util/linux/memory_map.cc index 58de835ed..583835737 100644 --- a/shared/sentry/src/external/crashpad/util/linux/memory_map.cc +++ b/shared/sentry/src/external/crashpad/util/linux/memory_map.cc @@ -18,7 +18,6 @@ #include #include -#include "base/bit_cast.h" #include "base/check_op.h" #include "base/files/file_path.h" #include "base/logging.h" diff --git a/shared/sentry/src/external/crashpad/util/mac/mac_util.cc b/shared/sentry/src/external/crashpad/util/mac/mac_util.cc index cef238729..37b595b60 100644 --- a/shared/sentry/src/external/crashpad/util/mac/mac_util.cc +++ b/shared/sentry/src/external/crashpad/util/mac/mac_util.cc @@ -207,7 +207,7 @@ int MacOSVersionNumber() { #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_13_4 // On macOS 10.13.4 and later, the sysctlbyname above should have been // successful. - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return -1; #else // DT >= 10.13.4 // The Darwin major version is always 4 greater than the macOS minor version diff --git a/shared/sentry/src/external/crashpad/util/mach/child_port_handshake.cc b/shared/sentry/src/external/crashpad/util/mach/child_port_handshake.cc index afbf55f19..3402d2daa 100644 --- a/shared/sentry/src/external/crashpad/util/mach/child_port_handshake.cc +++ b/shared/sentry/src/external/crashpad/util/mach/child_port_handshake.cc @@ -256,7 +256,7 @@ mach_port_t ChildPortHandshakeServer::RunServer( break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); break; } } diff --git a/shared/sentry/src/external/crashpad/util/mach/exc_client_variants.cc b/shared/sentry/src/external/crashpad/util/mach/exc_client_variants.cc index f7cb763af..a2c63139d 100644 --- a/shared/sentry/src/external/crashpad/util/mach/exc_client_variants.cc +++ b/shared/sentry/src/external/crashpad/util/mach/exc_client_variants.cc @@ -123,7 +123,7 @@ kern_return_t UniversalExceptionRaise(exception_behavior_t behavior, new_state_count); default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); return KERN_INVALID_ARGUMENT; } } diff --git a/shared/sentry/src/external/crashpad/util/mach/exception_ports.cc b/shared/sentry/src/external/crashpad/util/mach/exception_ports.cc index b983cc6c5..0f80887a1 100644 --- a/shared/sentry/src/external/crashpad/util/mach/exception_ports.cc +++ b/shared/sentry/src/external/crashpad/util/mach/exception_ports.cc @@ -80,7 +80,7 @@ ExceptionPorts::ExceptionPorts(TargetType target_type, mach_port_t target_port) break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); get_exception_ports_ = nullptr; set_exception_ports_ = nullptr; target_name_ = nullptr; diff --git a/shared/sentry/src/external/crashpad/util/mach/exception_ports_test.cc b/shared/sentry/src/external/crashpad/util/mach/exception_ports_test.cc index b37a99a24..a6d065bc7 100644 --- a/shared/sentry/src/external/crashpad/util/mach/exception_ports_test.cc +++ b/shared/sentry/src/external/crashpad/util/mach/exception_ports_test.cc @@ -188,7 +188,7 @@ class TestExceptionPorts : public MachMultiprocess, } else if (who_crashes_ == kOtherThreadCrashes) { expect_behavior = EXCEPTION_STATE_IDENTITY; } else { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); expect_behavior = 0; } @@ -286,7 +286,7 @@ class TestExceptionPorts : public MachMultiprocess, } default: { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); } } } @@ -369,7 +369,7 @@ class TestExceptionPorts : public MachMultiprocess, break; } default: { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); } } } @@ -520,7 +520,7 @@ class TestExceptionPorts : public MachMultiprocess, } default: { - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); } } } diff --git a/shared/sentry/src/external/crashpad/util/mach/symbolic_constants_mach.cc b/shared/sentry/src/external/crashpad/util/mach/symbolic_constants_mach.cc index eb90bd789..7a5919016 100644 --- a/shared/sentry/src/external/crashpad/util/mach/symbolic_constants_mach.cc +++ b/shared/sentry/src/external/crashpad/util/mach/symbolic_constants_mach.cc @@ -299,14 +299,11 @@ bool StringToExceptionMask(const base::StringPiece& string, size_t pos = -1; do { ++pos; - const char* substring_begin = string.begin() + pos; + const size_t start = pos; pos = string.find('|', pos); - const char* substring_end = (pos == base::StringPiece::npos) - ? string.end() - : (string.begin() + pos); - base::StringPiece substring = string.substr( - substring_begin - string.begin(), substring_end - substring_begin); - + base::StringPiece substring = (pos == base::StringPiece::npos) + ? string.substr(start) + : string.substr(start, pos - start); exception_mask_t temp_mask; if (!StringToExceptionMask(substring, options, &temp_mask)) { return false; diff --git a/shared/sentry/src/external/crashpad/util/misc/arm64_pac_bti.S b/shared/sentry/src/external/crashpad/util/misc/arm64_pac_bti.S index 85da8b564..77961d410 100644 --- a/shared/sentry/src/external/crashpad/util/misc/arm64_pac_bti.S +++ b/shared/sentry/src/external/crashpad/util/misc/arm64_pac_bti.S @@ -34,7 +34,7 @@ #endif #if defined(__ARM_FEATURE_PAC_DEFAULT) -#if ((__ARM_FEATURE_PAC_DEFAULT & ((1<<0)|(1<<2))) == 0) +#if ((__ARM_FEATURE_PAC_DEFAULT & ((1<<0)|(1<<1))) == 0) #error Pointer authentication defines no valid key! #endif #define GNU_PROPERTY_AARCH64_PAC 1 // Has PAC diff --git a/shared/sentry/src/external/crashpad/util/misc/clock_mac.cc b/shared/sentry/src/external/crashpad/util/misc/clock_mac.cc index 81e53170b..2b4dccd0c 100644 --- a/shared/sentry/src/external/crashpad/util/misc/clock_mac.cc +++ b/shared/sentry/src/external/crashpad/util/misc/clock_mac.cc @@ -14,32 +14,12 @@ #include "util/misc/clock.h" -#include - -#include "base/apple/mach_logging.h" - -namespace { - -mach_timebase_info_data_t* TimebaseInternal() { - mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t; - kern_return_t kr = mach_timebase_info(timebase_info); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info"; - return timebase_info; -} - -mach_timebase_info_data_t* Timebase() { - static mach_timebase_info_data_t* timebase_info = TimebaseInternal(); - return timebase_info; -} - -} // namespace +#include namespace crashpad { uint64_t ClockMonotonicNanoseconds() { - uint64_t absolute_time = mach_absolute_time(); - mach_timebase_info_data_t* timebase_info = Timebase(); - return absolute_time * timebase_info->numer / timebase_info->denom; + return clock_gettime_nsec_np(CLOCK_UPTIME_RAW); } } // namespace crashpad diff --git a/shared/sentry/src/external/crashpad/util/misc/reinterpret_bytes_test.cc b/shared/sentry/src/external/crashpad/util/misc/reinterpret_bytes_test.cc index ba72bc526..80bb979b9 100644 --- a/shared/sentry/src/external/crashpad/util/misc/reinterpret_bytes_test.cc +++ b/shared/sentry/src/external/crashpad/util/misc/reinterpret_bytes_test.cc @@ -75,27 +75,27 @@ TEST(ReinterpretBytes, ToSigned) { ExpectReinterpret(from64, &to64, static_cast(0)); to32 = -1; - from64 = bit_cast(to32); + from64 = base::bit_cast(to32); ExpectReinterpret(from64, &to32, to32); to32 = std::numeric_limits::max(); - from64 = bit_cast(to32); + from64 = base::bit_cast(to32); ExpectReinterpret(from64, &to32, to32); to32 = std::numeric_limits::min(); - from64 = bit_cast(to32); + from64 = base::bit_cast(to32); ExpectReinterpret(from64, &to32, to32); to64 = -1; - from64 = bit_cast(to64); + from64 = base::bit_cast(to64); ExpectReinterpret(from64, &to64, to64); to64 = std::numeric_limits::max(); - from64 = bit_cast(to64); + from64 = base::bit_cast(to64); ExpectReinterpret(from64, &to64, to64); to64 = std::numeric_limits::min(); - from64 = bit_cast(to64); + from64 = base::bit_cast(to64); ExpectReinterpret(from64, &to64, to64); } diff --git a/shared/sentry/src/external/crashpad/util/misc/uuid.cc b/shared/sentry/src/external/crashpad/util/misc/uuid.cc index 15870709e..fb27f27c1 100644 --- a/shared/sentry/src/external/crashpad/util/misc/uuid.cc +++ b/shared/sentry/src/external/crashpad/util/misc/uuid.cc @@ -23,13 +23,15 @@ #include #include +#include #include #include +#include "base/containers/span.h" +#include "base/numerics/byte_conversions.h" #include "base/rand_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/sys_byteorder.h" #include "build/build_config.h" #if BUILDFLAG(IS_APPLE) @@ -39,7 +41,9 @@ namespace crashpad { static_assert(sizeof(UUID) == 16, "UUID must be 16 bytes"); -static_assert(std::is_pod::value, "UUID must be POD"); +static_assert(std::is_standard_layout::value, + "UUID must be a standard-layout type"); +static_assert(std::is_trivial::value, "UUID must be a trivial type"); bool UUID::operator==(const UUID& that) const { return memcmp(this, &that, sizeof(*this)) == 0; @@ -53,11 +57,15 @@ void UUID::InitializeToZero() { memset(this, 0, sizeof(*this)); } -void UUID::InitializeFromBytes(const uint8_t* bytes) { - memcpy(this, bytes, sizeof(*this)); - data_1 = base::NetToHost32(data_1); - data_2 = base::NetToHost16(data_2); - data_3 = base::NetToHost16(data_3); +void UUID::InitializeFromBytes(const uint8_t* bytes_ptr) { + // TODO(crbug.com/40284755): This span construction is unsound. The caller + // should provide a span instead of an unbounded pointer. + base::span bytes(bytes_ptr, sizeof(UUID)); + data_1 = base::numerics::U32FromBigEndian(bytes.subspan<0u, 4u>()); + data_2 = base::numerics::U16FromBigEndian(bytes.subspan<4u, 2u>()); + data_3 = base::numerics::U16FromBigEndian(bytes.subspan<6u, 2u>()); + std::ranges::copy(bytes.subspan<8u, 2u>(), data_4); + std::ranges::copy(bytes.subspan<10u, 6u>(), data_5); } bool UUID::InitializeFromString(const base::StringPiece& string) { @@ -108,7 +116,7 @@ bool UUID::InitializeWithNew() { // from libuuid is not available everywhere. // On Windows, do not use UuidCreate() to avoid a dependency on rpcrt4, so // that this function is usable early in DllMain(). - base::RandBytes(this, sizeof(*this)); + base::RandBytes(base::byte_span_from_ref(*this)); // Set six bits per RFC 4122 §4.4 to identify this as a pseudo-random UUID. data_3 = (4 << 12) | (data_3 & 0x0fff); // §4.1.3 diff --git a/shared/sentry/src/external/crashpad/util/net/http_body_gzip_test.cc b/shared/sentry/src/external/crashpad/util/net/http_body_gzip_test.cc index a9438e0ca..6fed433ab 100644 --- a/shared/sentry/src/external/crashpad/util/net/http_body_gzip_test.cc +++ b/shared/sentry/src/external/crashpad/util/net/http_body_gzip_test.cc @@ -21,8 +21,9 @@ #include #include -#include "base/rand_util.h" +#include "base/containers/heap_array.h" #include "base/numerics/safe_conversions.h" +#include "base/rand_util.h" #include "gtest/gtest.h" #include "third_party/zlib/zlib_crashpad.h" #include "util/misc/zlib.h" @@ -54,17 +55,16 @@ void GzipInflate(const std::string& compressed, decompressed->clear(); // There’s got to be at least a small buffer. - buf_size = std::max(buf_size, static_cast(1)); - - std::unique_ptr buf(new uint8_t[buf_size]); + auto buf = base::HeapArray::Uninit( + std::max(buf_size, static_cast(1))); z_stream zlib = {}; zlib.zalloc = Z_NULL; zlib.zfree = Z_NULL; zlib.opaque = Z_NULL; zlib.next_in = reinterpret_cast(const_cast(&compressed[0])); zlib.avail_in = base::checked_cast(compressed.size()); - zlib.next_out = buf.get(); - zlib.avail_out = base::checked_cast(buf_size); + zlib.next_out = buf.data(); + zlib.avail_out = base::checked_cast(buf.size()); int zr = inflateInit2(&zlib, ZlibWindowBitsWithGzipWrapper(0)); ASSERT_EQ(zr, Z_OK) << "inflateInit2: " << ZlibErrorString(zr); @@ -73,9 +73,9 @@ void GzipInflate(const std::string& compressed, zr = inflate(&zlib, Z_FINISH); ASSERT_EQ(zr, Z_STREAM_END) << "inflate: " << ZlibErrorString(zr); - ASSERT_LE(zlib.avail_out, buf_size); - decompressed->assign(reinterpret_cast(buf.get()), - buf_size - zlib.avail_out); + ASSERT_LE(zlib.avail_out, buf.size()); + decompressed->assign(reinterpret_cast(buf.data()), + buf.size() - zlib.avail_out); } void TestGzipDeflateInflate(const std::string& string) { @@ -94,17 +94,17 @@ void TestGzipDeflateInflate(const std::string& string) { size_t buf_size = string.size() + kGzipHeaderSize + (string.empty() ? 2 : (((string.size() + 16383) / 16384) * 5)); - std::unique_ptr buf(new uint8_t[buf_size]); + auto buf = base::HeapArray::Uninit(buf_size); FileOperationResult compressed_bytes = - gzip_stream.GetBytesBuffer(buf.get(), buf_size); + gzip_stream.GetBytesBuffer(buf.data(), buf.size()); ASSERT_NE(compressed_bytes, -1); - ASSERT_LE(static_cast(compressed_bytes), buf_size); + ASSERT_LE(static_cast(compressed_bytes), buf.size()); // Make sure that the stream is really at EOF. uint8_t eof_buf[16]; ASSERT_EQ(gzip_stream.GetBytesBuffer(eof_buf, sizeof(eof_buf)), 0); - std::string compressed(reinterpret_cast(buf.get()), compressed_bytes); + std::string compressed(reinterpret_cast(buf.data()), compressed_bytes); ASSERT_GE(compressed.size(), kGzipHeaderSize); EXPECT_EQ(compressed[0], '\37'); diff --git a/shared/sentry/src/external/crashpad/util/net/http_body_test_util.cc b/shared/sentry/src/external/crashpad/util/net/http_body_test_util.cc index b011f4e37..960eb62a7 100644 --- a/shared/sentry/src/external/crashpad/util/net/http_body_test_util.cc +++ b/shared/sentry/src/external/crashpad/util/net/http_body_test_util.cc @@ -16,8 +16,7 @@ #include -#include - +#include "base/containers/heap_array.h" #include "gtest/gtest.h" #include "util/file/file_io.h" #include "util/net/http_body.h" @@ -30,17 +29,17 @@ std::string ReadStreamToString(HTTPBodyStream* stream) { } std::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size) { - std::unique_ptr buf(new uint8_t[buffer_size]); + auto buf = base::HeapArray::Uninit(buffer_size); std::string result; FileOperationResult bytes_read; - while ((bytes_read = stream->GetBytesBuffer(buf.get(), buffer_size)) != 0) { + while ((bytes_read = stream->GetBytesBuffer(buf.data(), buf.size())) != 0) { if (bytes_read < 0) { ADD_FAILURE() << "Failed to read from stream: " << bytes_read; return std::string(); } - result.append(reinterpret_cast(buf.get()), bytes_read); + result.append(reinterpret_cast(buf.data()), bytes_read); } return result; diff --git a/shared/sentry/src/external/crashpad/util/numeric/int128_test.cc b/shared/sentry/src/external/crashpad/util/numeric/int128_test.cc index 2166c9a43..5bc9e4159 100644 --- a/shared/sentry/src/external/crashpad/util/numeric/int128_test.cc +++ b/shared/sentry/src/external/crashpad/util/numeric/int128_test.cc @@ -33,7 +33,7 @@ TEST(Int128, UInt128) { uint128_struct uint128; static_assert(sizeof(uint128) == sizeof(kBytes), "sizes must be equal"); - uint128 = bit_cast(kBytes); + uint128 = base::bit_cast(kBytes); EXPECT_EQ(uint128.lo, 0x0706050403020100u); EXPECT_EQ(uint128.hi, 0x0f0e0d0c0b0a0908u); diff --git a/shared/sentry/src/external/crashpad/util/posix/spawn_subprocess.cc b/shared/sentry/src/external/crashpad/util/posix/spawn_subprocess.cc index df5b4a3f0..8f1f13d16 100644 --- a/shared/sentry/src/external/crashpad/util/posix/spawn_subprocess.cc +++ b/shared/sentry/src/external/crashpad/util/posix/spawn_subprocess.cc @@ -190,7 +190,8 @@ bool SpawnSubprocess(const std::vector& argv, auto execve_fp = use_path ? execvpe : execve; execve_fp(argv_for_spawn[0], argv_for_spawn, envp_for_spawn); - PLOG(FATAL) << (use_path ? "execvpe" : "execve"); + PLOG(FATAL) << (use_path ? "execvpe" : "execve") << " " + << argv_for_spawn[0]; #else #if BUILDFLAG(IS_APPLE) PosixSpawnAttr attr; @@ -218,7 +219,8 @@ bool SpawnSubprocess(const std::vector& argv, attr_p, argv_for_spawn, envp_for_spawn)) != 0) { - PLOG(FATAL) << (use_path ? "posix_spawnp" : "posix_spawn"); + PLOG(FATAL) << (use_path ? "posix_spawnp" : "posix_spawn") << " " + << argv_for_spawn[0]; } // _exit() instead of exit(), because fork() was called. diff --git a/shared/sentry/src/external/crashpad/util/process/process_memory_mac_test.cc b/shared/sentry/src/external/crashpad/util/process/process_memory_mac_test.cc index 296b536df..08e897b7c 100644 --- a/shared/sentry/src/external/crashpad/util/process/process_memory_mac_test.cc +++ b/shared/sentry/src/external/crashpad/util/process/process_memory_mac_test.cc @@ -23,6 +23,7 @@ #include "base/apple/scoped_mach_port.h" #include "base/apple/scoped_mach_vm.h" +#include "base/containers/heap_array.h" #include "gtest/gtest.h" #include "test/mac/mach_errors.h" #include "util/misc/from_pointer_cast.h" @@ -259,13 +260,12 @@ TEST(ProcessMemoryMac, MappedMemoryDeallocates) { // This is the same but with a big buffer that’s definitely larger than a // single page. This makes sure that the whole mapped region winds up being // deallocated. - const size_t kBigSize = 4 * PAGE_SIZE; - std::unique_ptr big_buffer(new char[kBigSize]); + auto big_buffer = base::HeapArray::Uninit(4 * PAGE_SIZE); test_address = FromPointerCast(&big_buffer[0]); - ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize))); + ASSERT_TRUE((mapped = memory.ReadMapped(test_address, big_buffer.size()))); mapped_address = reinterpret_cast(mapped->data()); - vm_address_t mapped_last_address = mapped_address + kBigSize - 1; + vm_address_t mapped_last_address = mapped_address + big_buffer.size() - 1; EXPECT_TRUE(IsAddressMapped(mapped_address)); EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE)); EXPECT_TRUE(IsAddressMapped(mapped_last_address)); diff --git a/shared/sentry/src/external/crashpad/util/process/process_memory_test.cc b/shared/sentry/src/external/crashpad/util/process/process_memory_test.cc index 11558e407..07b42535e 100644 --- a/shared/sentry/src/external/crashpad/util/process/process_memory_test.cc +++ b/shared/sentry/src/external/crashpad/util/process/process_memory_test.cc @@ -16,8 +16,7 @@ #include -#include - +#include "base/containers/heap_array.h" #include "base/memory/page_size.h" #include "build/build_config.h" #include "gtest/gtest.h" @@ -105,22 +104,20 @@ class MultiprocessAdaptor : public MultiprocessExec { }; #endif // BUILDFLAG(IS_APPLE) -void DoChildReadTestSetup(size_t* region_size, - std::unique_ptr* region) { - *region_size = 4 * base::GetPageSize(); - region->reset(new char[*region_size]); - for (size_t index = 0; index < *region_size; ++index) { - (*region)[index] = static_cast(index % 256); +base::HeapArray DoChildReadTestSetup() { + auto region = base::HeapArray::Uninit(4 * base::GetPageSize()); + for (size_t index = 0; index < region.size(); ++index) { + region[index] = static_cast(index % 256); } + return region; } CRASHPAD_CHILD_TEST_MAIN(ReadTestChild) { - size_t region_size; - std::unique_ptr region; - DoChildReadTestSetup(®ion_size, ®ion); + auto region = DoChildReadTestSetup(); + auto region_size = region.size(); FileHandle out = MultiprocessAdaptor::OutputHandle(); CheckedWriteFile(out, ®ion_size, sizeof(region_size)); - VMAddress address = FromPointerCast(region.get()); + VMAddress address = FromPointerCast(region.data()); CheckedWriteFile(out, &address, sizeof(address)); CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle()); return 0; @@ -136,12 +133,10 @@ class ReadTest : public MultiprocessAdaptor { ReadTest& operator=(const ReadTest&) = delete; void RunAgainstSelf() { - size_t region_size; - std::unique_ptr region; - DoChildReadTestSetup(®ion_size, ®ion); + auto region = DoChildReadTestSetup(); DoTest(GetSelfProcess(), - region_size, - FromPointerCast(region.get())); + region.size(), + FromPointerCast(region.data())); } void RunAgainstChild() { Run(); } @@ -167,50 +162,50 @@ class ReadTest : public MultiprocessAdaptor { #endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || // BUILDFLAG(IS_CHROMEOS) - std::unique_ptr result(new char[region_size]); + auto result = base::HeapArray::Uninit(region_size); // Ensure that the entire region can be read. - ASSERT_TRUE(memory.Read(address, region_size, result.get())); - for (size_t i = 0; i < region_size; ++i) { + ASSERT_TRUE(memory.Read(address, result.size(), result.data())); + for (size_t i = 0; i < result.size(); ++i) { EXPECT_EQ(result[i], static_cast(i % 256)); } // Ensure that a read of length 0 succeeds and doesn’t touch the result. - memset(result.get(), '\0', region_size); - ASSERT_TRUE(memory.Read(address, 0, result.get())); - for (size_t i = 0; i < region_size; ++i) { + memset(result.data(), '\0', result.size()); + ASSERT_TRUE(memory.Read(address, 0, result.data())); + for (size_t i = 0; i < result.size(); ++i) { EXPECT_EQ(result[i], 0); } // Ensure that a read starting at an unaligned address works. - ASSERT_TRUE(memory.Read(address + 1, region_size - 1, result.get())); - for (size_t i = 0; i < region_size - 1; ++i) { + ASSERT_TRUE(memory.Read(address + 1, result.size() - 1, result.data())); + for (size_t i = 0; i < result.size() - 1; ++i) { EXPECT_EQ(result[i], static_cast((i + 1) % 256)); } // Ensure that a read ending at an unaligned address works. - ASSERT_TRUE(memory.Read(address, region_size - 1, result.get())); - for (size_t i = 0; i < region_size - 1; ++i) { + ASSERT_TRUE(memory.Read(address, result.size() - 1, result.data())); + for (size_t i = 0; i < result.size() - 1; ++i) { EXPECT_EQ(result[i], static_cast(i % 256)); } // Ensure that a read starting and ending at unaligned addresses works. - ASSERT_TRUE(memory.Read(address + 1, region_size - 2, result.get())); - for (size_t i = 0; i < region_size - 2; ++i) { + ASSERT_TRUE(memory.Read(address + 1, result.size() - 2, result.data())); + for (size_t i = 0; i < result.size() - 2; ++i) { EXPECT_EQ(result[i], static_cast((i + 1) % 256)); } // Ensure that a read of exactly one page works. size_t page_size = base::GetPageSize(); - ASSERT_GE(region_size, page_size + page_size); - ASSERT_TRUE(memory.Read(address + page_size, page_size, result.get())); + ASSERT_GE(result.size(), page_size + page_size); + ASSERT_TRUE(memory.Read(address + page_size, page_size, result.data())); for (size_t i = 0; i < page_size; ++i) { EXPECT_EQ(result[i], static_cast((i + page_size) % 256)); } // Ensure that reading exactly a single byte works. result[1] = 'J'; - ASSERT_TRUE(memory.Read(address + 2, 1, result.get())); + ASSERT_TRUE(memory.Read(address + 2, 1, result.data())); EXPECT_EQ(result[0], 2); EXPECT_EQ(result[1], 'J'); } @@ -437,14 +432,13 @@ class ReadUnmappedTest : public MultiprocessAdaptor { VMAddress page_addr1 = address; VMAddress page_addr2 = page_addr1 + base::GetPageSize(); - std::unique_ptr result(new char[base::GetPageSize() * 2]); - EXPECT_TRUE(memory.Read(page_addr1, base::GetPageSize(), result.get())); - EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.get())); + auto result = base::HeapArray::Uninit(base::GetPageSize() * 2); + EXPECT_TRUE(memory.Read(page_addr1, base::GetPageSize(), result.data())); + EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.data())); - EXPECT_FALSE( - memory.Read(page_addr1, base::GetPageSize() * 2, result.get())); - EXPECT_FALSE(memory.Read(page_addr2, base::GetPageSize(), result.get())); - EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.get())); + EXPECT_FALSE(memory.Read(page_addr1, result.size(), result.data())); + EXPECT_FALSE(memory.Read(page_addr2, base::GetPageSize(), result.data())); + EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.data())); } }; diff --git a/shared/sentry/src/external/crashpad/util/stream/base94_output_stream_test.cc b/shared/sentry/src/external/crashpad/util/stream/base94_output_stream_test.cc index 1043840d8..09f6b0095 100644 --- a/shared/sentry/src/external/crashpad/util/stream/base94_output_stream_test.cc +++ b/shared/sentry/src/external/crashpad/util/stream/base94_output_stream_test.cc @@ -20,6 +20,7 @@ #include #include +#include "base/containers/heap_array.h" #include "base/rand_util.h" #include "base/strings/stringprintf.h" #include "gtest/gtest.h" @@ -71,17 +72,16 @@ class Base94OutputStreamTest : public testing::Test { } const uint8_t* BuildDeterministicInput(size_t size) { - deterministic_input_ = std::make_unique(size); - uint8_t* deterministic_input_base = deterministic_input_.get(); + deterministic_input_ = base::HeapArray::WithSize(size); while (size-- > 0) - deterministic_input_base[size] = static_cast(size); - return deterministic_input_base; + deterministic_input_[size] = static_cast(size); + return deterministic_input_.data(); } const uint8_t* BuildRandomInput(size_t size) { - input_ = std::make_unique(size); - base::RandBytes(&input_[0], size); - return input_.get(); + input_ = base::HeapArray::Uninit(size); + base::RandBytes(input_); + return input_.data(); } Base94OutputStream* round_trip() const { return round_trip_.get(); } @@ -127,8 +127,8 @@ class Base94OutputStreamTest : public testing::Test { TestOutputStream* encode_test_output_stream_; TestOutputStream* decode_test_output_stream_; TestOutputStream* round_trip_test_output_stream_; - std::unique_ptr input_; - std::unique_ptr deterministic_input_; + base::HeapArray input_; + base::HeapArray deterministic_input_; }; TEST_F(Base94OutputStreamTest, Encoding) { diff --git a/shared/sentry/src/external/crashpad/util/stream/zlib_output_stream_test.cc b/shared/sentry/src/external/crashpad/util/stream/zlib_output_stream_test.cc index 768851683..25de62ad6 100644 --- a/shared/sentry/src/external/crashpad/util/stream/zlib_output_stream_test.cc +++ b/shared/sentry/src/external/crashpad/util/stream/zlib_output_stream_test.cc @@ -19,6 +19,7 @@ #include #include +#include "base/containers/heap_array.h" #include "base/rand_util.h" #include "base/strings/stringprintf.h" #include "gtest/gtest.h" @@ -46,17 +47,16 @@ class ZlibOutputStreamTest : public testing::Test { ZlibOutputStreamTest& operator=(const ZlibOutputStreamTest&) = delete; const uint8_t* BuildDeterministicInput(size_t size) { - deterministic_input_ = std::make_unique(size); - uint8_t* deterministic_input_base = deterministic_input_.get(); + deterministic_input_ = base::HeapArray::WithSize(size); while (size-- > 0) - deterministic_input_base[size] = static_cast(size); - return deterministic_input_base; + deterministic_input_[size] = static_cast(size); + return deterministic_input_.data(); } const uint8_t* BuildRandomInput(size_t size) { - input_ = std::make_unique(size); - base::RandBytes(&input_[0], size); - return input_.get(); + input_ = base::HeapArray::Uninit(size); + base::RandBytes(input_); + return input_.data(); } const TestOutputStream& test_output_stream() const { @@ -69,8 +69,8 @@ class ZlibOutputStreamTest : public testing::Test { private: std::unique_ptr zlib_output_stream_; - std::unique_ptr input_; - std::unique_ptr deterministic_input_; + base::HeapArray input_; + base::HeapArray deterministic_input_; TestOutputStream* test_output_stream_; // weak, owned by zlib_output_stream_ }; diff --git a/shared/sentry/src/external/crashpad/util/synchronization/scoped_spin_guard.h b/shared/sentry/src/external/crashpad/util/synchronization/scoped_spin_guard.h index f924d991a..081419222 100644 --- a/shared/sentry/src/external/crashpad/util/synchronization/scoped_spin_guard.h +++ b/shared/sentry/src/external/crashpad/util/synchronization/scoped_spin_guard.h @@ -73,10 +73,17 @@ class ScopedSpinGuard final { ClockMonotonicNanoseconds() + timeout_nanos; while (true) { bool expected_current_value = false; - if (state.locked.compare_exchange_weak(expected_current_value, - true, - std::memory_order_acquire, - std::memory_order_relaxed)) { + // `std::atomic::compare_exchange_weak()` is allowed to spuriously fail on + // architectures like ARM, which can cause timeouts even for + // single-threaded cases (https://crbug.com/340980960, + // http://b/296082201). + // + // Use `std::compare_exchange_strong()` instead to avoid spurious failures + // in the common case. + if (state.locked.compare_exchange_strong(expected_current_value, + true, + std::memory_order_acquire, + std::memory_order_relaxed)) { return std::make_optional(state); } if (ClockMonotonicNanoseconds() >= clock_end_time_nanos) { @@ -85,7 +92,7 @@ class ScopedSpinGuard final { SleepNanoseconds(kSpinGuardSleepTimeNanos); } - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); } ~ScopedSpinGuard() { diff --git a/shared/sentry/src/external/crashpad/util/win/exception_handler_server.cc b/shared/sentry/src/external/crashpad/util/win/exception_handler_server.cc index 32d767df7..bb5b90a1d 100644 --- a/shared/sentry/src/external/crashpad/util/win/exception_handler_server.cc +++ b/shared/sentry/src/external/crashpad/util/win/exception_handler_server.cc @@ -22,6 +22,7 @@ #include #include "base/check.h" +#include "base/containers/heap_array.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/rand_util.h" @@ -295,16 +296,16 @@ void ExceptionHandlerServer::InitializeWithInheritedDataForInitialClient( first_pipe_instance_.reset(initial_client_data.first_pipe_instance()); // TODO(scottmg): Vista+. Might need to pass through or possibly find an Nt*. - size_t bytes = sizeof(wchar_t) * _MAX_PATH + sizeof(FILE_NAME_INFO); - std::unique_ptr data(new uint8_t[bytes]); + auto data = base::HeapArray::Uninit(sizeof(wchar_t) * _MAX_PATH + + sizeof(FILE_NAME_INFO)); if (!GetFileInformationByHandleEx(first_pipe_instance_.get(), FileNameInfo, - data.get(), - static_cast(bytes))) { + data.data(), + static_cast(data.size()))) { PLOG(FATAL) << "GetFileInformationByHandleEx"; } FILE_NAME_INFO* file_name_info = - reinterpret_cast(data.get()); + reinterpret_cast(data.data()); pipe_name_ = L"\\\\.\\pipe" + std::wstring(file_name_info->FileName, file_name_info->FileNameLength / diff --git a/shared/sentry/src/external/crashpad/util/win/module_version.cc b/shared/sentry/src/external/crashpad/util/win/module_version.cc index ca7aef836..e06236694 100644 --- a/shared/sentry/src/external/crashpad/util/win/module_version.cc +++ b/shared/sentry/src/external/crashpad/util/win/module_version.cc @@ -17,8 +17,7 @@ #include #include -#include - +#include "base/containers/heap_array.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" @@ -33,15 +32,18 @@ bool GetModuleVersionAndType(const base::FilePath& path, return false; } - std::unique_ptr data(new uint8_t[size]); - if (!GetFileVersionInfo(path.value().c_str(), 0, size, data.get())) { + auto data = base::HeapArray::Uninit(size); + if (!GetFileVersionInfo(path.value().c_str(), + 0, + static_cast(data.size()), + data.data())) { PLOG(WARNING) << "GetFileVersionInfo: " << base::WideToUTF8(path.value()); return false; } VS_FIXEDFILEINFO* fixed_file_info; UINT ffi_size; - if (!VerQueryValue(data.get(), + if (!VerQueryValue(data.data(), L"\\", reinterpret_cast(&fixed_file_info), &ffi_size)) { diff --git a/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.cc b/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.cc index a1a9e3713..d4cd72c47 100644 --- a/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.cc +++ b/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.cc @@ -17,7 +17,10 @@ #include #include +#include "base/immediate_crash.h" +#include "base/scoped_clear_last_error.h" #include "base/strings/stringprintf.h" +#include "build/build_config.h" namespace { @@ -68,8 +71,29 @@ NtstatusLogMessage::NtstatusLogMessage( } NtstatusLogMessage::~NtstatusLogMessage() { + AppendError(); +} + +void NtstatusLogMessage::AppendError() { + // Don't let actions from this method affect the system error after returning. + base::ScopedClearLastError scoped_clear_last_error; + stream() << ": " << FormatNtstatus(ntstatus_) << base::StringPrintf(" (0x%08lx)", ntstatus_); } +#if defined(COMPILER_MSVC) +// Ignore warning that ~NtStatusLogMessageFatal never returns. +#pragma warning(push) +#pragma warning(disable : 4722) +#endif // COMPILER_MSVC +NtstatusLogMessageFatal::~NtstatusLogMessageFatal() { + AppendError(); + Flush(); + base::ImmediateCrash(); +} +#if defined(COMPILER_MSVC) +#pragma warning(pop) // C4722 +#endif // COMPILER_MSVC + } // namespace logging diff --git a/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.h b/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.h index dfcac5d03..183c90678 100644 --- a/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.h +++ b/shared/sentry/src/external/crashpad/util/win/ntstatus_logging.h @@ -37,10 +37,19 @@ class NtstatusLogMessage : public logging::LogMessage { ~NtstatusLogMessage(); + protected: + void AppendError(); + private: DWORD ntstatus_; }; +class NtstatusLogMessageFatal final : public NtstatusLogMessage { + public: + using NtstatusLogMessage::NtstatusLogMessage; + [[noreturn]] ~NtstatusLogMessageFatal() override; +}; + } // namespace logging #define NTSTATUS_LOG_STREAM(severity, ntstatus) \ diff --git a/shared/sentry/src/external/crashpad/util/win/process_info.cc b/shared/sentry/src/external/crashpad/util/win/process_info.cc index 6c23cd009..1290e22cf 100644 --- a/shared/sentry/src/external/crashpad/util/win/process_info.cc +++ b/shared/sentry/src/external/crashpad/util/win/process_info.cc @@ -24,6 +24,7 @@ #include #include "base/check_op.h" +#include "base/containers/heap_array.h" #include "base/logging.h" #include "base/memory/free_deleter.h" #include "base/process/memory.h" @@ -147,33 +148,34 @@ MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64( // NtQueryObject with a retry for size mismatch as well as a minimum size to // retrieve (and expect). -std::unique_ptr QueryObject( +base::HeapArray QueryObject( HANDLE handle, OBJECT_INFORMATION_CLASS object_information_class, ULONG minimum_size) { - ULONG size = minimum_size; ULONG return_length; - std::unique_ptr buffer(new uint8_t[size]); - NTSTATUS status = crashpad::NtQueryObject( - handle, object_information_class, buffer.get(), size, &return_length); + auto buffer = base::HeapArray::Uninit(minimum_size); + NTSTATUS status = crashpad::NtQueryObject(handle, + object_information_class, + buffer.data(), + (ULONG)buffer.size(), + &return_length); if (status == STATUS_INFO_LENGTH_MISMATCH) { - DCHECK_GT(return_length, size); - size = return_length; - - // Free the old buffer before attempting to allocate a new one. - buffer.reset(); - - buffer.reset(new uint8_t[size]); - status = crashpad::NtQueryObject( - handle, object_information_class, buffer.get(), size, &return_length); + DCHECK_GT(return_length, buffer.size()); + + buffer = base::HeapArray::Uninit(return_length); + status = crashpad::NtQueryObject(handle, + object_information_class, + buffer.data(), + (ULONG)buffer.size(), + &return_length); } if (!NT_SUCCESS(status)) { NTSTATUS_LOG(ERROR, status) << "NtQueryObject"; - return nullptr; + return base::HeapArray(); } - DCHECK_LE(return_length, size); + DCHECK_LE(return_length, buffer.size()); DCHECK_GE(return_length, minimum_size); return buffer; } @@ -413,14 +415,14 @@ std::vector ProcessInfo::BuildHandleVector( // information, but include the information that we do have already. ScopedKernelHANDLE scoped_dup_handle(dup_handle); - std::unique_ptr object_basic_information_buffer = + auto object_basic_information_buffer = QueryObject(dup_handle, ObjectBasicInformation, sizeof(PUBLIC_OBJECT_BASIC_INFORMATION)); - if (object_basic_information_buffer) { + if (!object_basic_information_buffer.empty()) { PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information = reinterpret_cast( - object_basic_information_buffer.get()); + object_basic_information_buffer.data()); // The Attributes and GrantedAccess sometimes differ slightly between // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in @@ -439,14 +441,14 @@ std::vector ProcessInfo::BuildHandleVector( result_handle.handle_count = object_basic_information->HandleCount - 1; } - std::unique_ptr object_type_information_buffer = + auto object_type_information_buffer = QueryObject(dup_handle, ObjectTypeInformation, sizeof(PUBLIC_OBJECT_TYPE_INFORMATION)); - if (object_type_information_buffer) { + if (!object_type_information_buffer.empty()) { PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information = reinterpret_cast( - object_type_information_buffer.get()); + object_type_information_buffer.data()); DCHECK_EQ(object_type_information->TypeName.Length % sizeof(result_handle.type_name[0]), diff --git a/shared/sentry/src/external/crashpad/util/win/process_info_test.cc b/shared/sentry/src/external/crashpad/util/win/process_info_test.cc index a4a5a57dc..edd4c417d 100644 --- a/shared/sentry/src/external/crashpad/util/win/process_info_test.cc +++ b/shared/sentry/src/external/crashpad/util/win/process_info_test.cc @@ -20,6 +20,7 @@ #include +#include "base/containers/heap_array.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/strings/stringprintf.h" @@ -507,26 +508,23 @@ TEST(ProcessInfo, ReadableRanges) { // Also make sure what we think we can read corresponds with what we can // actually read. - std::unique_ptr into(new unsigned char[kBlockSize * 6]); + auto into = base::HeapArray::Uninit(kBlockSize * 6); SIZE_T bytes_read; EXPECT_TRUE(ReadProcessMemory( - current_process, readable1, into.get(), kBlockSize, &bytes_read)); + current_process, readable1, into.data(), kBlockSize, &bytes_read)); EXPECT_EQ(bytes_read, kBlockSize); EXPECT_TRUE(ReadProcessMemory( - current_process, readable2, into.get(), kBlockSize * 2, &bytes_read)); + current_process, readable2, into.data(), kBlockSize * 2, &bytes_read)); EXPECT_EQ(bytes_read, kBlockSize * 2); EXPECT_FALSE(ReadProcessMemory( - current_process, no_access, into.get(), kBlockSize, &bytes_read)); + current_process, no_access, into.data(), kBlockSize, &bytes_read)); EXPECT_FALSE(ReadProcessMemory( - current_process, reserve_region, into.get(), kBlockSize, &bytes_read)); - EXPECT_FALSE(ReadProcessMemory(current_process, - reserve_region, - into.get(), - kBlockSize * 6, - &bytes_read)); + current_process, reserve_region, into.data(), kBlockSize, &bytes_read)); + EXPECT_FALSE(ReadProcessMemory( + current_process, reserve_region, into.data(), into.size(), &bytes_read)); } TEST(ProcessInfo, Handles) { @@ -633,15 +631,15 @@ TEST(ProcessInfo, Handles) { } TEST(ProcessInfo, OutOfRangeCheck) { - constexpr size_t kAllocationSize = 12345; - std::unique_ptr safe_memory(new char[kAllocationSize]); + auto safe_memory = base::HeapArray::Uninit(12345); ProcessInfo info; info.Initialize(GetCurrentProcess()); EXPECT_TRUE( info.LoggingRangeIsFullyReadable(CheckedRange( - FromPointerCast(safe_memory.get()), kAllocationSize))); + FromPointerCast(safe_memory.data()), + safe_memory.size()))); EXPECT_FALSE(info.LoggingRangeIsFullyReadable( CheckedRange(0, 1024))); } diff --git a/shared/sentry/src/external/crashpad/util/win/registration_protocol_win_test.cc b/shared/sentry/src/external/crashpad/util/win/registration_protocol_win_test.cc index 20c752398..9ce265ac1 100644 --- a/shared/sentry/src/external/crashpad/util/win/registration_protocol_win_test.cc +++ b/shared/sentry/src/external/crashpad/util/win/registration_protocol_win_test.cc @@ -98,7 +98,7 @@ void CheckAce(PACL acl, mask = label_ace->Mask; } break; default: - NOTREACHED(); + NOTREACHED_IN_MIGRATION(); break; } diff --git a/shared/sentry/src/include/sentry.h b/shared/sentry/src/include/sentry.h index ba462eae5..413b8da39 100644 --- a/shared/sentry/src/include/sentry.h +++ b/shared/sentry/src/include/sentry.h @@ -30,7 +30,7 @@ extern "C" { # define SENTRY_SDK_NAME "sentry.native" # endif #endif -#define SENTRY_SDK_VERSION "0.7.6" +#define SENTRY_SDK_VERSION "0.7.9" #define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION /* common platform detection */ @@ -1366,6 +1366,9 @@ SENTRY_API sentry_uuid_t sentry_capture_event(sentry_value_t event); * Captures an exception to be handled by the backend. * * This is safe to be called from a crashing thread and may not return. + * + * Note: The `crashpad` client currently supports this only on Windows. `inproc` + * and `breakpad` support it on all platforms. */ SENTRY_EXPERIMENTAL_API void sentry_handle_exception( const sentry_ucontext_t *uctx); diff --git a/shared/sentry/src/ndk/gradle.properties b/shared/sentry/src/ndk/gradle.properties index 57341cd59..c2d2e68fc 100644 --- a/shared/sentry/src/ndk/gradle.properties +++ b/shared/sentry/src/ndk/gradle.properties @@ -10,7 +10,7 @@ android.useAndroidX=true android.defaults.buildfeatures.buildconfig=true # Release information, used for maven publishing -versionName=0.7.6 +versionName=0.7.9 # disable renderscript, it's enabled by default android.defaults.buildfeatures.renderscript=false diff --git a/shared/sentry/src/ndk/lib/CMakeLists.txt b/shared/sentry/src/ndk/lib/CMakeLists.txt index b9f8f4a4b..ff1471b35 100644 --- a/shared/sentry/src/ndk/lib/CMakeLists.txt +++ b/shared/sentry/src/ndk/lib/CMakeLists.txt @@ -12,6 +12,7 @@ set(SENTRY_BUILD_SHARED_LIBS ON) add_subdirectory(${SENTRY_NATIVE_SRC} sentry_build) # Link to sentry-native -target_link_libraries(sentry-android PRIVATE - $ -) +target_link_libraries(sentry-android PRIVATE $) + +# Support 16KB page sizes +target_link_options(sentry-android PRIVATE "-Wl,-z,max-page-size=16384") diff --git a/shared/sentry/src/ndk/lib/build.gradle.kts b/shared/sentry/src/ndk/lib/build.gradle.kts index 8e3600a59..3381b21b9 100644 --- a/shared/sentry/src/ndk/lib/build.gradle.kts +++ b/shared/sentry/src/ndk/lib/build.gradle.kts @@ -86,6 +86,12 @@ android { ignore = true } } + + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } } dependencies { diff --git a/shared/sentry/src/ndk/sample/CMakeLists.txt b/shared/sentry/src/ndk/sample/CMakeLists.txt index de05d5a02..10933b78c 100644 --- a/shared/sentry/src/ndk/sample/CMakeLists.txt +++ b/shared/sentry/src/ndk/sample/CMakeLists.txt @@ -16,3 +16,5 @@ target_link_libraries(ndk-sample PRIVATE ${LOG_LIB} $ ) +# Support 16KB page sizes +target_link_options(ndk-sample PRIVATE "-Wl,-z,max-page-size=16384") diff --git a/shared/sentry/src/ndk/sample/build.gradle.kts b/shared/sentry/src/ndk/sample/build.gradle.kts index 5390793df..2227fe1ea 100644 --- a/shared/sentry/src/ndk/sample/build.gradle.kts +++ b/shared/sentry/src/ndk/sample/build.gradle.kts @@ -63,6 +63,12 @@ android { kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() } + packagingOptions { + jniLibs { + useLegacyPackaging = true + } + } + ndkVersion = "27.0.12077973" } dependencies { diff --git a/shared/sentry/src/sentry-config.cmake.in b/shared/sentry/src/sentry-config.cmake.in index 16f24a0de..ac75b8d3e 100644 --- a/shared/sentry/src/sentry-config.cmake.in +++ b/shared/sentry/src/sentry-config.cmake.in @@ -5,30 +5,26 @@ set(SENTRY_BACKEND @SENTRY_BACKEND@) set(SENTRY_TRANSPORT @SENTRY_TRANSPORT@) set(SENTRY_BUILD_SHARED_LIBS @SENTRY_BUILD_SHARED_LIBS@) set(SENTRY_LINK_PTHREAD @SENTRY_LINK_PTHREAD@) +set(SENTRY_TRANSPORT_COMPRESSION @SENTRY_TRANSPORT_COMPRESSION@) +set(SENTRY_BREAKPAD_SYSTEM @SENTRY_BREAKPAD_SYSTEM@) +set(CRASHPAD_ZLIB_SYSTEM @CRASHPAD_ZLIB_SYSTEM@) -if(SENTRY_BACKEND STREQUAL "crashpad") - include("${CMAKE_CURRENT_LIST_DIR}/sentry_crashpad-targets.cmake") - if(NOT MSVC AND NOT SENTRY_BUILD_SHARED_LIBS) - find_dependency(ZLIB REQUIRED) - endif() +if(NOT SENTRY_BUILD_SHARED_LIBS) + if(SENTRY_TRANSPORT_COMPRESSION OR CRASHPAD_ZLIB_SYSTEM) + find_dependency(ZLIB) + endif() + if(SENTRY_BACKEND STREQUAL "breakpad" AND SENTRY_BREAKPAD_SYSTEM) + find_dependency(PkgConfig) + pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client) + endif() + if(SENTRY_TRANSPORT STREQUAL "curl") + find_dependency(CURL COMPONENTS AsynchDNS) + endif() + if(SENTRY_LINK_PTHREAD) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_dependency(Threads) + endif() endif() -if(SENTRY_BACKEND STREQUAL "breakpad" AND NOT SENTRY_BUILD_SHARED_LIBS) - set(SENTRY_BREAKPAD_SYSTEM @SENTRY_BREAKPAD_SYSTEM@) - if(SENTRY_BREAKPAD_SYSTEM) - find_dependency(PkgConfig REQUIRED) - pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client) - endif() -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/sentry-targets.cmake") - -if(SENTRY_TRANSPORT STREQUAL "curl" AND (NOT @BUILD_SHARED_LIBS@ OR NOT SENTRY_BUILD_SHARED_LIBS)) - find_dependency(CURL REQUIRED) - set_property(TARGET sentry::sentry APPEND - PROPERTY INTERFACE_LINK_LIBRARIES ${CURL_LIBRARIES}) -endif() - -if(SENTRY_LINK_PTHREAD AND NOT SENTRY_BUILD_SHARED_LIBS) - find_dependency(Threads REQUIRED) -endif() +include("${CMAKE_CURRENT_LIST_DIR}/sentry_crashpad-targets.cmake" OPTIONAL) +include("${CMAKE_CURRENT_LIST_DIR}/sentry-targets.cmake") \ No newline at end of file diff --git a/shared/sentry/src/src/backends/sentry_backend_breakpad.cpp b/shared/sentry/src/src/backends/sentry_backend_breakpad.cpp index 68718eaaf..afbf910a2 100644 --- a/shared/sentry/src/src/backends/sentry_backend_breakpad.cpp +++ b/shared/sentry/src/src/backends/sentry_backend_breakpad.cpp @@ -179,9 +179,8 @@ sentry__breakpad_backend_callback( static bool IsDebuggerActive() { - int junk; int mib[4]; - struct kinfo_proc info; + kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre @@ -197,7 +196,8 @@ IsDebuggerActive() // Call sysctl. size = sizeof(info); - junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + [[maybe_unused]] const int junk + = sysctl(mib, std::size(mib), &info, &size, nullptr, 0); assert(junk == 0); // We're being debugged if the P_TRACED flag is set. diff --git a/shared/sentry/src/src/backends/sentry_backend_crashpad.cpp b/shared/sentry/src/src/backends/sentry_backend_crashpad.cpp index 19df8bf81..6941f95c0 100644 --- a/shared/sentry/src/src/backends/sentry_backend_crashpad.cpp +++ b/shared/sentry/src/src/backends/sentry_backend_crashpad.cpp @@ -85,6 +85,10 @@ constexpr int g_CrashSignals[] = { }; #endif +static_assert(std::atomic::is_always_lock_free, + "The crashpad-backend requires lock-free atomic to safely handle " + "crashes"); + typedef struct { crashpad::CrashReportDatabase *db; crashpad::CrashpadClient *client; @@ -92,7 +96,8 @@ typedef struct { sentry_path_t *breadcrumb1_path; sentry_path_t *breadcrumb2_path; size_t num_breadcrumbs; - sentry_value_t crash_event; + std::atomic crashed; + std::atomic scope_flush; } crashpad_state_t; /** @@ -161,41 +166,23 @@ crashpad_register_wer_module( #endif static void -crashpad_backend_flush_scope( - sentry_backend_t *backend, const sentry_options_t *options) +crashpad_backend_flush_scope_to_event(const sentry_path_t *event_path, + const sentry_options_t *options, sentry_value_t crash_event) { - auto *data = static_cast(backend->data); - if (!data->event_path) { - return; - } - - // This here is an empty object that we copy the scope into. - // Even though the API is specific to `event`, an `event` has a few default - // properties that we do not want here. But in case of a crash we use the - // crash-event filled in the crash-handler and on_crash/before_send - // respectively. - sentry_value_t event = data->crash_event; - if (sentry_value_is_null(event)) { - event = sentry_value_new_object(); - // FIXME: This should be handled in the FirstChanceHandler but that does - // not exist for macOS just yet. - sentry_value_set_by_key( - event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL)); - } - SENTRY_WITH_SCOPE (scope) { // we want the scope without any modules or breadcrumbs - sentry__scope_apply_to_event(scope, options, event, SENTRY_SCOPE_NONE); + sentry__scope_apply_to_event( + scope, options, crash_event, SENTRY_SCOPE_NONE); } size_t mpack_size; - char *mpack = sentry_value_to_msgpack(event, &mpack_size); - sentry_value_decref(event); + char *mpack = sentry_value_to_msgpack(crash_event, &mpack_size); + sentry_value_decref(crash_event); if (!mpack) { return; } - int rv = sentry__path_write_buffer(data->event_path, mpack, mpack_size); + int rv = sentry__path_write_buffer(event_path, mpack, mpack_size); sentry_free(mpack); if (rv != 0) { @@ -203,7 +190,63 @@ crashpad_backend_flush_scope( } } +// This function is necessary for macOS since it has no `FirstChanceHandler`. +// but it is also necessary on Windows if the WER handler is enabled. +// This means we have to continuously flush the scope on +// every change so that `__sentry_event` is ready to upload when the crash +// happens. With platforms that have a `FirstChanceHandler` we can do that +// once in the handler. No need to share event- or crashpad-state mutation. +static void +crashpad_backend_flush_scope( + sentry_backend_t *backend, const sentry_options_t *options) +{ +#if defined(SENTRY_PLATFORM_LINUX) + (void)backend; + (void)options; +#else + auto *data = static_cast(backend->data); + bool expected = false; + + // + if (!data->event_path || data->crashed.load(std::memory_order_relaxed) + || !data->scope_flush.compare_exchange_strong( + expected, true, std::memory_order_acquire)) { + return; + } + + sentry_value_t event = sentry_value_new_object(); + // Since this will only be uploaded in case of a crash we must make this + // event fatal. + sentry_value_set_by_key( + event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL)); + + crashpad_backend_flush_scope_to_event(data->event_path, options, event); + data->scope_flush.store(false, std::memory_order_release); +#endif +} + #if defined(SENTRY_PLATFORM_LINUX) || defined(SENTRY_PLATFORM_WINDOWS) +static void +flush_scope_from_handler( + const sentry_options_t *options, sentry_value_t crash_event) +{ + auto state = static_cast(options->backend->data); + + // this blocks any further calls to `crashpad_backend_flush_scope` + state->crashed.store(true, std::memory_order_relaxed); + + // busy-wait until any in-progress scope flushes are finished + bool expected = false; + while (!state->scope_flush.compare_exchange_strong( + expected, true, std::memory_order_acquire)) { + expected = false; + } + + // now we are the sole flusher and can flush into the crash event + crashpad_backend_flush_scope_to_event( + state->event_path, options, crash_event); +} + # ifdef SENTRY_PLATFORM_WINDOWS static bool sentry__crashpad_handler(EXCEPTION_POINTERS *ExceptionInfo) @@ -213,18 +256,15 @@ static bool sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) { sentry__page_allocator_enable(); - sentry__enter_signal_handler(); # endif SENTRY_DEBUG("flushing session and queue before crashpad handler"); bool should_dump = true; SENTRY_WITH_OPTIONS (options) { - auto *data = static_cast(options->backend->data); - sentry_value_decref(data->crash_event); - data->crash_event = sentry_value_new_event(); - sentry_value_set_by_key(data->crash_event, "level", - sentry__value_new_level(SENTRY_LEVEL_FATAL)); + sentry_value_t crash_event = sentry_value_new_event(); + sentry_value_set_by_key( + crash_event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL)); if (options->on_crash_func) { sentry_ucontext_t uctx; @@ -237,18 +277,17 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) # endif SENTRY_TRACE("invoking `on_crash` hook"); - data->crash_event = options->on_crash_func( - &uctx, data->crash_event, options->on_crash_data); + crash_event = options->on_crash_func( + &uctx, crash_event, options->on_crash_data); } else if (options->before_send_func) { SENTRY_TRACE("invoking `before_send` hook"); - data->crash_event = options->before_send_func( - data->crash_event, nullptr, options->before_send_data); + crash_event = options->before_send_func( + crash_event, nullptr, options->before_send_data); } - should_dump = !sentry_value_is_null(data->crash_event); + should_dump = !sentry_value_is_null(crash_event); if (should_dump) { - crashpad_backend_flush_scope(options->backend, options); - + flush_scope_from_handler(options, crash_event); sentry__write_crash_marker(options); sentry__record_errors_on_current_session(1); @@ -272,10 +311,6 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) } SENTRY_DEBUG("handing control over to crashpad"); -# ifndef SENTRY_PLATFORM_WINDOWS - sentry__leave_signal_handler(); -# endif - // If we __don't__ want a minidump produced by crashpad we need to either // exit or longjmp at this point. The crashpad client handler which calls // back here (SetFirstChanceExceptionHandler) does the same if the @@ -527,7 +562,6 @@ crashpad_backend_free(sentry_backend_t *backend) sentry__path_free(data->event_path); sentry__path_free(data->breadcrumb1_path); sentry__path_free(data->breadcrumb2_path); - sentry_value_decref(data->crash_event); sentry_free(data); } @@ -609,7 +643,8 @@ sentry__backend_new(void) return nullptr; } memset(data, 0, sizeof(crashpad_state_t)); - data->crash_event = sentry_value_new_null(); + data->scope_flush = false; + data->crashed = false; backend->startup_func = crashpad_backend_startup; backend->shutdown_func = crashpad_backend_shutdown; diff --git a/shared/sentry/src/src/path/sentry_path_unix.c b/shared/sentry/src/src/path/sentry_path_unix.c index 298913996..b12a9dbea 100644 --- a/shared/sentry/src/src/path/sentry_path_unix.c +++ b/shared/sentry/src/src/path/sentry_path_unix.c @@ -501,3 +501,67 @@ sentry__path_append_buffer( return write_buffer_with_flags( path, buf, buf_len, O_RDWR | O_CREAT | O_APPEND); } + +struct sentry_filewriter_s { + size_t byte_count; + int fd; +}; + +MUST_USE sentry_filewriter_t * +sentry__filewriter_new(const sentry_path_t *path) +{ + int fd = open(path->path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + if (fd < 0) { + return NULL; + } + + sentry_filewriter_t *result = SENTRY_MAKE(sentry_filewriter_t); + if (!result) { + close(fd); + return NULL; + } + + result->fd = fd; + result->byte_count = 0; + return result; +} + +size_t +sentry__filewriter_write( + sentry_filewriter_t *filewriter, const char *buf, size_t buf_len) +{ + if (!filewriter) { + return 0; + } + while (buf_len > 0) { + ssize_t n = write(filewriter->fd, buf, buf_len); + if (n < 0 && (errno == EAGAIN || errno == EINTR)) { + continue; + } else if (n <= 0) { + break; + } + filewriter->byte_count += n; + buf += n; + buf_len -= n; + } + + return buf_len; +} + +void +sentry__filewriter_free(sentry_filewriter_t *filewriter) +{ + if (!filewriter) { + return; + } + + close(filewriter->fd); + sentry_free(filewriter); +} + +size_t +sentry__filewriter_byte_count(sentry_filewriter_t *filewriter) +{ + return filewriter->byte_count; +} diff --git a/shared/sentry/src/src/path/sentry_path_windows.c b/shared/sentry/src/src/path/sentry_path_windows.c index b953af9a6..b55ebbdf3 100644 --- a/shared/sentry/src/src/path/sentry_path_windows.c +++ b/shared/sentry/src/src/path/sentry_path_windows.c @@ -273,6 +273,9 @@ bool sentry__path_filename_matches(const sentry_path_t *path, const char *filename) { sentry_path_t *fn = sentry__path_from_str(filename); + if (!fn) { + return false; + } bool matches = _wcsicmp(sentry__path_filename(path), fn->path) == 0; sentry__path_free(fn); return matches; @@ -282,6 +285,9 @@ bool sentry__path_ends_with(const sentry_path_t *path, const char *suffix) { sentry_path_t *s = sentry__path_from_str(suffix); + if (!s) { + return false; + } size_t pathlen = wcslen(path->path); size_t suffixlen = wcslen(s->path); if (suffixlen > pathlen) { @@ -570,3 +576,66 @@ sentry__path_append_buffer( { return write_buffer_with_mode(path, buf, buf_len, L"ab"); } + +struct sentry_filewriter_s { + size_t byte_count; + FILE *f; +}; + +MUST_USE sentry_filewriter_t * +sentry__filewriter_new(const sentry_path_t *path) +{ + FILE *f = _wfopen(path->path, L"wb"); + if (!f) { + return NULL; + } + + sentry_filewriter_t *result = SENTRY_MAKE(sentry_filewriter_t); + if (!result) { + fclose(f); + return NULL; + } + + result->f = f; + result->byte_count = 0; + return result; +} + +size_t +sentry__filewriter_write( + sentry_filewriter_t *filewriter, const char *buf, size_t buf_len) +{ + if (!filewriter) { + return 0; + } + while (buf_len > 0) { + size_t n = fwrite(buf, 1, buf_len, filewriter->f); + if (n == 0 && errno == EINVAL) { + continue; + } else if (n < buf_len) { + break; + } + filewriter->byte_count += n; + buf += n; + buf_len -= n; + } + + return buf_len; +} + +void +sentry__filewriter_free(sentry_filewriter_t *filewriter) +{ + if (!filewriter) { + return; + } + fflush(filewriter->f); + fclose(filewriter->f); + sentry_free(filewriter); +} + +size_t +sentry__filewriter_byte_count(sentry_filewriter_t *filewriter) +{ + return filewriter->byte_count; +} diff --git a/shared/sentry/src/src/sentry_core.c b/shared/sentry/src/src/sentry_core.c index 55241aa75..8b35d0469 100644 --- a/shared/sentry/src/src/sentry_core.c +++ b/shared/sentry/src/src/sentry_core.c @@ -1002,10 +1002,9 @@ sentry_span_t * sentry_transaction_start_child(sentry_transaction_t *opaque_parent, const char *operation, const char *description) { - const size_t operation_len = operation ? strlen(operation) : 0; - const size_t description_len = description ? strlen(description) : 0; - return sentry_transaction_start_child_n( - opaque_parent, operation, operation_len, description, description_len); + return sentry_transaction_start_child_n(opaque_parent, operation, + sentry__guarded_strlen(operation), description, + sentry__guarded_strlen(description)); } sentry_span_t * @@ -1040,10 +1039,9 @@ sentry_span_t * sentry_span_start_child(sentry_span_t *opaque_parent, const char *operation, const char *description) { - size_t operation_len = operation ? strlen(operation) : 0; - size_t description_len = description ? strlen(description) : 0; - return sentry_span_start_child_n( - opaque_parent, operation, operation_len, description, description_len); + return sentry_span_start_child_n(opaque_parent, operation, + sentry__guarded_strlen(operation), description, + sentry__guarded_strlen(description)); } void diff --git a/shared/sentry/src/src/sentry_database.c b/shared/sentry/src/src/sentry_database.c index 6ebe89008..c9c19bacf 100644 --- a/shared/sentry/src/src/sentry_database.c +++ b/shared/sentry/src/src/sentry_database.c @@ -116,7 +116,7 @@ bool sentry__run_write_session( const sentry_run_t *run, const sentry_session_t *session) { - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); if (!jw) { return false; } diff --git a/shared/sentry/src/src/sentry_envelope.c b/shared/sentry/src/src/sentry_envelope.c index 615197654..e173e9494 100644 --- a/shared/sentry/src/src/sentry_envelope.c +++ b/shared/sentry/src/src/sentry_envelope.c @@ -234,7 +234,7 @@ sentry__envelope_add_event(sentry_envelope_t *envelope, sentry_value_t event) return NULL; } - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); if (!jw) { return NULL; } @@ -265,7 +265,7 @@ sentry__envelope_add_transaction( return NULL; } - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); if (!jw) { return NULL; } @@ -304,7 +304,7 @@ sentry__envelope_add_user_feedback( return NULL; } - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); if (!jw) { return NULL; } @@ -332,7 +332,7 @@ sentry__envelope_add_session( if (!envelope || !session) { return NULL; } - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); if (!jw) { return NULL; } @@ -378,7 +378,7 @@ static void sentry__envelope_serialize_headers_into_stringbuilder( const sentry_envelope_t *envelope, sentry_stringbuilder_t *sb) { - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(sb); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(sb); if (jw) { sentry__jsonwriter_write_value(jw, envelope->contents.items.headers); sentry__jsonwriter_free(jw); @@ -389,7 +389,7 @@ static void sentry__envelope_serialize_item_into_stringbuilder( const sentry_envelope_item_t *item, sentry_stringbuilder_t *sb) { - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(sb); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(sb); if (!jw) { return; } @@ -476,16 +476,42 @@ MUST_USE int sentry_envelope_write_to_path( const sentry_envelope_t *envelope, const sentry_path_t *path) { - // TODO: This currently builds the whole buffer in-memory. - // It would be nice to actually stream this to a file. - size_t buf_len = 0; - char *buf = sentry_envelope_serialize(envelope, &buf_len); + sentry_filewriter_t *fw = sentry__filewriter_new(path); + if (!fw) { + return 1; + } + + if (envelope->is_raw) { + return envelope->contents.raw.payload_len + != sentry__filewriter_write(fw, envelope->contents.raw.payload, + envelope->contents.raw.payload_len); + } - int rv = sentry__path_write_buffer(path, buf, buf_len); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_fw(fw); + if (jw) { + sentry__jsonwriter_write_value(jw, envelope->contents.items.headers); + sentry__jsonwriter_reset(jw); - sentry_free(buf); + for (size_t i = 0; i < envelope->contents.items.item_count; i++) { + const sentry_envelope_item_t *item + = &envelope->contents.items.items[i]; + const char newline = '\n'; + sentry__filewriter_write(fw, &newline, sizeof(char)); - return rv; + sentry__jsonwriter_write_value(jw, item->headers); + sentry__jsonwriter_reset(jw); + + sentry__filewriter_write(fw, &newline, sizeof(char)); + + sentry__filewriter_write(fw, item->payload, item->payload_len); + } + sentry__jsonwriter_free(jw); + } + + size_t rv = sentry__filewriter_byte_count(fw); + sentry__filewriter_free(fw); + + return rv == 0; } int diff --git a/shared/sentry/src/src/sentry_json.c b/shared/sentry/src/src/sentry_json.c index 904ee4825..1ee9d84fb 100644 --- a/shared/sentry/src/src/sentry_json.c +++ b/shared/sentry/src/src/sentry_json.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,16 +12,117 @@ #include "sentry_utils.h" #include "sentry_value.h" +typedef struct { + void (*free)(sentry_jsonwriter_t *writer); + void (*write_str)(sentry_jsonwriter_t *writer, const char *str); + void (*write_buf)(sentry_jsonwriter_t *writer, const char *buf, size_t len); + void (*write_char)(sentry_jsonwriter_t *writer, char c); + char *(*into_string)(sentry_jsonwriter_t *jw, size_t *len_out); +} sentry_jsonwriter_ops_t; + struct sentry_jsonwriter_s { - sentry_stringbuilder_t *sb; + union { + sentry_stringbuilder_t *sb; + sentry_filewriter_t *fw; + } output; uint64_t want_comma; uint32_t depth; bool last_was_key; bool owns_sb; + sentry_jsonwriter_ops_t *ops; +}; + +static void +jsonwriter_free_sb(sentry_jsonwriter_t *jw) +{ + if (!jw) { + return; + } + if (jw->owns_sb) { + sentry__stringbuilder_cleanup(jw->output.sb); + sentry_free(jw->output.sb); + } + sentry_free(jw); +} + +static void +jsonwriter_free_file(sentry_jsonwriter_t *jw) +{ + if (!jw) { + return; + } + sentry_free(jw); +} + +static void +write_char_sb(sentry_jsonwriter_t *jw, char c) +{ + sentry__stringbuilder_append_char(jw->output.sb, c); +} + +static void +write_str_sb(sentry_jsonwriter_t *jw, const char *str) +{ + sentry__stringbuilder_append(jw->output.sb, str); +} + +static void +write_buf_sb(sentry_jsonwriter_t *jw, const char *buf, size_t len) +{ + sentry__stringbuilder_append_buf(jw->output.sb, buf, len); +} + +static void +write_char_file(sentry_jsonwriter_t *jw, char c) +{ + sentry__filewriter_write(jw->output.fw, &c, sizeof(char)); +} + +static void +write_str_file(sentry_jsonwriter_t *jw, const char *str) +{ + sentry__filewriter_write(jw->output.fw, str, sizeof(char) * strlen(str)); +} + +static void +write_buf_file(sentry_jsonwriter_t *jw, const char *buf, size_t len) +{ + sentry__filewriter_write(jw->output.fw, buf, len); +} + +static char * +into_string_sb(sentry_jsonwriter_t *jw, size_t *len_out) +{ + char *rv = NULL; + sentry_stringbuilder_t *sb = jw->output.sb; + if (len_out) { + *len_out = sb->len; + } + rv = sentry__stringbuilder_into_string(sb); + sentry__jsonwriter_free(jw); + return rv; +} + +static char * +into_string_file(sentry_jsonwriter_t *jw, size_t *len_out) +{ + (void)jw; + assert(!"A file-based jsonwriter can't convert into string"); + + *len_out = 0; + return NULL; +} + +sentry_jsonwriter_ops_t sb_ops = { + .write_char = write_char_sb, + .write_str = write_str_sb, + .write_buf = write_buf_sb, + .free = jsonwriter_free_sb, + .into_string = into_string_sb, }; sentry_jsonwriter_t * -sentry__jsonwriter_new(sentry_stringbuilder_t *sb) +sentry__jsonwriter_new_sb(sentry_stringbuilder_t *sb) { bool owns_sb = false; if (!sb) { @@ -36,38 +138,59 @@ sentry__jsonwriter_new(sentry_stringbuilder_t *sb) return NULL; } - rv->sb = sb; + rv->output.sb = sb; + rv->want_comma = 0; + rv->depth = 0; + rv->last_was_key = 0; + rv->owns_sb = owns_sb; + rv->ops = &sb_ops; + return rv; +} + +sentry_jsonwriter_ops_t file_ops = { + .free = jsonwriter_free_file, + .write_char = write_char_file, + .write_str = write_str_file, + .write_buf = write_buf_file, + .into_string = into_string_file, +}; + +sentry_jsonwriter_t * +sentry__jsonwriter_new_fw(sentry_filewriter_t *fw) +{ + bool owns_sb = false; + sentry_jsonwriter_t *rv = SENTRY_MAKE(sentry_jsonwriter_t); + if (!rv) { + return NULL; + } + + rv->output.fw = fw; rv->want_comma = 0; rv->depth = 0; rv->last_was_key = 0; rv->owns_sb = owns_sb; + rv->ops = &file_ops; return rv; } void sentry__jsonwriter_free(sentry_jsonwriter_t *jw) { - if (!jw) { - return; - } - if (jw->owns_sb) { - sentry__stringbuilder_cleanup(jw->sb); - sentry_free(jw->sb); - } - sentry_free(jw); + jw->ops->free(jw); +} + +void +sentry__jsonwriter_reset(sentry_jsonwriter_t *jw) +{ + jw->want_comma = 0; + jw->depth = 0; + jw->last_was_key = 0; } char * sentry__jsonwriter_into_string(sentry_jsonwriter_t *jw, size_t *len_out) { - char *rv = NULL; - sentry_stringbuilder_t *sb = jw->sb; - if (len_out) { - *len_out = sb->len; - } - rv = sentry__stringbuilder_into_string(sb); - sentry__jsonwriter_free(jw); - return rv; + return jw->ops->into_string(jw, len_out); } static bool @@ -92,13 +215,13 @@ set_comma(sentry_jsonwriter_t *jw, bool val) static void write_char(sentry_jsonwriter_t *jw, char c) { - sentry__stringbuilder_append_char(jw->sb, c); + jw->ops->write_char(jw, c); } static void write_str(sentry_jsonwriter_t *jw, const char *str) { - sentry__stringbuilder_append(jw->sb, str); + jw->ops->write_str(jw, str); } // The Lookup table and algorithm below are adapted from: @@ -141,7 +264,7 @@ write_json_str(sentry_jsonwriter_t *jw, const char *str) size_t len = ptr - start; if (len) { - sentry__stringbuilder_append_buf(jw->sb, (const char *)start, len); + jw->ops->write_buf(jw, (const char *)start, len); } switch (*ptr) { @@ -184,7 +307,7 @@ write_json_str(sentry_jsonwriter_t *jw, const char *str) size_t len = ptr - start; if (len) { - sentry__stringbuilder_append_buf(jw->sb, (const char *)start, len); + jw->ops->write_buf(jw, (const char *)start, len); } write_char(jw, '"'); diff --git a/shared/sentry/src/src/sentry_json.h b/shared/sentry/src/src/sentry_json.h index 8379bf021..3a61c4d4c 100644 --- a/shared/sentry/src/src/sentry_json.h +++ b/shared/sentry/src/src/sentry_json.h @@ -2,6 +2,7 @@ #define SENTRY_JSON_H_INCLUDED #include "sentry_boot.h" +#include "sentry_path.h" typedef struct sentry_stringbuilder_s sentry_stringbuilder_t; typedef struct sentry_jsonwriter_s sentry_jsonwriter_t; @@ -12,13 +13,25 @@ typedef struct sentry_jsonwriter_s sentry_jsonwriter_t; * It will use an existing `sentry_stringbuilder_t` as its output if one is * provided, otherwise it will allocate a new one. */ -sentry_jsonwriter_t *sentry__jsonwriter_new(sentry_stringbuilder_t *sb); +sentry_jsonwriter_t *sentry__jsonwriter_new_sb(sentry_stringbuilder_t *sb); + +/** + * This creates a new JSON writer. + * + * It requires an existing `sentry_filewriter_t` as its output. + */ +sentry_jsonwriter_t *sentry__jsonwriter_new_fw(sentry_filewriter_t *fw); /** * Deallocates a JSON writer. */ void sentry__jsonwriter_free(sentry_jsonwriter_t *jw); +/** + * Resets the internal state of a JSON writer. + */ +void sentry__jsonwriter_reset(sentry_jsonwriter_t *jw); + /** * This will consume and deallocate the JSON writer, returning the generated * JSON string, and writing its length into `len_out`. diff --git a/shared/sentry/src/src/sentry_logger.c b/shared/sentry/src/src/sentry_logger.c index 6a0f4703e..0dc8f05d5 100644 --- a/shared/sentry/src/src/sentry_logger.c +++ b/shared/sentry/src/src/sentry_logger.c @@ -1,6 +1,7 @@ #include "sentry_logger.h" #include "sentry_core.h" #include "sentry_options.h" +#include "sentry_string.h" #include #include @@ -52,7 +53,8 @@ sentry__logger_defaultlogger( const char *prefix = "[sentry] "; const char *priority = sentry__logger_describe(level); - size_t len = strlen(prefix) + strlen(priority) + strlen(message) + 2; + size_t len = strlen(prefix) + strlen(priority) + + sentry__guarded_strlen(message) + 2; char *format = sentry_malloc(len); snprintf(format, len, "%s%s%s\n", prefix, priority, message); diff --git a/shared/sentry/src/src/sentry_path.h b/shared/sentry/src/src/sentry_path.h index 02e42d5fe..9cd72dc32 100644 --- a/shared/sentry/src/src/sentry_path.h +++ b/shared/sentry/src/src/sentry_path.h @@ -23,9 +23,12 @@ struct sentry_filelock_s { bool is_locked; }; +struct sentry_filewriter_s; + typedef struct sentry_path_s sentry_path_t; typedef struct sentry_pathiter_s sentry_pathiter_t; typedef struct sentry_filelock_s sentry_filelock_t; +typedef struct sentry_filewriter_s sentry_filewriter_t; /** * NOTE on encodings: @@ -200,6 +203,28 @@ void sentry__filelock_unlock(sentry_filelock_t *lock); */ void sentry__filelock_free(sentry_filelock_t *lock); +/** + * Create a new file-writer, which is a stateful abstraction over the + * OS-specific file-handle and a byte counter. + */ +sentry_filewriter_t *sentry__filewriter_new(const sentry_path_t *path); + +/** + * Writes a buffer to the file behind the handle stored in the filewriter. + */ +size_t sentry__filewriter_write( + sentry_filewriter_t *filewriter, const char *buf, size_t buf_len); + +/** + * Retrieves the count of written bytes. + */ +size_t sentry__filewriter_byte_count(sentry_filewriter_t *filewriter); + +/** + * Frees the filewriter and closes the handle. + */ +void sentry__filewriter_free(sentry_filewriter_t *filewriter); + /* windows specific API additions */ #ifdef SENTRY_PLATFORM_WINDOWS /** diff --git a/shared/sentry/src/src/sentry_slice.c b/shared/sentry/src/src/sentry_slice.c index 0f68311b2..43880fb8b 100644 --- a/shared/sentry/src/src/sentry_slice.c +++ b/shared/sentry/src/src/sentry_slice.c @@ -8,7 +8,7 @@ sentry__slice_from_str(const char *str) { sentry_slice_t rv; rv.ptr = str; - rv.len = str ? strlen(str) : 0; + rv.len = sentry__guarded_strlen(str); return rv; } diff --git a/shared/sentry/src/src/sentry_string.h b/shared/sentry/src/src/sentry_string.h index 95e1e4f61..ee933d4da 100644 --- a/shared/sentry/src/src/sentry_string.h +++ b/shared/sentry/src/src/sentry_string.h @@ -173,6 +173,15 @@ sentry__string_eq(const char *a, const char *b) return strcmp(a, b) == 0; } +/** + * Guards strlen() against NULL pointers + */ +static inline size_t +sentry__guarded_strlen(const char *s) +{ + return s ? strlen(s) : 0; +} + /** * Converts an int64_t into a string. */ diff --git a/shared/sentry/src/src/sentry_tracing.c b/shared/sentry/src/src/sentry_tracing.c index 5f3ae7c69..4d6195a0d 100644 --- a/shared/sentry/src/src/sentry_tracing.c +++ b/shared/sentry/src/src/sentry_tracing.c @@ -75,11 +75,8 @@ sentry_transaction_context_new_n(const char *name, size_t name_len, sentry_transaction_context_t * sentry_transaction_context_new(const char *name, const char *operation) { - size_t name_len = name ? strlen(name) : 0; - size_t operation_len = operation ? strlen(operation) : 0; - - return sentry_transaction_context_new_n( - name, name_len, operation, operation_len); + return sentry_transaction_context_new_n(name, sentry__guarded_strlen(name), + operation, sentry__guarded_strlen(operation)); } void @@ -212,11 +209,8 @@ void sentry_transaction_context_update_from_header( sentry_transaction_context_t *tx_cxt, const char *key, const char *value) { - size_t key_len = key ? strlen(key) : 0; - size_t value_len = value ? strlen(value) : 0; - - sentry_transaction_context_update_from_header_n( - tx_cxt, key, key_len, value, value_len); + sentry_transaction_context_update_from_header_n(tx_cxt, key, + sentry__guarded_strlen(key), value, sentry__guarded_strlen(value)); } sentry_transaction_t * @@ -338,11 +332,8 @@ sentry_value_t sentry__value_span_new(size_t max_spans, sentry_value_t parent, const char *operation, const char *description) { - const size_t operation_len = operation ? strlen(operation) : 0; - const size_t description_len = description ? strlen(description) : 0; return sentry__value_span_new_n(max_spans, parent, - (sentry_slice_t) { operation, operation_len }, - (sentry_slice_t) { description, description_len }); + sentry__slice_from_str(operation), sentry__slice_from_str(description)); } sentry_value_t @@ -417,10 +408,7 @@ set_tag_n(sentry_value_t item, sentry_slice_t tag, sentry_slice_t value) static void set_tag(sentry_value_t item, const char *tag, const char *value) { - const size_t tag_len = tag ? strlen(tag) : 0; - const size_t value_len = value ? strlen(value) : 0; - set_tag_n(item, (sentry_slice_t) { tag, tag_len }, - (sentry_slice_t) { value, value_len }); + set_tag_n(item, sentry__slice_from_str(tag), sentry__slice_from_str(value)); } void diff --git a/shared/sentry/src/src/sentry_utils.c b/shared/sentry/src/src/sentry_utils.c index f2980b728..62c36758f 100644 --- a/shared/sentry/src/src/sentry_utils.c +++ b/shared/sentry/src/src/sentry_utils.c @@ -418,7 +418,7 @@ sentry__usec_time_to_iso8601(uint64_t time) uint64_t sentry__iso8601_to_usec(const char *iso) { - size_t len = strlen(iso); + size_t len = sentry__guarded_strlen(iso); if (len != 20 && len != 27) { return 0; } diff --git a/shared/sentry/src/src/sentry_value.c b/shared/sentry/src/src/sentry_value.c index e2315914b..682325f71 100644 --- a/shared/sentry/src/src/sentry_value.c +++ b/shared/sentry/src/src/sentry_value.c @@ -742,8 +742,7 @@ sentry_value_get_by_key_n(sentry_value_t value, const char *k, size_t k_len) sentry_value_t sentry_value_get_by_key(sentry_value_t value, const char *k) { - const size_t k_len = k ? strlen(k) : 0; - return sentry_value_get_by_key_n(value, k, k_len); + return sentry_value_get_by_key_n(value, k, sentry__guarded_strlen(k)); } sentry_value_t @@ -945,7 +944,7 @@ sentry__jsonwriter_write_value(sentry_jsonwriter_t *jw, sentry_value_t value) char * sentry_value_to_json(sentry_value_t value) { - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); if (!jw) { return NULL; } @@ -1151,10 +1150,8 @@ sentry_value_t sentry_value_new_message_event( sentry_level_t level, const char *logger, const char *text) { - size_t logger_len = logger ? strlen(logger) : 0; - size_t text_len = text ? strlen(text) : 0; - return sentry_value_new_message_event_n( - level, logger, logger_len, text, text_len); + return sentry_value_new_message_event_n(level, logger, + sentry__guarded_strlen(logger), text, sentry__guarded_strlen(text)); } static void @@ -1186,9 +1183,8 @@ sentry_value_new_breadcrumb_n( sentry_value_t sentry_value_new_breadcrumb(const char *type, const char *message) { - const size_t type_len = type ? strlen(type) : 0; - const size_t message_len = message ? strlen(message) : 0; - return sentry_value_new_breadcrumb_n(type, type_len, message, message_len); + return sentry_value_new_breadcrumb_n(type, sentry__guarded_strlen(type), + message, sentry__guarded_strlen(message)); } sentry_value_t @@ -1206,9 +1202,8 @@ sentry_value_new_exception_n( sentry_value_t sentry_value_new_exception(const char *type, const char *value) { - const size_t type_len = type ? strlen(type) : 0; - const size_t value_len = value ? strlen(value) : 0; - return sentry_value_new_exception_n(type, type_len, value, value_len); + return sentry_value_new_exception_n(type, sentry__guarded_strlen(type), + value, sentry__guarded_strlen(value)); } sentry_value_t @@ -1236,8 +1231,7 @@ sentry_value_new_thread_n(uint64_t id, const char *name, size_t name_len) sentry_value_t sentry_value_new_thread(uint64_t id, const char *name) { - const size_t name_len = name ? strlen(name) : 0; - return sentry_value_new_thread_n(id, name, name_len); + return sentry_value_new_thread_n(id, name, sentry__guarded_strlen(name)); } sentry_value_t @@ -1269,11 +1263,9 @@ sentry_value_t sentry_value_new_user_feedback(const sentry_uuid_t *uuid, const char *name, const char *email, const char *comments) { - size_t name_len = name ? strlen(name) : 0; - size_t email_len = email ? strlen(email) : 0; - size_t comments_len = email ? strlen(comments) : 0; - return sentry_value_new_user_feedback_n( - uuid, name, name_len, email, email_len, comments, comments_len); + return sentry_value_new_user_feedback_n(uuid, name, + sentry__guarded_strlen(name), email, sentry__guarded_strlen(email), + comments, sentry__guarded_strlen(comments)); } sentry_value_t diff --git a/shared/sentry/src/tests/assertions.py b/shared/sentry/src/tests/assertions.py index 8ab086124..a415965c5 100644 --- a/shared/sentry/src/tests/assertions.py +++ b/shared/sentry/src/tests/assertions.py @@ -59,7 +59,19 @@ def assert_meta( sdk_override=None, ): event = envelope.get_event() + assert_event_meta( + event, release, integration, transaction, transaction_data, sdk_override + ) + +def assert_event_meta( + event, + release="test-example-release", + integration=None, + transaction="test-transaction", + transaction_data=None, + sdk_override=None, +): extra = { "extra stuff": "some value", "…unicode key…": "őá…–🤮🚀¿ 한글 테스트", @@ -78,9 +90,9 @@ def assert_meta( } expected_sdk = { "name": "sentry.native", - "version": "0.7.6", + "version": "0.7.9", "packages": [ - {"name": "github:getsentry/sentry-native", "version": "0.7.6"}, + {"name": "github:getsentry/sentry-native", "version": "0.7.9"}, ], } if is_android: @@ -316,8 +328,7 @@ def assert_crashpad_upload(req): attachments = _load_crashpad_attachments(msg) assert_overflowing_breadcrumb(attachments) - assert attachments.event["level"] == "fatal" - + assert_event_meta(attachments.event, integration="crashpad") assert any( b'name="upload_file_minidump"' in part.as_bytes() and b"\n\nMDMP" in part.as_bytes() diff --git a/shared/sentry/src/tests/cmake.py b/shared/sentry/src/tests/cmake.py index 906b8afad..6466d7910 100644 --- a/shared/sentry/src/tests/cmake.py +++ b/shared/sentry/src/tests/cmake.py @@ -153,6 +153,20 @@ def cmake(cwd, targets, options=None): if "llvm-cov" in os.environ.get("RUN_ANALYZER", ""): flags = "-fprofile-instr-generate -fcoverage-mapping" configcmd.append("-DCMAKE_C_FLAGS='{}'".format(flags)) + + # Since we overwrite `CXXFLAGS` below, we must add the experimental library here for the GHA runner that builds + # sentry-native with LLVM clang for macOS (to run ASAN on macOS) rather than the version coming with XCode. + # TODO: remove this if the GHA runner image for macOS ever updates beyond llvm15. + if ( + sys.platform == "darwin" + and os.environ.get("CC", "") == "clang" + and shutil.which("clang") == "/usr/local/opt/llvm@15/bin/clang" + ): + flags = ( + flags + + " -L/usr/local/opt/llvm@15/lib/c++ -fexperimental-library -Wno-unused-command-line-argument" + ) + configcmd.append("-DCMAKE_CXX_FLAGS='{}'".format(flags)) if "CMAKE_DEFINES" in os.environ: configcmd.extend(os.environ.get("CMAKE_DEFINES").split()) diff --git a/shared/sentry/src/tests/leaks.txt b/shared/sentry/src/tests/leaks.txt index 4ca4e4b68..4772a4261 100644 --- a/shared/sentry/src/tests/leaks.txt +++ b/shared/sentry/src/tests/leaks.txt @@ -3,4 +3,11 @@ # Adding a manual `[paths release]` "fixes" the `crashpad_handler` leak, but leads to a use-after-free in sentry. # https://github.com/getsentry/crashpad/blob/9cd1a4dadb51b31665f5e50c5ffc25bb9d10571a/client/crash_report_database_mac.mm#L705 leak:contentsOfDirectoryAtPath + +# This is a known issue introduced with libcurl 7.77.0 on macOS: https://github.com/getsentry/sentry-native/pull/827#issuecomment-1521956265 +# While it should have been fixed with 7.78.0, tests with SDK-packaged version 7.85.0 still detect the same leak. leak:SCDynamicStoreCopyProxiesWithOptions + +# This is a known issue in ASAN packaged with llvm15/16 (and below): https://github.com/google/sanitizers/issues/1501 +# I cannot reproduce it with the current brew llvm package (18.1.7). TODO: remove when GHA macOS runner image updates the llvm package. +leak:realizeClassWithoutSwift diff --git a/shared/sentry/src/tests/test_integration_crashpad.py b/shared/sentry/src/tests/test_integration_crashpad.py index ccbbfdea5..0340cfbb2 100644 --- a/shared/sentry/src/tests/test_integration_crashpad.py +++ b/shared/sentry/src/tests/test_integration_crashpad.py @@ -118,6 +118,9 @@ def test_crashpad_wer_crash(cmake, httpserver, run_args): assert_session(envelope, {"status": "crashed", "errors": 1}) assert_crashpad_upload(multipart) + # Windows throttles WER crash reporting frequency, so let's wait a bit + time.sleep(1) + @pytest.mark.parametrize( "run_args,build_args", diff --git a/shared/sentry/src/tests/test_integration_http.py b/shared/sentry/src/tests/test_integration_http.py index 179566e5a..6467099f2 100644 --- a/shared/sentry/src/tests/test_integration_http.py +++ b/shared/sentry/src/tests/test_integration_http.py @@ -27,7 +27,7 @@ pytestmark = pytest.mark.skipif(not has_http, reason="tests need http") auth_header = ( - "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.7.6" + "Sentry sentry_key=uiaeosnrtdy, sentry_version=7, sentry_client=sentry.native/0.7.9" ) diff --git a/shared/sentry/src/tests/unit/fuzz.c b/shared/sentry/src/tests/unit/fuzz.c index b0de75e3e..186a1792a 100644 --- a/shared/sentry/src/tests/unit/fuzz.c +++ b/shared/sentry/src/tests/unit/fuzz.c @@ -52,7 +52,7 @@ main(int argc, char **argv) sentry_value_t value = sentry__value_from_json(buf, buf_len); sentry_free(buf); - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); sentry__jsonwriter_write_value(jw, value); size_t serialized1_len = 0; char *serialized1 = sentry__jsonwriter_into_string(jw, &serialized1_len); @@ -60,7 +60,7 @@ main(int argc, char **argv) value = sentry__value_from_json(serialized1, serialized1_len); - jw = sentry__jsonwriter_new(NULL); + jw = sentry__jsonwriter_new_sb(NULL); sentry__jsonwriter_write_value(jw, value); size_t serialized2_len = 0; char *serialized2 = sentry__jsonwriter_into_string(jw, &serialized2_len); diff --git a/shared/sentry/src/tests/unit/test_envelopes.c b/shared/sentry/src/tests/unit/test_envelopes.c index 803fb4730..8d0de1fc5 100644 --- a/shared/sentry/src/tests/unit/test_envelopes.c +++ b/shared/sentry/src/tests/unit/test_envelopes.c @@ -253,3 +253,19 @@ SENTRY_TEST(write_envelope_to_file_null) sentry_envelope_free(empty_envelope); } + +SENTRY_TEST(write_envelope_to_invalid_path) +{ + sentry_envelope_t *envelope = create_test_envelope(); + const char *test_file_str + = "./directory_that_does_not_exist/sentry_test_envelope"; + sentry_path_t *test_file_path = sentry__path_from_str(test_file_str); + + int rv = sentry_envelope_write_to_file(envelope, test_file_str); + TEST_CHECK_INT_EQUAL(rv, 1); + + sentry__path_remove(test_file_path); + sentry__path_free(test_file_path); + sentry_envelope_free(envelope); + sentry_close(); +} diff --git a/shared/sentry/src/tests/unit/test_fuzzfailures.c b/shared/sentry/src/tests/unit/test_fuzzfailures.c index 38b4b237d..46da41e82 100644 --- a/shared/sentry/src/tests/unit/test_fuzzfailures.c +++ b/shared/sentry/src/tests/unit/test_fuzzfailures.c @@ -19,7 +19,7 @@ parse_json_roundtrip(const sentry_path_t *path) sentry_value_t value = sentry__value_from_json(buf, buf_len); sentry_free(buf); - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); sentry__jsonwriter_write_value(jw, value); size_t serialized1_len = 0; char *serialized1 = sentry__jsonwriter_into_string(jw, &serialized1_len); @@ -27,7 +27,7 @@ parse_json_roundtrip(const sentry_path_t *path) value = sentry__value_from_json(serialized1, serialized1_len); - jw = sentry__jsonwriter_new(NULL); + jw = sentry__jsonwriter_new_sb(NULL); sentry__jsonwriter_write_value(jw, value); size_t serialized2_len = 0; char *serialized2 = sentry__jsonwriter_into_string(jw, &serialized2_len); diff --git a/shared/sentry/src/tests/unit/test_value.c b/shared/sentry/src/tests/unit/test_value.c index d1012602b..8b03565c1 100644 --- a/shared/sentry/src/tests/unit/test_value.c +++ b/shared/sentry/src/tests/unit/test_value.c @@ -394,7 +394,7 @@ SENTRY_TEST(value_json_deeply_nested) child = new_child; } - sentry_jsonwriter_t *jw = sentry__jsonwriter_new(NULL); + sentry_jsonwriter_t *jw = sentry__jsonwriter_new_sb(NULL); sentry__jsonwriter_write_value(jw, root); size_t serialized_len = 0; char *serialized = sentry__jsonwriter_into_string(jw, &serialized_len); @@ -793,3 +793,67 @@ SENTRY_TEST(user_feedback_is_valid) sentry_value_decref(user_feedback); } + +SENTRY_TEST(user_feedback_with_null_args) +{ + sentry_uuid_t event_id + = sentry_uuid_from_string("c993afb6-b4ac-48a6-b61b-2558e601d65d"); + sentry_value_t user_feedback + = sentry_value_new_user_feedback(&event_id, NULL, NULL, NULL); + + TEST_CHECK(!sentry_value_is_null(user_feedback)); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(user_feedback, "name"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(user_feedback, "email"))); + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key(user_feedback, "comments"))); + + sentry_value_decref(user_feedback); + + user_feedback = sentry_value_new_user_feedback( + &event_id, NULL, "some-email", "some-comment"); + + TEST_CHECK(!sentry_value_is_null(user_feedback)); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(user_feedback, "name"))); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), + "some-email"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "comments")), + "some-comment"); + + sentry_value_decref(user_feedback); + + user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", NULL, "some-comment"); + + TEST_CHECK(!sentry_value_is_null(user_feedback)); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), + "some-name"); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(user_feedback, "email"))); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + user_feedback, "comments")), + "some-comment"); + + sentry_value_decref(user_feedback); + + user_feedback = sentry_value_new_user_feedback( + &event_id, "some-name", "some-email", NULL); + + TEST_CHECK(!sentry_value_is_null(user_feedback)); + + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "name")), + "some-name"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(user_feedback, "email")), + "some-email"); + TEST_CHECK(sentry_value_is_null( + sentry_value_get_by_key(user_feedback, "comments"))); + + sentry_value_decref(user_feedback); +} diff --git a/shared/sentry/src/tests/unit/tests.inc b/shared/sentry/src/tests/unit/tests.inc index d7f9fae29..cc7920bc1 100644 --- a/shared/sentry/src/tests/unit/tests.inc +++ b/shared/sentry/src/tests/unit/tests.inc @@ -109,6 +109,7 @@ XX(url_parsing_complete) XX(url_parsing_invalid) XX(url_parsing_partial) XX(user_feedback_is_valid) +XX(user_feedback_with_null_args) XX(uuid_api) XX(uuid_v4) XX(value_bool) @@ -136,3 +137,4 @@ XX(value_string_n) XX(value_unicode) XX(value_wrong_type) XX(write_envelope_to_file_null) +XX(write_envelope_to_invalid_path) diff --git a/shared/sentry/src/vendor/mpack.c b/shared/sentry/src/vendor/mpack.c index 67e54e8c6..ca27d80f6 100644 --- a/shared/sentry/src/vendor/mpack.c +++ b/shared/sentry/src/vendor/mpack.c @@ -34,6 +34,8 @@ #include "mpack.h" +extern void* sentry_malloc(size_t); +extern void sentry_free(void*); /* mpack/mpack-platform.c.c */ diff --git a/shared/sentry/src/vendor/mpack.h b/shared/sentry/src/vendor/mpack.h index 2067a8af3..f72fb3c4b 100644 --- a/shared/sentry/src/vendor/mpack.h +++ b/shared/sentry/src/vendor/mpack.h @@ -201,9 +201,8 @@ * to grow buffers. */ #if defined(MPACK_STDLIB) && MPACK_STDLIB && !defined(MPACK_MALLOC) -#define MPACK_MALLOC malloc -#define MPACK_REALLOC realloc -#define MPACK_FREE free +#define MPACK_MALLOC sentry_malloc +#define MPACK_FREE sentry_free #endif /**