Summary
- Three iLEAPP artifacts returned an error or nothing on recent iPhone images, and the reports looked like an unused feature.
- One item with a stored time that prints as year 0000 stopped the whole Calendar Events artifact; a renamed key left the location configuration artifact at zero rows; a sidecar file confused the Waze reader.
- The fixes recover 866 calendar rows on one image and 5 location rows on each of eight, and change nothing else in 46 compared report files.
- The pull request was merged on 25 October 2026.
- 3defects fixed
- 866calendar rows recovered on one image
- 46report files byte-identical elsewhere
- +592 / -37lines changed
The issue
An examiner runs iLEAPP over a full file system image and looks at the report. The calendar is not there. The log shows an error, "year 0 is out of range", and the Calendar Events artifact has no table at all. On the same image the calendar database holds 866 events. A single item with a stored creation time of -63114076800 seconds was enough: SQLite prints that value as year 0000, Python cannot hold a date before year 1, and the first such row stopped the whole artifact.
That failure is at least loud. The other two are not. A location-services artifact that reads one entry of the locationd configuration file returned no rows on every image we tried, because the entry is keyed differently on recent iOS releases. A missing table of this kind is identical to a phone that never used the feature. And the Waze artifacts logged "file is not a database" because a search pattern that ends in user.db* also matches the -wal and -shm sidecar files, and the parser took whichever one the archive listed first.
The examiner's problem is the same in all three cases. An empty or absent table is read as "the phone holds nothing", and the report does not say that the parser could not read what was there. Figure 1 shows the three cases and what each returns now.
Show this figure as a table
| Fix | Before | After |
|---|---|---|
| Calendar Events | error, no table | 866 rows, 5 blank creation times |
| Location services configuration | 0 rows | 5 rows on all eight images |
| Waze | 8 log errors | 0 log errors |
The essence
Three small changes, each tied to one mechanism, and nothing else changes in the reports. In Calendar Events the four time conversions leave a value that Python cannot hold blank instead of raising. In the location-services artifact the Routine.bundle entry is read under both of its key forms. In the Waze artifacts a small helper returns the main database file when it is present and nothing when only sidecars are present.
On eight real images the pull request compared 46 report files between the branch and upstream main and found them byte-identical, so no other artifact output changes.
| Artifact | File changed | Fields touched | What changes |
|---|---|---|---|
| Calendar Events | calendarAll.py | creation, start, end and modification times | A time SQLite renders as year 0000 is left blank; the row is kept |
| Location services configuration | locServicesconfig.py | Routine.bundle entry, five keys | Both key forms are read: the older com.apple.locationd.bundle-<path> and the newer p<path>: |
| Waze, five artifacts | waze.py | user.db and its sidecars | The main database file is returned whatever the order; a lone sidecar returns nothing |
Our methodology
The three defects were found by running every module over iPhone full file system extractions from the Lab's research corpus and looking at what returned nothing or failed. For each artifact that matched a file but returned no rows, or that raised an error, we compared it with the raw store: the row count of the table, or the presence of the key the artifact reads. An empty table is recorded as expected. A table that holds rows the artifact did not return, a key present under another name, or an exception on a stored value is recorded as a defect.
Each defect then went through the same steps. We reproduced it on a synthetic file, wrote the fix, and added a test that fails without it. A small builder creates a Calendar database and a configuration file with invented values, and no device data is used in the fixtures. The pull request adds three unit-test files with eight tests, and four registered test cases (three Calendar tables and the configuration artifact). On upstream main the Calendar Events case errors with "year 0 is out of range" and the configuration case returns no rows; on the branch all four pass.
Row counts on the real images were taken with separate code before and after. The independent count of non-Gregorian calendar items on the failing image is 866, and 5 of them carry the year-0000 time. On the other seven images the Calendar Events rows are identical to main. The configuration artifact returns 5 rows on all eight images against 0 before; every one of the eight files holds only the newer key form. For Waze, upstream main logged the database error 8 times on the iOS 26.6.2 image and the branch logs it 0 times (Figures 2 and 3).
Two points are stated as limits in the pull request. The Waze fix is verified on one image and one failure mode, because it is a search-order effect: the tar, directory and backup readers were not exercised with a sidecar listed first. And no Waze test case is possible in the repository's harness, which hands artifacts a mock file seeker, so the order is covered by a unit test and by the real-image run. The Calendar fix leaves the time blank rather than guessing a date; creation time was the only column seen to carry the value, with start, end and last-modified times valid on all five items.
Show this figure as a table
| Fix | Stored | What the parser did | Fix |
|---|---|---|---|
| Calendar Events | creation_date -63114076800, printed as year 0000 | Python datetime holds years 1 to 9999; ValueError | _utc_or_blank leaves it blank |
| Location services configuration | newer key p<path>: | looked up only the older key | ROUTINE_KEYS holds both |
| Waze | user.db, -wal, -shm; -shm listed first | pattern user.db* took the first hit | return the main file or nothing |
Show this figure as a table
| Image | Upstream main | This branch |
|---|---|---|
| Image with the year-0000 item | error | 866 |
| Other image 1 | 1,348 | 1,348 |
| Other image 2 | 191 | 191 |
| Other image 3 | 204 | 204 |
| Other image 4 | 149 | 149 |
| Other image 5 | 1,578 | 1,578 |
| Other image 6 | 141 | 141 |
| Other image 7 | 143 | 143 |
The value
The fixes restore three results an examiner would otherwise have missed: the whole calendar on one image, the Routine.bundle timing entries on all eight images, and a clean log for Waze. The wider lesson is in how they were found. A parser written for one release can return an empty table when a later release renames a key or stores a value outside the range of the language, and the report gives no sign of it. Counting rows in the raw store, as the checks here do, separates a real empty from a failed read.
It is easy to over-read the numbers. In the sweep behind this work, 322 iOS artifacts had no rows or an error on at least one of two images, and only two were drift defects, with a third that changed only the log (Figure 4). That is a lower bound on what the sweep found, not a rate, and it says that most empty results were genuinely empty. The right habit is to count before concluding, not to distrust every blank.
The limits are specific. The location configuration artifact writes all five rows whenever the entry exists, so a blank value can mean the key was not present: three of the five rows are blank on three of the eight images. Versions before iOS 18 are not covered by the newer-key finding in the pull request, and the iOS 18.1.1 check is carried over from an earlier local run. An artifact that now returns a row has still not shown that a person used the feature.
Show this figure as a table
| Group | Artifacts |
|---|---|
| No rows or an error on at least one image | 322 |
| Drift defects | 2 |
| Changed only the log | 1 |
Where this sits in the Lab's research
This pull request is one of the fixes reported in Lab Technical Report LAB-TR-0006, Empty Is Not Absent: Silent Failure Modes in Open-Source Mobile Forensic Parsers, published on Zenodo (doi:10.5281/zenodo.23717929, version 1.0). The report runs the complete artifact sets of an iOS and an Android parser suite, classifies every outcome and traces zero-row outcomes to the raw stores. Calendar Events is its example of a loud failure, the location-services list of a silent zero, and Waze of a noisy but benign read.
Lab Technical Report LAB-TR-0037, a fault-injection study on synthetic phones, is available on this site as a preprint; the Zenodo deposit is pending. It measures how far a silently empty or truncated read changes a stated conclusion, and it lists the location-services application list among the real cases of an empty read (zero rows on 8 of 8 images). The same pull request is cited in LAB-TR-0006 as the source of the fixes.
Sources
Every figure in the text and in the diagrams is listed here with where it comes from. Nothing else was used: no device, image or report was opened for this post.
| Number or claim | Source |
|---|---|
| 3 fixes; +592 lines, -37 lines | PR #2574 file list and body |
| Merged 25 October 2026 | PR #2574 state and mergedAt |
| creation_date -63114076800 prints as year 0000; first such row fails the artifact | PR #2574 body; diff file calendarAll.py |
| 866 rows; 5 items with year 0000; independent count 866 | PR #2574 body, checklist item 1; LAB-TR-0006 Table 6 |
| Calendar rows on the other seven images: 1,348; 191; 204; 149; 1,578; 141; 143 | PR #2574 body, checklist item 1 |
| 5 rows on all eight images against 0; all eight files hold only the newer key | PR #2574 body; diff file locServicesconfig.py |
| Waze: 8 log errors before, 0 after, iOS 26.6.2; PLACES table empty | PR #2574 body; LAB-TR-0006 Table 6 |
| 46 report TSVs byte-identical between branch and upstream main on eight images | PR #2574 body, first section |
| 3 unit-test files, 8 tests; 4 registered test cases; main errors, branch passes | PR #2574 body, test record; diff files test_calendar_year_zero.py, test_loc_services_clients.py, test_waze_user_db_search.py |
| Waze verified on one image and one failure mode; harness uses a mock seeker | PR #2574 body, reviewer notes and test record |
| 3 of 5 location rows blank on 3 of 8 images | PR #2574 body, checklist item 2 |
| iOS 18.1.1 carried over from an earlier run; versions before iOS 18 not covered | PR #2574 body, checklist item 1 |
| 322 iOS artifacts with no rows or an error; 2 drift defects; 1 log-only | LAB-TR-0006 section 5.3 |
| Location-services application list: zero rows on 8 of 8 images | LAB-TR-0037, table of real cases (preprint on this site; Zenodo deposit pending) |
| Zenodo DOI 10.5281/zenodo.23717929 (version 1.0) | papers/zenodo/DOIS.md |