I found a bug in WebKit for iOS and wanted to setup a debug environment to find the exact relevant line. This was harder than I thought, so I decided to document the process.
Collect Initial Crash Dump
I noticed after running the crashing JavaScript code, I got prompted with a “This webpage was reloaded because a problem occurred.” error. I went and checked Settings > Privacy > Analytics > Analytics Data and found a com.apple.WebKit.WebContent entry with the following crash information:
... Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000 VM Region Info: 0 is not in any region. Bytes before following region: 4372119552 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL UNUSED SPACE AT START ---> __TEXT 0000000104994000-0000000104998000 [ 16K] r-x/r-x SM=COW ...it.WebContent Termination Signal: Segmentation fault: 11 Termination Reason: Namespace SIGNAL, Code 0xb Terminating Process: exc handler [30816] Triggered by Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 WebCore 0x00000001b0225e78 0x1aef42000 + 19807864 1 WebCore 0x00000001af51f158 0x1aef42000 + 6148440 2 WebCore 0x00000001b039bcf8 0x1aef42000 + 21339384 3 WebCore 0x00000001b0322a50 0x1aef42000 + 20843088 4 WebCore 0x00000001b03927c4 0x1aef42000 + 21301188 5 WebCore 0x00000001b03099cc 0x1aef42000 + 20740556 6 WebCore 0x00000001b0310034 0x1aef42000 + 20766772 7 WebCore 0x00000001b048e35c 0x1aef42000 + 22332252 8 WebCore 0x00000001b040d8ec 0x1aef42000 + 21805292 9 WebCore 0x00000001b032325c 0x1aef42000 + 20845148 10 WebCore 0x00000001b0322a50 0x1aef42000 + 20843088 11 WebCore 0x00000001b0322f28 0x1aef42000 + 20844328 12 WebCore 0x00000001b0322bdc 0x1aef42000 + 20843484 13 WebCore 0x00000001b0323294 0x1aef42000 + 20845204 14 WebCore 0x00000001b0322a50 0x1aef42000 + 20843088 15 WebCore 0x00000001b0322f28 0x1aef42000 + 20844328 16 WebCore 0x00000001b0322bdc 0x1aef42000 + 20843484 17 WebCore 0x00000001b0323294 0x1aef42000 + 20845204 18 WebCore 0x00000001b0322a50 0x1aef42000 + 20843088 19 WebCore 0x00000001b03e6e0c 0x1aef42000 + 21646860 20 WebCore 0x00000001b03e4944 0x1aef42000 + 21637444 21 WebCore 0x00000001b03e202c 0x1aef42000 + 21626924 22 WebCore 0x00000001b03e1ff4 0x1aef42000 + 21626868 23 WebCore 0x00000001b03dfe84 0x1aef42000 + 21618308 24 WebCore 0x00000001b00bb610 0x1aef42000 + 18322960 25 WebCore 0x00000001b015b8e8 0x1aef42000 + 18979048 26 WebCore 0x00000001b00bb354 0x1aef42000 + 18322260 27 WebKit 0x00000001b629b134 0x1b6135000 + 1466676 28 WebKit 0x00000001b629c814 0x1b6135000 + 1472532 29 WebKit 0x00000001b61f1430 0x1b6135000 + 771120 30 WebCore 0x00000001b016734c 0x1aef42000 + 19026764 31 WebCore 0x00000001b0150c5c 0x1aef42000 + 18934876 32 WebCore 0x00000001b01ab680 0x1aef42000 + 19306112 33 CoreFoundation 0x00000001a63e0b80 0x1a6330000 + 723840 34 CoreFoundation 0x00000001a63e08ac 0x1a6330000 + 723116 35 CoreFoundation 0x00000001a63e0090 0x1a6330000 + 721040 36 CoreFoundation 0x00000001a63dad28 0x1a6330000 + 699688 37 CoreFoundation 0x00000001a63da2e8 0x1a6330000 + 697064 38 Foundation 0x00000001a6dde3e0 0x1a6dd6000 + 33760 39 Foundation 0x00000001a6e1b1cc 0x1a6dd6000 + 283084 40 libxpc.dylib 0x00000001a6099d10 0x1a6085000 + 85264 41 libxpc.dylib 0x00000001a609c7a8 0x1a6085000 + 96168 42 com.apple.WebKit.WebContent 0x00000001049976b4 0x104994000 + 14004 43 libdyld.dylib 0x00000001a5e8d050 0x1a5e8c000 + 4176 ...
It’s clear that this is a null dereference scenario, but finding the bug in the code out of this dump would be a nightmare. So I decided to setup my WebKit for iOS environment.
System Prerequisites
You have to have:
- Recent Xcode with iPhone SDK
- Disable SIP!
- I couldn’t tell you how much time I wasted on this one. It was unclear to me that SIP on the Mac host machine would affect attaching to Safari / WebKit inside the iPhone simulator. Before disabling SIP I kept getting error: attach failed: unable to attach from lldb, and Could not attach to pid (unable to attach) trying to attach from Xcode. Disable SIP solved the debugger attaching errors
Get the Code
Generally just follow the Webkit guide for getting the code. All you have to run is:
svn checkout https://svn.webkit.org/repository/webkit/trunk WebKit
This will checkout the up-to-date code version, which is probably not very stable. Try updating it once in a while if it’s not stable until you reach a stable enough version using the SVN update command from within the WebKit directory:
svn up
Compiling + Running WebKit for iOS
All iOS browsers (including Safari, Chrome, Opera, Firefox..) must use the pre-installed iOS WebKit version to get approved into the App Store. The browser used for debugging the WebKit engine on iOS is Safari and there are automated scripts ready to compile and launch everything for you. Simply follow this guide from the WebKit blog. (Be sure to pass --ios-simulator --debug
to every WebKit script you are running)
Better Crash Logs
Now that you have a running Safari with debug symbols – reproduce the crash. If you manage to get the browser to crash with the “This webpage was reloaded because a problem occurred.” message – look at the ~/Library/Logs/DiagnosticReports/
directory on your Mac and check if a log was recently created. It’s file name should end with your Mac’s name and not with the iPhone, which could be a bit confusing. Open the crash dump and you should be able to read it much clearly:
... Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Termination Signal: Segmentation fault: 11 Termination Reason: Namespace SIGNAL, Code 0xb Terminating Process: exc handler [64527] VM Regions Near 0: --> __TEXT 0000000108332000-0000000108334000 [ 8K] r-x/rwx SM=COW /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/WebKit.framework/XPCServices/com.apple.WebKit.WebContent.xpc/com.apple.WebKit.WebContent Application Specific Information: CoreSimulator 581.2 - Device: iPhone SE For WebKit Development - Runtime: iOS 12.1 (16B91) - DeviceType: iPhone SE Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 com.apple.WebCore 0x000000026a32c1f7 WebCore::GraphicsContext::platformContext() const + 7 1 com.apple.WebCore 0x00000002695ff2fc WebCore::RenderThemeIOS::paintFileUploadIconDecorations(WebCore::RenderObject const&, WebCore::RenderObject const&, WebCore::PaintInfo const&, WebCore::IntRect const&, WebCore::Icon*, WebCore::RenderTheme::FileUploadDecorations) + 524 2 com.apple.WebCore 0x000000026a4b323f WebCore::RenderFileUploadControl::paintObject(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 2671 3 com.apple.WebCore 0x000000026a4343af WebCore::RenderBlock::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 271 4 com.apple.WebCore 0x000000026a4a9082 WebCore::RenderElement::paintAsInlineBlock(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 162 5 com.apple.WebCore 0x000000026a41a687 WebCore::InlineElementBox::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::LayoutUnit, WebCore::LayoutUnit) + 119 6 com.apple.WebCore 0x000000026a420cf0 WebCore::InlineFlowBox::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::LayoutUnit, WebCore::LayoutUnit) + 1056 7 com.apple.WebCore 0x000000026a5ac9e2 WebCore::RootInlineBox::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::LayoutUnit, WebCore::LayoutUnit) + 34 8 com.apple.WebCore 0x000000026a52a122 WebCore::RenderLineBoxList::paint(WebCore::RenderBoxModelObject*, WebCore::PaintInfo&, WebCore::LayoutPoint const&) const + 994 9 com.apple.WebCore 0x000000026a434b97 WebCore::RenderBlock::paintObject(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 695 10 com.apple.WebCore 0x000000026a4343af WebCore::RenderBlock::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 271 11 com.apple.WebCore 0x000000026a4347fa WebCore::RenderBlock::paintChild(WebCore::RenderBox&, WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::PaintInfo&, bool, WebCore::RenderBlock::PaintBlockType) + 666 12 com.apple.WebCore 0x000000026a43452f WebCore::RenderBlock::paintChildren(WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::PaintInfo&, bool) + 95 13 com.apple.WebCore 0x000000026a434bc3 WebCore::RenderBlock::paintObject(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 739 14 com.apple.WebCore 0x000000026a4343af WebCore::RenderBlock::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 271 15 com.apple.WebCore 0x000000026a4347fa WebCore::RenderBlock::paintChild(WebCore::RenderBox&, WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::PaintInfo&, bool, WebCore::RenderBlock::PaintBlockType) + 666 16 com.apple.WebCore 0x000000026a43452f WebCore::RenderBlock::paintChildren(WebCore::PaintInfo&, WebCore::LayoutPoint const&, WebCore::PaintInfo&, bool) + 95 17 com.apple.WebCore 0x000000026a434bc3 WebCore::RenderBlock::paintObject(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 739 18 com.apple.WebCore 0x000000026a4343af WebCore::RenderBlock::paint(WebCore::PaintInfo&, WebCore::LayoutPoint const&) + 271 19 com.apple.WebCore 0x000000026a500033 WebCore::RenderLayer::paintForegroundForFragmentsWithPhase(WebCore::PaintPhase, WTF::Vector<WebCore::LayerFragment, 1ul, WTF::CrashOnOverflow, 16ul> const&, WebCore::GraphicsContext&, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int, WebCore::RenderObject*) + 403 20 com.apple.WebCore 0x000000026a4fd809 WebCore::RenderLayer::paintForegroundForFragments(WTF::Vector<WebCore::LayerFragment, 1ul, WTF::CrashOnOverflow, 16ul> const&, WebCore::GraphicsContext&, WebCore::GraphicsContext&, WebCore::LayoutRect const&, bool, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int, WebCore::RenderObject*) + 393 21 com.apple.WebCore 0x000000026a4fa829 WebCore::RenderLayer::paintLayerContents(WebCore::GraphicsContext&, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int) + 2697 22 com.apple.WebCore 0x000000026a4fa8e2 WebCore::RenderLayer::paintLayerContents(WebCore::GraphicsContext&, WebCore::RenderLayer::LayerPaintingInfo const&, unsigned int) + 2882 23 com.apple.WebCore 0x000000026a4f7e01 WebCore::RenderLayer::paint(WebCore::GraphicsContext&, WebCore::LayoutRect const&, WebCore::LayoutSize const&, unsigned int, WebCore::RenderObject*, unsigned int, WebCore::RenderLayer::SecurityOriginPaintPolicy) + 273 24 com.apple.WebCore 0x000000026a1a64a7 WebCore::FrameView::paintContents(WebCore::GraphicsContext&, WebCore::IntRect const&, WebCore::Widget::SecurityOriginPaintPolicy) + 743 25 com.apple.WebCore 0x000000026a24e5ee WebCore::ScrollView::paint(WebCore::GraphicsContext&, WebCore::IntRect const&, WebCore::Widget::SecurityOriginPaintPolicy) + 574 26 com.apple.WebCore 0x000000026a1a619d WebCore::FrameView::traverseForPaintInvalidation(WebCore::GraphicsContext::PaintInvalidationReasons) + 253 27 com.apple.WebKit 0x0000000108a96e8a WebKit::RemoteLayerTreeDrawingArea::flushLayers() + 340 28 com.apple.WebCore 0x000000026a259f00 WebCore::ThreadTimers::sharedTimerFiredInternal() + 336 29 com.apple.WebCore 0x000000026a2a75ef WebCore::timerFired(__CFRunLoopTimer*, void*) + 31 30 com.apple.CoreFoundation 0x0000000109469f34 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20 31 com.apple.CoreFoundation 0x0000000109469b32 __CFRunLoopDoTimer + 1026 32 com.apple.CoreFoundation 0x000000010946939a __CFRunLoopDoTimers + 266 33 com.apple.CoreFoundation 0x0000000109463a1c __CFRunLoopRun + 2252 34 com.apple.CoreFoundation 0x0000000109462e11 CFRunLoopRunSpecific + 625 35 com.apple.Foundation 0x00000001083ee322 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 277 36 com.apple.Foundation 0x00000001083ee492 -[NSRunLoop(NSRunLoop) run] + 76 37 libxpc.dylib 0x000000010ba72812 _xpc_objc_main + 460 38 libxpc.dylib 0x000000010ba74cbd xpc_main + 143 39 com.apple.WebKit.WebContent 0x00000001083332d9 0x108332000 + 4825 40 libdyld.dylib 0x000000010b76b575 start + 1 ...
Debugging using Xcode
At this point you should already know the area of code you want to put breakpoints in. For convenience reasons – I chose to use Xcode over lldb for debugging. To do that you must configure Xcode to use the command-line-built binaries instead of rebuilding it itself. Do that by following the Debugging using Xcode section of the WebKit guide.
Now you can attach Xcode to the right WebKit process within the simulator. Important notice that also took me a long time to figure out – WebKit has multiple different processes for every tab. So the process ID printed when you run Tools/Scripts/run-safari
is not the one you want to attach to usually. In my example, I saw my crash is in WebCore, so I attached to the com.apple.WebKit.WebContent.Development within the simulator. If my bug was related to the networking part of WebKit, I would have attached to a different process. The different process names are documented here.
You can distinguish the debug versions from the real stable versions by the process names – WebKit process names that end .Development
are the debug versions.
Submitting the Fix
If you found and fixed the bug, follow WebKit’s guide for submitting a patch.
Good luck!