Debugging WebKit for iOS

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&amp;, WebCore::GraphicsContext&amp;, WebCore::RenderLayer::LayerPaintingInfo const&amp;, unsigned int, WebCore::RenderObject*) + 403
20  com.apple.WebCore             	0x000000026a4fd809 WebCore::RenderLayer::paintForegroundForFragments(WTF::Vector<WebCore::LayerFragment, 1ul, WTF::CrashOnOverflow, 16ul> const&amp;, WebCore::GraphicsContext&amp;, WebCore::GraphicsContext&amp;, WebCore::LayoutRect const&amp;, bool, WebCore::RenderLayer::LayerPaintingInfo const&amp;, unsigned int, WebCore::RenderObject*) + 393
21  com.apple.WebCore             	0x000000026a4fa829 WebCore::RenderLayer::paintLayerContents(WebCore::GraphicsContext&amp;, WebCore::RenderLayer::LayerPaintingInfo const&amp;, unsigned int) + 2697
22  com.apple.WebCore             	0x000000026a4fa8e2 WebCore::RenderLayer::paintLayerContents(WebCore::GraphicsContext&amp;, WebCore::RenderLayer::LayerPaintingInfo const&amp;, unsigned int) + 2882
23  com.apple.WebCore             	0x000000026a4f7e01 WebCore::RenderLayer::paint(WebCore::GraphicsContext&amp;, WebCore::LayoutRect const&amp;, WebCore::LayoutSize const&amp;, unsigned int, WebCore::RenderObject*, unsigned int, WebCore::RenderLayer::SecurityOriginPaintPolicy) + 273
24  com.apple.WebCore             	0x000000026a1a64a7 WebCore::FrameView::paintContents(WebCore::GraphicsContext&amp;, WebCore::IntRect const&amp;, WebCore::Widget::SecurityOriginPaintPolicy) + 743
25  com.apple.WebCore             	0x000000026a24e5ee WebCore::ScrollView::paint(WebCore::GraphicsContext&amp;, WebCore::IntRect const&amp;, 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!

Leave a Reply

Your email address will not be published. Required fields are marked *