Lab Technical Report LAB-TR-0023

Zeroed Cells, Kept Pages, Pending Logs: What SQLite Leaves of a Deleted Row on 47 iOS and Android Devices

Lab Technical Report, preprint, not peer reviewed

Abstract

Whether a deleted SQLite row can be recovered from a phone depends on settings examiners rarely report: secure_delete, auto-vacuum and the journal mode. Recovery methods are tested on prepared files; how these settings occur on phones in use has not been measured. We read the headers of 120,678 databases in 47 full file system extractions (33 Apple, 14 Android), walked the free space of a hashed sample of 10,286 without decoding a row, and built 1,440 synthetic databases under every combination of settings to measure what each leaves of a deleted row. On the median device, freed cells were zeroed in 93 to 98 percent of the databases that had any. Android databases also auto-vacuumed (median 95 percent), as the platform’s build flags set; on iOS, 669 of 684 databases with free pages kept their content, the pattern of secure_delete FAST. Zeroing did not make deletion final: in the experiment, a row deleted while the write-ahead log was pending survived in full under every setting, and 34 to 47 percent of databases per device had pending frames. On these devices, the state of the log at acquisition, more than secure_delete, sets how much of a deleted row can be recovered.

CCS Concepts • Applied computing → System forensics; • Information systems → Data management systems.

Keywords SQLite, deleted record recovery, secure_delete, auto-vacuum, write-ahead log, freelist, carving, mobile forensics, iOS, Android, empirical study

ACM Reference FormatDaniel Addai, Kingsley Albert Egyir, Sanders Larweh Tetteh, and Selorm Adjabeng. 2026. Zeroed Cells, Kept Pages, Pending Logs: What SQLite Leaves of a Deleted Row on 47 iOS and Android Devices. Lab Technical Report LAB-TR-0023, version 1.0. SectraTEK Lab, Prudential Associates. Preprint, not peer reviewed. https://lab.sectratek.com/research/reports/lab-tr-0023/

1 Introduction

Most of what a phone records about messages, calls, locations and app use is kept in SQLite databases, and examiners are often asked whether a record that is no longer there once existed. SQLite does not erase a deleted row by default: its bytes stay in the file until they are reused [16], and recovery tools and methods carve them from the free space of the file [7], [10], [12]. Whether that free space holds anything depends on settings chosen by the platform, the library build and the application. These settings are tested on prepared files [11], [14]; we found no measurement of how they are set on the databases of phones in use.

A motivating example. In our controlled experiment (Section 3.4), 1,000 invented message rows were deleted one at a time from databases with secure_delete ON and no auto-vacuum. With a rollback journal emptied after each commit, none of the 1,000 rows could be found anywhere in the files. With a write-ahead log whose changes had not yet been copied into the main file, all 1,000 were still there in full, under the same secure_delete setting. The setting that examiners usually ask about decided nothing; the state of the log decided everything.

The problem. Before a recovery is attempted or a negative result is reported, an examiner needs to know what the configuration of each database allows. That needs two pieces of evidence that are not available together: how the settings and the free structure are distributed over the databases of current devices, and how much of a deleted row each configuration leaves under a known procedure. We therefore ask four questions about the SQLite databases in full file system extractions of iOS 15 to 26 and Android 10 to 16 devices.

  • RQ1 (Settings). How are zeroing, auto-vacuum, journal mode and page size distributed, by OS group and by family of database?
  • RQ2 (State). How much free space and how many unretired log frames do the databases hold at acquisition?
  • RQ3 (Recoverability). Under a known procedure, how much of a deleted row survives each combination of settings, deletion pattern and later writes, and how much does a content-blind reader recover?
  • RQ4 (Ceiling). What recoverability do the configurations observed on the devices imply?
Table 1: Research questions, answers and evidence. Percentages per device are medians across devices; all values describe this sample.
RQAnswerEvidence
RQ1 SettingsFreed cells are zeroed on both platforms (non-zero freeblocks in 2.0 to 6.7 percent of databases per device). Android databases use auto-vacuum FULL (95.5 and 94.5 percent), so free pages are rare (1 percent). iOS databases use no auto-vacuum or INCREMENTAL, free pages occur in 15 to 19 percent, and 669 of 684 databases with free pages kept their content: the signature of secure_delete FAST. WAL mode: 76 to 88 percent on iOS, 49 to 56 percent on Android.Tables 5 to 8; Figure 2
RQ2 StatePer device, a header-level ceiling of 938 and 1,403 MiB on iOS against 271 and 308 MiB on Android, mostly -wal bytes. Pending frames on 34 to 47 percent of databases per device; superseded frames in 87 to 92 percent of iOS and 73 to 78 percent of Android log files walked.Tables 5, 8
RQ3 RecoverabilityOf 504,000 deleted rows, 62.6 percent remained in full. The journal state mattered most: pending WAL 100 percent, TRUNCATE 32.2 percent without later writes; secure_delete ON with TRUNCATE left nothing. A content-blind reader recovered 51.6 percent from free space, and 60.0 percent with a main-file differential.Tables 7, 9; Figures 3, 4
RQ4 CeilingFor a row deleted just before acquisition, the observed configurations imply that it remains in full in 27 to 41 percent of databases (scattered deletes); for a range delete on iOS, 32 to 47 percent if freed pages are zeroed and 95 to 96 percent if they are kept, as observed.Figure 5; Table 6

Contributions. This report makes four contributions.

  • A read-only structure reader and a pre-stated census of deletion-relevant settings and free structure over 120,678 databases on 47 devices, by OS group and family, without decoding a row (Sections 3.1, 3.2, 5.1 and 5.3).
  • A tested way to read secure_delete from a file, in which freed cells and freed pages are read separately: on known settings it separates OFF, FAST and ON whenever a free page exists (Sections 3.3 and 5.2).
  • A controlled experiment on 1,440 synthetic databases that measures, for every combination of the settings, what a known oracle finds and what a content-blind reader recovers (Sections 3.4 and 5.4).
  • Configuration-implied recoverability ceilings for each OS group and family, obtained by joining the census to the experiment (Section 5.5).

Scope. All results describe the Lab's research corpus: 47 full file system extractions readable when the study ran, most from iOS 26 and Android 16, with 12 of the 14 Android phones from one vendor. No row of a real database was read; recoverability comes only from synthetic databases built with one SQLite version. Cells of fewer than five devices are not reported. Section 7 states the limits.

2 Background and Related Work

This section describes where a deleted row can survive in an SQLite database and which settings remove it, then places the work among recovery methods, test corpora and single-application studies.

2.1 Where a deleted row can survive

An SQLite database is a file of fixed-size pages. Rows of a table are cells in the leaf pages of a b-tree; each leaf page holds a header, an array of pointers to its cells, and the cells, which grow from the end of the page towards the pointer array [15]. When a row is deleted, its cell is removed from the pointer array. The bytes it occupied become a freeblock, a chained run of free bytes whose first four bytes are overwritten by a pointer to the next freeblock and a length, or they join the gap between the pointer array and the first cell. We call freeblocks and the gap together the unallocated space of a live page. When a page becomes empty, it is put on the freelist, a chain of trunk pages that list free leaf pages; the header field at offset 36 counts them [15]. Long rows continue on overflow pages, which are freed with the row. Figure 1 summarizes where the bytes of a deleted row can remain and what removes them.

Where a deleted row can survive, what removes it, and how the two arms of the study meetFour places, each with the setting or event that removes it. One: unallocated space of a live page (freeblocks and gap), removed by secure_delete ON or FAST. Two: free pages on the freelist, removed by secure_delete ON, or truncated by auto_vacuum FULL. Three: earlier page images in WAL mode, in the main file until a checkpoint and in superseded log frames until the log is overwritten. Four: original page images in a rollback journal kept by the PERSIST mode; TRUNCATE and DELETE leave none. Below, the census reads which settings and structures occur on real devices, the mirror experiment measures what each configuration leaves of a deleted row, and together they give a configuration-implied ceiling.Where the row survivesRemoved by1 Unallocated spacefreeblocks and gap of a live pagesecure_delete ON, FAST2 Free pagesfreelist trunk and leaf pagessecure_delete ONauto_vacuum FULL3 Earlier page images (WAL)stale main file, old log framescheckpointthen log overwrite4 Rollback journaloriginal pages kept by PERSISTTRUNCATE, DELETECensus (47 devices)which settings and structuresoccur; no row is readMirror (1,440 databases)what each configurationleaves of a deleted rowconfiguration-implied ceiling
Figure 1: Where a deleted row can survive in an SQLite database and what removes it (Sections 2.1 and 2.2), and how the census and the mirror experiment are joined (Section 3).

A deleted row can also survive outside the main file. In write-ahead-log (WAL) mode, a transaction appends new images of the changed pages, called frames, to a -wal file; a checkpoint later copies them into the main file, and once all frames are copied the next writer starts again from the beginning of the log instead of shrinking it [18]. Until the checkpoint, the main file still holds the pages as they were before the deletion. After it, earlier frames for the same page can remain in the log file until they are overwritten. The shared-memory file -shm records how many frames are valid (mxFrame) and how many have been copied (nBackfill). In rollback-journal mode, the -journal file holds the original images of the pages a transaction changes; with the PERSIST journal mode the file and its content stay after the commit, with only the header zeroed [16].

2.2 Settings that erase it

Three settings decide how much of this survives (Table 2). With secure_delete on, SQLite overwrites deleted content with zeros; the FAST setting does so only where it costs no extra I/O, which, in the documentation's words, purges old content from b-tree pages but leaves forensic traces on freelist pages [16]. The default is off unless the library is compiled with SQLITE_SECURE_DELETE [17], and the setting belongs to the connection: it is not stored in the file. With auto_vacuum FULL, free pages are moved to the end of the file and the file is truncated at every commit; with INCREMENTAL they stay until an incremental_vacuum runs; the mode must be chosen before tables are created and is recorded in the header [15], [16]. The journal mode decides whether earlier page images are kept beside the file. Platform builds set some of these defaults. The Android build of SQLite defines SQLITE_SECURE_DELETE and SQLITE_DEFAULT_AUTOVACUUM=1 [2], and the Android framework's default journal mode is TRUNCATE, with a WAL checkpoint every 100 pages when WAL is enabled [3]. Chromium's SQLite build also defines SQLITE_SECURE_DELETE [6]. Core Data stores on iOS use WAL by default since iOS 7 [5]. We found no public statement of how Apple's system SQLite is compiled.

Table 2: Settings that decide what remains of a deleted row, where each is recorded, and the defaults set by the platform builds we could read.
SettingRecorded in the file?What it removesDefaults
secure_delete ONNo; connection setting, compile-time defaultFreed cells (freeblocks, gap) and freed pages, overwritten with zerosSQLite: off [17]. Android build: on [2]. Chromium build: on [6]
secure_delete FASTNoFreed cells only; free pages keep their content [16]None found
auto_vacuum FULLYes; header offsets 52 and 64Free pages, truncated from the file at each commitSQLite: none [16]. Android build: FULL [2]
auto_vacuum INCREMENTALYesFree pages, only when incremental_vacuum runsNone found
WAL journalYes; header bytes 18 and 19 equal 2; -wal, -shmNothing by itself: the main file keeps the old pages until a checkpoint, and the log keeps earlier frames until it is overwritten [18]Core Data: WAL since iOS 7 [5]. Android framework: checkpoint every 100 pages [3]
Rollback journal: TRUNCATE, DELETE, PERSISTPartly; -journal fileTRUNCATE and DELETE leave no journal content; PERSIST keeps the last transaction's original pages [16]Android framework: TRUNCATE [3]

2.3 Recovery methods and test corpora

Jeon et al. described the structure of deleted records in SQLite and a recovery method tested on SQLite-based software [7]. Nemetz, Schmitt and Freiling built a standardized corpus of SQLite files with special format features, pitfalls and deleted records, and found that none of six tools handled all corner cases [11]; Schmitt added anti-forensic databases to such corpora [14]. Meng and Baier analysed deletion behaviour under different database parameters, named the pragmas as the key factor, and presented bring2lite [10]. Pawlaszczyk and Hummert recover records from the freelist, freeblocks and the unallocated region and report that the unallocated region usually holds only zeros but can keep fragments [12]. Sanderson's book covers SQLite structures and recovery for examiners [13], and the Lab's earlier report on the Screen Time store measured how counts change when a database is read without its write-ahead log [1]. Recent work recovers encrypted WAL pages from unallocated file-system space [8], and a 2025 survey compares deleted-record recovery techniques [9].

2.4 Studies of single applications

Studies of one application show the settings at work. Anglano, Canonico and Guazzone found that ChatSecure on Android stores its messages in SQLCipher databases that delete securely, and that the commercial tools they tried recovered nothing [4]. Such studies answer the question for one application on one device; they do not say how often each setting occurs.

Position of this work. The mechanisms and the settings are documented, and recovery methods are tested on prepared files whose settings the authors chose. What is missing, as far as we found, is how the settings are distributed over the databases on phones in use, and how much a deleted row's recoverability depends on them under a known procedure. We measure both, and we connect them: the census tells which configurations occur, and the controlled experiment tells what each configuration leaves.

3 Method

The study has two arms joined by one reader (Figure 1). The census reads the settings and the free structure of real databases and never reads a row. The mirror experiment builds databases under every combination of the settings, deletes invented rows in controlled ways, and measures what remains. The sampling rule, strata, thresholds and the inference rule of Section 3.3 were written into the runner and a dated note before the full run.

3.1 A read-only structure reader

We wrote a reader for the SQLite file format in the Python standard library [15]. It parses bytes in memory and never opens a file through the SQLite library, so a reading cannot checkpoint a log, roll back a journal or change a file. From the 100-byte header it takes the page size, the reserved bytes, the read and write versions (2 for WAL mode), the freelist trunk and count, the largest root page and the incremental-vacuum flag, which together give the auto-vacuum mode, and the version number of the library that last wrote the file. A full walk follows the freelist chain and classifies each free leaf page as all zero, an intact b-tree page image, or other non-zero content; it then follows every b-tree from the schema table, classifies each page as b-tree, overflow, pointer-map or unreached, and measures, in each live b-tree page, the freeblock chain and the gap, counting their bytes and how many of them are non-zero. For a -wal file it verifies the header checksum, then walks frames while the salts match and the cumulative checksum holds, and counts valid frames, committed frames, frames after the last commit, and superseded frames, which hold an earlier image of a page that a later committed frame replaces. From -shm it reads mxFrame and nBackfill, and calls the difference the pending frames: committed changes not yet in the main file. The reader returns counts and classes only.

3.2 Census

Frame and unit. The frame was every iOS and Android full file system extraction in the Lab's research corpus that was readable on 27 October 2026. The unit is the device: one byte-identical copy and one second extraction of the same handset on the same day were excluded. An extraction counted as readable if the central directory of its archive could be parsed.

Candidates and families. A file was a candidate if its name ends in .db, .sqlite, .sqlite3, .sqlitedb, .storedata or .db3, if it lies in an Android databases folder, or if it has a -wal or -journal sibling; it was a database if its first 16 bytes are the SQLite magic string. A candidate named as a database that was not plaintext SQLite and whose first 4,096 bytes had an entropy of at least 7.5 bits per byte was counted as a likely encrypted container. Each database was assigned to one of five families by its path: OS image (read-only system partitions), OS system (system data outside app sandboxes), platform app (apps and services of Apple, Google or the Android Open Source Project, from the container identifier on iOS and the package name on Android), OEM or carrier app (Android only) and third-party app. Identifiers were used for this assignment only and are not reported.

Header level, all databases. For every database we read the first 4,096 bytes of the main file, the 32-byte header and the size of its -wal file, the first 136 bytes of its -shm file and the first 8 bytes of its -journal file. A journal with a zeroed header and a non-zero size is the PERSIST state of Section 2.1. The header-level ceiling of a database is its freelist bytes (free pages times page size) plus its -wal bytes: an upper bound on the space where deleted content could lie, not a measure of what is there. Freelist counts larger than the file allows were set aside as inconsistent.

Deep sample. Within each extraction and family, the first 60 databases in the order of a SHA-256 hash of the extraction and the entry name, with main file and -wal each at most 64 MiB, were read in full into memory and walked (Section 3.1). Database bytes were never written to disk.

Strata and statistics. Strata are four OS groups: iOS 15 to 18, iOS 26, Android 10 to 14 and Android 16. A cell is reported only if at least five devices contribute. Because one device can hold thousands of databases, we compute each share per device and report the median and quartiles across devices; pooled shares over all databases appear only where stated.

3.3 Inferring secure_delete from what it leaves

secure_delete is not stored in the file, so we infer its effect from the free structure (Listing 1). The rule follows from the documented behaviour [16]: ON zeroes both freed cells and freed pages, FAST zeroes freed cells only, and OFF zeroes neither. It describes what the file shows, not the setting that produced it, and a database written at different times under different settings shows the mixture. We tested the rule on the mirror databases, whose settings are known (Section 5.2).

Listing 1: Zeroing pattern of one database, fixed before the run. A freeblock body is the freeblock without its 4-byte header.

if any freeblock body is non-zero      -> off-pattern
elif free pages hold non-zero bytes:
    freeblocks present, all zero       -> fast-pattern
    no freeblocks                      -> pages-retained
elif free pages or freeblocks exist    -> on-pattern
else                                   -> no evidence

3.4 Mirror experiment

Design. Each mirror database has one table of invented messages, msg(id INTEGER PRIMARY KEY, thread, ts, body, flag), with an index on (thread, ts), and 2,000 rows inserted in transactions of 25; bodies are 38 to 408 random characters, and on average one row in twenty has about 2,000 to 6,000 characters so that it spills to overflow pages. We crossed five factors (Table 3): secure_delete (OFF, FAST, ON), auto-vacuum (NONE, FULL, INCREMENTAL never run, INCREMENTAL run after the deletions) and four journal states: TRUNCATE (rollback, journal emptied after each commit, the Android default), PERSIST, WAL with the deletions still pending, and WAL after a checkpoint with the log file left in place. Two deletion patterns were applied: scattered, 200 single-row deletions in separate transactions, as when a user deletes messages one by one, and range, one transaction deleting 500 consecutive rows, as when old records are purged. After the deletions, new rows were written amounting to 0, 0.5 or 2 times the deleted text. In the WAL states the inserted rows were checkpointed before the deletions, so that the deletions and later writes are the log's content. With five seeds this gives 1,440 databases. The page size was 4,096, the library SQLite 3.50.4, and synchronous was OFF, which changes when data reach the disk but not what is written. Files were copied while the connection was open, as an extraction copies files of a running phone.

Two readers. The oracle knows every deleted body and searches the raw bytes of the main file, the log and the journal for it; a row counts as fully present if its whole body occurs contiguously, or, for a row longer than one page, if its first, middle and last 16 bytes all occur, and as partly present if any of these anchors occurs. The oracle gives an upper bound on what any reader could recover, and the position of each find in the reader's page map tells where the row survived. The carver is content-blind. It knows only the table's schema. It parses the cells of free pages that are intact table pages, searches freeblocks, gaps, free pages, every frame of the log and every page image of the journal for a record header that fits the schema, decodes the record, follows its overflow chain, and accepts it if the text decodes. A carved record counts as a recovered row only if all four of its stored values equal those of a deleted row; carved records matching no row, deleted or live, are counted as false records.

Table 3: Factors of the mirror experiment, fully crossed (3 × 4 × 4 × 2 × 3 = 288 configurations, five seeds each).
FactorLevels
secure_deleteOFF, FAST, ON
Auto-vacuumNONE; FULL; INCREMENTAL, idle; INCREMENTAL, run after the deletions
Journal stateTRUNCATE; PERSIST; WAL, deletions pending; WAL, checkpointed after the deletions, log file kept
Deletion patternScattered: 200 single-row transactions. Range: 500 consecutive rows in one transaction
Later writesNew rows totalling 0, 0.5 or 2 times the deleted text

4 Evidence and Setup

Table 4 lists the evidence. The 47 extractions are full file system archives. Device classes are reported only where a class has at least five devices.

Table 4: Evidence.
ClassContent
iOS extractions33 Apple devices (at least 19 iPhones, one iPad): iOS 15 to 18 (12 devices) and iOS 26 (20); one device's version could not be read and counts only in totals.
Android extractions14 phones (12 Samsung): Android 10 to 14 (5) and Android 16 (9).
Databases126,779 candidates: 120,678 SQLite databases, 3,836 files named as databases that are not plaintext SQLite (975 of them with high entropy), the rest empty or other files.
Deep sample10,286 databases on the 46 devices with a known OS group, walked in memory.
Mirror1,440 synthetic databases, 2,000 invented rows each; 504,000 deleted rows.

Runs. One runner script ran every measurement in sequence, with a time limit for each extraction and for the experiment, and wrote one report of aggregates; every number in this report comes from it. All 47 archives were read without error, in a median of 17.6 s each (2.2 to 61.1 s); the experiment took 1,213 s. Android archives are read without any bind-mount copy of the data partition, which would be counted as read-only system files. The validation of Listing 1 (Section 5.2) led us to read freed cells and freed pages separately and to add the differential reader of Section 3.4; both additions are post hoc, and the pre-stated outputs are kept in the report.

Tools. Python 3.14.5 and its standard library; the bundled SQLite 3.50.4 built the mirror databases and read them for the differential reader only. The census never used the SQLite library. Every -wal walk was checked against the -shm frame count where both existed (Table 8).

5 Results

The two platforms zero freed cells alike and differ in what happens to freed pages; on both, the write-ahead log keeps what zeroing removes. Table 1 summarizes the answers; this section gives the evidence.

5.1 Settings (RQ1)

The platforms differ in auto-vacuum and journal mode (Table 5, Figure 2). On Android, the median device used auto-vacuum FULL for 95.5 percent (Android 10 to 14) and 94.5 percent (Android 16) of its databases, the default of the platform's SQLite build [2], and only about 1 percent of databases held a free page. On iOS no single mode dominates: half (iOS 15 to 18) to two thirds (iOS 26) of the databases used no auto-vacuum and most of the rest INCREMENTAL, FULL was rare (3 percent), and 15 to 19 percent of databases held at least one free page. WAL mode was the norm on iOS (76 and 88 percent of databases per device) and about half on Android (49 and 56 percent). The page size was 4,096 bytes for 94 to 99 percent. A PERSIST journal, which keeps the last transaction's original pages, was rare: 1.4 and 1.5 percent of databases per device on Android, almost none on iOS.

Table 5: Settings and state of all SQLite databases, by OS group (header level). Cells give the median across devices of the per-device value, with quartiles in parentheses. The header-level ceiling is freelist bytes plus -wal bytes.
MeasureiOS 15–18iOS 26Android 10–14Android 16
Devices122059
SQLite databases22,53266,9519,75820,235
Journal and log (percent of databases)
WAL journal mode76 (73–81)88 (85–89)49 (45–55)56 (54–63)
Non-empty -wal file49 (42–52)38 (35–46)45 (43–45)54 (51–62)
Pending frames (mxFrame > nBackfill)47 (42–50)37 (34–45)34 (32–38)43 (38–49)
PERSIST journal kept0.0 (0.0–0.1)0.0 (0.0–0.0)1.4 (0.7–1.8)1.5 (0.9–1.8)
Auto-vacuum (percent of databases)
NONE50 (48–53)64 (54–68)3 (3–5)4 (3–5)
INCREMENTAL46 (44–48)34 (29–44)1 (0–1)1 (1–1)
FULL3 (3–5)3 (2–3)96 (94–97)95 (94–96)
Free space (percent of databases)
At least one free page19 (16–21)15 (12–17)1 (1–1)1 (1–1)
Page size 4,096 bytes99 (97–99)99 (99–99)94 (94–95)97 (96–97)
Per device (MiB)
Freelist bytes199 (122–361)345 (153–526)5 (5–17)32 (19–42)
-wal bytes740 (129–1470)1033 (793–1322)269 (152–343)291 (207–300)
Pending frame bytes691 (128–1182)872 (678–1192)164 (81–177)147 (112–169)
Header-level ceiling938 (194–1869)1403 (929–1843)271 (192–347)308 (249–326)
Deletion-relevant settings and state by OS group: median share of databases per device, with quartilesHorizontal bars from 0 to 100 percent, four bars per measure, one per OS group; bar = median across devices of the per-device share of databases, whisker = quartiles. WAL mode: iOS 15–18 median 76.3, quartiles 73.4 to 80.9 (n 12 devices); iOS 26 median 87.5, quartiles 85 to 89.3 (n 20 devices); Android 10–14 median 49, quartiles 44.6 to 55.1 (n 5 devices); Android 16 median 55.6, quartiles 53.6 to 62.6 (n 9 devices). Pending frames: iOS 15–18 median 47.5, quartiles 41.5 to 50.3 (n 12 devices); iOS 26 median 37.3, quartiles 33.9 to 44.6 (n 20 devices); Android 10–14 median 33.6, quartiles 31.8 to 37.8 (n 5 devices); Android 16 median 43.1, quartiles 38.2 to 48.8 (n 9 devices). Auto-vacuum FULL: iOS 15–18 median 3.4, quartiles 2.7 to 4.6 (n 12 devices); iOS 26 median 2.5, quartiles 2.1 to 2.9 (n 20 devices); Android 10–14 median 95.5, quartiles 94.5 to 96.5 (n 5 devices); Android 16 median 94.5, quartiles 94 to 96.2 (n 9 devices). Auto-vac. INCR.: iOS 15–18 median 45.8, quartiles 44.1 to 48.3 (n 12 devices); iOS 26 median 34.3, quartiles 29 to 43.6 (n 20 devices); Android 10–14 median 0.7, quartiles 0.5 to 0.8 (n 5 devices); Android 16 median 1, quartiles 0.7 to 1.4 (n 9 devices). Free pages: iOS 15–18 median 19, quartiles 16.3 to 21 (n 12 devices); iOS 26 median 15, quartiles 11.8 to 16.8 (n 20 devices); Android 10–14 median 1.1, quartiles 0.9 to 1.2 (n 5 devices); Android 16 median 0.9, quartiles 0.6 to 1 (n 9 devices). Freed cells kept: iOS 15–18 median 6.7, quartiles 5.1 to 7.7 (n 12 devices); iOS 26 median 4.5, quartiles 3.7 to 5.8 (n 20 devices); Android 10–14 median 2, quartiles 1.4 to 5.4 (n 5 devices); Android 16 median 2.7, quartiles 2 to 4.5 (n 9 devices). Free pages kept: iOS 15–18 median 100, quartiles 99.3 to 100 (n 12 devices); iOS 26 median 100, quartiles 99.1 to 100 (n 20 devices); Android 10–14 not shown, fewer than 5 devices; Android 16 median 100, quartiles 100 to 100 (n 6 devices).iOS 15–18iOS 26Android 10–14Android 160255075100percent of databases per deviceWAL modePending framesAuto-vacuum FULLAuto-vac. INCR.Free pagesFreed cells keptFree pages keptunder 5 devices
Figure 2: Deletion-relevant settings and state by OS group. Bars: median across devices of the per-device share of databases; whiskers: quartiles. The last two measures come from the deep sample: databases with a non-zero freeblock among those with freeblocks, and with a non-zero free page among those with free pages.

Families. Within each platform the families follow the platform pattern (Table 6). The exceptions are in what applications choose. On Android, OEM or carrier apps used WAL most (81 and 87 percent) and had the most pending frames; third-party apps used WAL least (27 and 36 percent) and were the family in which non-zero freed cells were most common on Android 16 (8 percent per device), as expected from applications that bring their own SQLite build. On iOS, system and platform-app databases almost never kept freed cells, while read-only system databases did (14 and 15 percent); those files are built off the device and are not written on it. Files named as databases that were not plaintext SQLite and had high entropy, most likely encrypted containers, occurred on 44 of the 46 devices: 975 files, under 1 percent of all candidates.

Table 6: Settings by family. Percentages are medians across devices of per-device shares; “Freed cells kept” is the share of deep-sampled databases with freeblocks in which some freeblock holds non-zero bytes. The implied ceiling is explained in Section 5.5 (scattered deletions, low–high reading). A dash marks a cell with fewer than five devices; OEM or carrier apps exist only on Android.
OS group, familyDevicesDatabasesWAL mode %Pending %Auto-vacuum FULL %INCR. %Freed cells kept %Implied ceiling %
iOS 15–18
OS system96,70288497500 (0–7)59–60
Platform app92,72496614680 (0–0)51
Third-party app911,42576522463 (0–7)49
OS image (read-only)121,681001515 (11–17)n/a
iOS 26
OS system2031,18394293300 (0–2)29
Platform app2014,49398452420 (0–0)37
Third-party app2019,39082531421 (0–6)44
OS image (read-only)201,885101414 (11–15)n/a
Android 10–14
OS system5204201610000 (0–0)13
Platform app53,27542219802 (0–2)24
OEM or carrier app52,85181599710 (0–9)63–64
Third-party app53,36527249313 (2–5)20
OS image (read-only)56300130–n/a
Android 16
OS system953028239900 (0–0)21
Platform app95,92249299810 (0–0)31
OEM or carrier app95,85887689903 (0–6)68
Third-party app97,78736319128 (5–11)27
OS image (read-only)9138541400 (0–0)n/a

5.2 Zeroing and how to read it (RQ1)

Validation. On the mirror databases, whose settings are known, the pre-stated rule of Listing 1 classified all 480 OFF databases as off-pattern and all 480 ON databases as on-pattern, but only 90 of the 480 FAST databases as fast-pattern: the other 390 had no free page, and without one, a FAST database looks exactly like an ON database. Reading freed cells and freed pages separately removes the confusion where it can be removed (Table 7): every combination that the file shows belongs to exactly one setting, except “cells zeroed, no free page”, which is ON or FAST. We therefore report the two readings separately.

Table 7: What the file shows under each known secure_delete setting (mirror, 480 databases per setting): freed cells (freeblock bodies), free pages. “None” means the database had no free page to test.
Cells, pagesOFFFASTON
kept, kept9000
kept, none39000
zeroed, kept0900
zeroed, zeroed0090
zeroed, none0390390

Real databases. Freed cells were zeroed on both platforms (Table 8). Per device, the median share of databases with a non-zero freeblock was 6.7 and 4.5 percent on iOS and 2.0 and 2.7 percent on Android. Freed pages split the platforms. On iOS, 289 of 294 and 380 of 390 databases with free pages kept non-zero content, and most free pages (47,821 of 56,020 and 26,140 of 54,397) were intact b-tree pages; zeroed cells with kept pages is what FAST produces (Table 7). On Android, free pages were too rare to read in most databases, which follows from auto-vacuum FULL: on Android 16, nine deep-sampled databases on six devices had any, and 760 of their 1,550 free pages were all zero; on Android 10 to 14, fewer than five devices had any. Together with the platform build flag [2], the Android pattern is that of secure_delete ON. We found no public statement of the iOS build (Section 2.2), so the iOS result is a reading of the files, not of the library.

Table 8: Free structure and log state in the deep sample. Counts are databases (or -wal files) pooled over the devices of each group; percentages per device are medians with quartiles.
MeasureiOS 15–18iOS 26Android 10–14Android 16
Devices, databases walked12, 2,34020, 4,6225, 1,1679, 2,157
Freed cells (freeblocks)
Databases with freeblocks1,6073,3427281,276
of which some freeblock non-zero1211682340
Per device, % with non-zero freeblock6.7 (5.1–7.7)4.5 (3.7–5.8)2.0 (1.4–5.4)2.7 (2.0–4.5)
Free pages
Devices with such databases122046
Databases with free pages294390–9
of which some free page non-zero289380–8
Free leaf pages walked56,02054,397–1,550
all zero260789–760
intact b-tree page image47,82126,140–679
Write-ahead log
-wal files walked8451,454434938
with superseded frames7791,258316731
superseded frames per file78 (9–365)58 (5–289)4 (0–37)5 (3–35)
with frames after the last commit815114326
committed frames equal mxFrame834 of 8361,439 of 1,441408 of 433878 of 933

5.3 Free space and the log (RQ2)

Measured by the header alone, the space in which deleted content could lie is large and lies mostly in the logs. The median iOS 26 device held 345 MiB in free pages and 1,033 MiB in -wal files, a header-level ceiling of 1,403 MiB; the median Android 16 device held 32 MiB and 291 MiB (Table 5). The ceiling is an upper bound on space, not a measure of content: Section 5.2 shows that freed cells within it are mostly zero.

The log state matters more than its size. Pending frames, committed changes that -shm records as not yet copied into the main file, were present in 34 to 47 percent of databases per device, a median of 872 MiB of page images on an iOS 26 device and 147 MiB on an Android 16 device. While a frame is pending, the main file still holds the page as it was before the change. Most walked log files also held superseded frames, earlier images of a page that a later frame replaces: 87 and 92 percent of the iOS files and 73 and 78 percent of the Android files (Table 8). On Android, a quarter to a third of log files also held frames after the last commit, left from an earlier generation of the log; on iOS this was rare. The frame count from our walk equalled the mxFrame value of -shm for 99.8 and 99.9 percent of iOS files and 94.2 and 94.1 percent of Android files.

5.4 What each configuration leaves (RQ3)

Of the 504,000 rows deleted in the experiment, 315,696 (62.6 percent) remained in full somewhere in the files and 2,327 (0.5 percent) in part. Figure 3 gives every configuration without later writes, and Table 9 the effect of each factor.

Mirror experiment: percent of deleted rows whose content is still fully present, by setting, journal state and deletion pattern, no later writesHeat map, 12 rows (secure_delete by auto_vacuum) and 8 columns (deletion pattern by journal state); each cell is the percent of deleted rows found in full by the oracle reader over five seeds. secure_delete OFF, auto_vacuum NONE: scattered TRUNCATE 95.6, scattered PERSIST 95.5, scattered WAL-pending 100, scattered WAL-checkpointed 98, range TRUNCATE 97.2, range PERSIST 100, range WAL-pending 100, range WAL-checkpointed 100; secure_delete OFF, auto_vacuum FULL: scattered TRUNCATE 92.1, scattered PERSIST 91.2, scattered WAL-pending 100, scattered WAL-checkpointed 96.7, range TRUNCATE 1, range PERSIST 99.9, range WAL-pending 100, range WAL-checkpointed 94.2; secure_delete OFF, auto_vacuum INCR-idle: scattered TRUNCATE 95.2, scattered PERSIST 94, scattered WAL-pending 100, scattered WAL-checkpointed 98.9, range TRUNCATE 96.9, range PERSIST 100, range WAL-pending 100, range WAL-checkpointed 100; secure_delete OFF, auto_vacuum INCR-run: scattered TRUNCATE 91.6, scattered PERSIST 93.5, scattered WAL-pending 100, scattered WAL-checkpointed 96.4, range TRUNCATE 1.6, range PERSIST 83.8, range WAL-pending 100, range WAL-checkpointed 100; secure_delete FAST, auto_vacuum NONE: scattered TRUNCATE 0.7, scattered PERSIST 1.7, scattered WAL-pending 100, scattered WAL-checkpointed 67.2, range TRUNCATE 97.1, range PERSIST 100, range WAL-pending 100, range WAL-checkpointed 98.1; secure_delete FAST, auto_vacuum FULL: scattered TRUNCATE 0, scattered PERSIST 0.9, scattered WAL-pending 100, scattered WAL-checkpointed 67.4, range TRUNCATE 0, range PERSIST 100, range WAL-pending 100, range WAL-checkpointed 94.3; secure_delete FAST, auto_vacuum INCR-idle: scattered TRUNCATE 0.9, scattered PERSIST 1.7, scattered WAL-pending 99.9, scattered WAL-checkpointed 68.3, range TRUNCATE 96.4, range PERSIST 100, range WAL-pending 99.9, range WAL-checkpointed 100; secure_delete FAST, auto_vacuum INCR-run: scattered TRUNCATE 0, scattered PERSIST 0.4, scattered WAL-pending 99.9, scattered WAL-checkpointed 68, range TRUNCATE 0, range PERSIST 91.9, range WAL-pending 99.9, range WAL-checkpointed 96.7; secure_delete ON, auto_vacuum NONE: scattered TRUNCATE 0, scattered PERSIST 0.7, scattered WAL-pending 100, scattered WAL-checkpointed 64.7, range TRUNCATE 0, range PERSIST 100, range WAL-pending 100, range WAL-checkpointed 100; secure_delete ON, auto_vacuum FULL: scattered TRUNCATE 0, scattered PERSIST 0.6, scattered WAL-pending 100, scattered WAL-checkpointed 68, range TRUNCATE 0, range PERSIST 100, range WAL-pending 100, range WAL-checkpointed 100; secure_delete ON, auto_vacuum INCR-idle: scattered TRUNCATE 0, scattered PERSIST 0.5, scattered WAL-pending 100, scattered WAL-checkpointed 67.5, range TRUNCATE 0, range PERSIST 99.9, range WAL-pending 100, range WAL-checkpointed 98.9; secure_delete ON, auto_vacuum INCR-run: scattered TRUNCATE 0, scattered PERSIST 0, scattered WAL-pending 100, scattered WAL-checkpointed 69, range TRUNCATE 0, range PERSIST 5.8, range WAL-pending 100, range WAL-checkpointed 84.4.Scattered deletesTRPEWPWCRange deleteTRPEWPWCsd, vacuumOFF, none96961009897100100100OFF, full929110097110010094OFF, incr.95941009997100100100OFF, incr. run929410096284100100FAST, none12100679710010098FAST, full0110067010010094FAST, incr.121006896100100100FAST, incr. run001006809210097ON, none01100650100100100ON, full01100680100100100ON, incr.0010068010010099ON, incr. run00100690610084TR TRUNCATE, PE PERSIST, WP WAL pending,WC WAL checkpointed. Oracle, no later writes.
Figure 3: Mirror experiment: percent of deleted rows still fully present in the files (oracle) for every combination of secure_delete (sd), auto-vacuum and journal state, without later writes. Each cell pools five seeds (1,000 scattered or 2,500 range-deleted rows).

The journal state dominates. A row deleted while the log was pending survived in full in every one of the 360 pending-WAL databases, under every secure_delete and auto-vacuum setting and at every level of later writes: the main file still held it as a live row (97.6 percent of rows without later writes), and the log held its earlier page image (89.8 percent). After a checkpoint, 91.6 percent survived, most of them only in superseded log frames (60.1 percent of deleted rows). With TRUNCATE, only the main file's free space remained, and 32.2 percent survived; with PERSIST, the kept journal added the last transaction's pages and 73.4 percent survived. secure_delete ON with TRUNCATE left nothing in any of the eight configurations of auto-vacuum and pattern.

Then the settings. Without later writes and over all journal states, rows survived at 88.8 percent with secure_delete OFF, 73.4 with FAST and 60.6 with ON. FAST kept whole freed pages, so a range delete under FAST survived almost as under OFF (97.1 against 97.2 percent with no auto-vacuum and TRUNCATE), while scattered deletes, which free cells, not pages, left almost nothing (0.7 percent). Auto-vacuum FULL removed freed pages: under OFF and TRUNCATE, a range delete survived at 1.0 percent with FULL against 97.2 without. INCREMENTAL behaved like no auto-vacuum until incremental_vacuum ran, and like FULL after it had run (range delete under OFF and TRUNCATE: 96.9 and 1.6 percent). Range deletes survived better than scattered ones (79.9 against 60.1 percent).

Table 9: Mirror experiment: percent of deleted rows fully present (oracle), recovered by the free-space carver, and recovered by the carver plus the main-file differential, for each level of one factor, pooled over the others (no later writes, except in the last group).
Factor, levelPresentCarver+ diff.
secure_delete
OFF88.881.983.4
FAST73.468.571.7
ON60.655.358.8
Auto-vacuum
NONE82.376.279.2
FULL70.064.467.0
INCREMENTAL, idle82.376.979.4
INCREMENTAL, run62.656.759.6
Journal state
TRUNCATE32.229.529.5
PERSIST73.468.668.6
WAL, pending100.089.0100.0
WAL, checkpointed91.687.187.1
Deletion pattern
Scattered (200 rows)60.148.455.0
Range (500 rows)79.976.677.8
New text written after the deletion
0× the deleted text74.368.671.3
0.5× the deleted text65.056.362.3
2× the deleted text48.629.946.5

Later writes. New rows written after the deletion reduced survival from 74.3 percent with none to 65.0 and 48.6 percent with half and twice the deleted text (Figure 4). The pending log was the exception, because no checkpoint occurred in the experiment's small logs.

Mirror experiment: deleted rows present and recovered as new rows are written after the deletion, by journal stateLine chart. X axis: new text written after the deletion, 0, 0.5 and 2 times the deleted text. Y axis: percent of deleted rows, pooled over secure_delete, auto_vacuum, deletion pattern and seeds. Solid lines: fully present (oracle). Dashed lines: recovered by the content-blind reader (carver plus differential). TRUNCATE: present 32.2, 20.2, 8.9; recovered 29.5, 17.8, 6.8; PERSIST: present 73.4, 59.3, 49.8; recovered 68.6, 55.1, 45.8; WAL-pending: present 100, 100, 100; recovered 100, 100, 100; WAL-checkpointed: present 91.6, 80.5, 35.8; recovered 87.1, 76.4, 33.5.02550751000×0.5×2×new text written after the deletion% of deleted rowsTRUNCATEPERSISTWAL pendingWAL ckpt.presentrecovered
Figure 4: Effect of new rows written after the deletion, by journal state. Solid: rows fully present (oracle); dashed: rows recovered by the content-blind reader (carver plus differential). Pooled over secure_delete, auto-vacuum, deletion pattern and seeds.

What a content-blind reader recovers. The carver recovered 51.6 percent of deleted rows with all four values correct, against 62.6 percent present. The gap had two causes. Under OFF with TRUNCATE, rows in freeblocks were present at 95.6 percent but carved at 72.4: a freeblock header overwrites the first four bytes of the freed cell, and when the cell's length and key take fewer than four bytes, the start of the record header goes with them. With a pending log, the deleted rows were live in the main file, where a free-space carver does not look; the differential reader found all of them, and carver and differential together recovered 60.0 percent overall and 100 percent of the pending cases. Over all 1,440 databases, 851 carved records matched no row.

5.5 Configuration-implied ceilings (RQ4)

To join the census to the experiment, each deep-sampled database was given the experiment's configuration that matches its zeroing reading, auto-vacuum mode and journal state, and the rate at which that configuration kept a deleted row in full, for a row deleted just before acquisition with no later writes. Where the zeroing reading is ambiguous (ON or FAST; OFF or FAST) we give a low and a high rate. Databases without any freed cell or page (724, 1,262, 438 and 880 in the four groups) carry no zeroing evidence and are left out; read-only system databases are left out because they are not written on the device. The rate is averaged per device, and Figure 5 shows the median over devices.

For scattered single-row deletes the ambiguity hardly matters, and the implied ceiling is 41 and 27 percent on iOS 15 to 18 and iOS 26 and 31 and 35 percent on Android: on the median device, a recently deleted message row would remain in full in roughly a third of the databases, almost all of them through a pending log. For a range delete on iOS, the reading of freed pages decides: 47 and 32 percent if they were zeroed and 95 and 96 percent if they were kept. Section 5.2 found them kept in 669 of 684 databases where they could be tested, so the high reading is the one these files support. On Android, auto-vacuum FULL keeps the range ceiling at 37 to 43 percent. Among families, OEM or carrier apps on Android had the highest ceilings (63 and 68 percent), because they kept most of their databases in WAL mode with pending frames (Table 6).

Configuration-implied ceiling: expected percent of a just-deleted row still fully present, by OS group and deletion patternFor each deep-sampled database with zeroing evidence, the mirror rate of its configuration (zeroing reading, auto_vacuum mode, journal state) for a row deleted just before acquisition with no later writes; mean per device, median over devices. Where the zeroing reading is ambiguous (ON or FAST; OFF or FAST) the low rate is the solid bar and the high rate ends the lighter extension. iOS 15–18 scattered: median per-device mean 41 (low reading) to 41.4 (high reading), 1616 databases with evidence; iOS 15–18 range: median per-device mean 47.1 (low reading) to 95.2 (high reading), 1616 databases with evidence; iOS 26 scattered: median per-device mean 27.4 (low reading) to 28.3 (high reading), 3360 databases with evidence; iOS 26 range: median per-device mean 31.5 (low reading) to 96.1 (high reading), 3360 databases with evidence; Android 10–14 scattered: median per-device mean 31.3 (low reading) to 31.4 (high reading), 729 databases with evidence; Android 10–14 range: median per-device mean 37.1 (low reading) to 38.9 (high reading), 729 databases with evidence; Android 16 scattered: median per-device mean 35 (low reading) to 35.1 (high reading), 1277 databases with evidence; Android 16 range: median per-device mean 40.6 (low reading) to 43.1 (high reading), 1277 databases with evidence.low readingup to high reading0255075100implied percent of deleted rows fully presentiOS 15–18scattered41range47–95iOS 26scattered27–28range31–96Android 10–14scattered31range37–39Android 16scattered35range41–43
Figure 5: Configuration-implied ceiling for a row deleted just before acquisition: mirror rate of each database’s configuration, mean per device, median over devices. Light extension: low to high reading where zeroing is ambiguous.

6 Discussion

6.1 On these devices, zeroing is the rule and the log is the gap

The databases on these devices zero freed cells, and on Android they also remove freed pages. A recovery that searches only the free space of a main file therefore starts from little, and a negative result from such a search says little about whether a row existed. The places where deleted rows remain are the ones that zeroing does not reach: a main file that still holds the pages a pending log has changed, and log frames that hold earlier images of a page. The census found pending frames in a third to a half of the databases on the median device, and the experiment found every row deleted in that state.

6.2 What to acquire and report

Three practices follow. First, the -wal and -shm files belong to the evidence: without them, neither the pending state nor the superseded frames can be read. Second, a database should be read twice, with the log applied and from the main file alone, and rows present only in the main file reported as such; the Lab's earlier report found that this changes counts on most Screen Time stores [1], and here the same differential recovered every row deleted while the log was pending. Third, a statement about a deleted row should carry the database's configuration: auto-vacuum mode and journal state from the header and the log, and the zeroing reading of Listing 1 read as cells and pages. These are read from bytes alone, without decoding a row.

6.3 Platform differences

The Android result is what the platform's build flags predict [2], [3]. The iOS result is new as far as we found: cells zeroed and pages kept in nearly every database where both could be tested. Whether this comes from the system library's default or from settings of the frameworks that open the databases, the files cannot say. Its consequence can be stated: on iOS, a deletion that frees whole pages, such as the purge of an old conversation, can leave those pages intact, while a single message deleted from a page that stays in use is zeroed.

6.4 Relation to prior work

Recovery corpora and tools are built and compared on files whose settings the authors choose [10], [11], [12], [14]; the census suggests which configurations matter on current devices. Pawlaszczyk and Hummert report that unallocated space usually holds only zeros [12], which the census confirms at scale for freed cells. The ChatSecure study found that secure deletion defeated the tools tried [4]; the census shows that zeroing is the common case, not an application's choice, and the experiment shows which state still keeps deleted rows when it is on.

7 Threats to Validity

Internal validity. The reader, the census and the experiment were written by the same authors; the reader was checked against -shm frame counts and against known settings in the experiment, not against an independent implementation. The zeroing reading describes the end state of a file: a database written under different settings over time shows a mixture, and a freeblock whose deleted bytes were themselves zero would read as zeroed. Pending frames are read from -shm, whose backfill field a rare SQLite defect could set incorrectly [18]; walk and -shm agreed for 94 to 99.9 percent of log files. Two analyses were added after the first run (Section 4).

Construct validity. The experiment measures presence with an oracle that knows each deleted body, an upper bound for any reader; the carver measures one content-blind method. The implied ceiling assumes that a row was deleted just before acquisition with no later writes, maps a WAL database with an empty log to TRUNCATE, and leaves out databases without freed space, so it is a ceiling for recently deleted rows, not an estimate of what was deleted on a device. No checkpoint occurred within the experiment's small logs; on a phone, an automatic checkpoint at 100 or 1,000 pages [3], [18] or a closed connection ends the pending state.

External validity. The devices are research devices of one collection, not a sample of a population; 20 of the 33 Apple devices run iOS 26, and 12 of the 14 Android phones are Samsung. Families were assigned from container identifiers and package prefixes, which can misassign a database. The deep sample excluded databases or logs over 64 MiB and took at most 60 per family and device. The experiment used one schema, one page size, one deletion size per pattern and the SQLite library of a workstation, not the builds on the phones; the behaviour of each setting is documented as library-wide [16], but frameworks such as Core Data or Room may add their own deletion behaviour.

Conclusion validity and limitations. No hypothesis is tested; shares are given per device so that devices with many databases do not dominate. The census reads structure only, so it cannot say whether the free space holds deleted rows of interest. The iOS library build was not examined, and Android vendors other than Samsung are represented by two phones.

8 Conclusion and Future Work

We measured the SQLite settings and free structure of 120,678 databases on 47 iOS and Android devices without reading a row, and measured on 1,440 synthetic databases what each configuration leaves of a deleted row. On these devices, freed cells are zeroed on both platforms; Android also removes freed pages through auto-vacuum, while iOS keeps them. A third to a half of the databases on the median device held pending log frames, and in the experiment every row deleted in that state survived in full, whatever the other settings. The configuration of a database, read from its header, its log and the zeroing reading of its free space, should therefore accompany any statement about a deleted row, by examiners and by the tools, open-source parsers included, that report such rows. What remains open is why iOS files keep freed pages, and whether the pattern holds on devices and vendors not in this collection.

Future work, in order of importance: (1) a controlled deletion on a test device of each platform, to confirm the zeroing readings and the pending-log state on the device's own library; (2) more Android vendors and older iOS releases; (3) the experiment with the platform builds of SQLite and with Core Data and Room; (4) differential reading of main file and log in open-source parsers.

Artifact Availability

The structure reader, the mirror generator with both readers, the census runner and a generator of invented archives (standard library only) will be released in the Lab's public artifacts repository, with no release date set (code MIT, data and text CC BY 4.0); they hold no collection data. The package is not public yet, and the sentences that follow describe it as planned. The package reproduces the mirror experiment exactly (Tables 7 and 9, Figures 3 and 4, totals of Section 5.4; about 20 minutes). The census runner is exercised on invented archives only; the census configuration and the family prefix lists are not released, so the census values cannot be reproduced. The extractions cannot be released.

Data Handling Statement

The evidence for this study is drawn from the Lab's research corpus of mobile device extractions and was read only, together with synthetic databases built by the mirror experiment with invented rows (random text). From the extractions, only database headers, journal and log headers, and the free structure of a hashed sample of databases were read; no table row of a real database was decoded (the schema table was parsed only to locate the b-trees). The report gives aggregate counts and device classes. It contains no identifiers, personal data, message or file content, or case information. Only aggregate statistics, counts, OS versions, device classes and generic families are reported; no names, numbers, accounts, identifiers, application names, file paths of the collection, record content or per-device values appear in this paper or its figures.

Declaration of AI Assistance

The authors conceived the research questions and directed the work. The measurement code, the data checks and the first draft of this report were produced with AI assistance (Anthropic Claude). The authors reviewed the results and the text and are responsible for the content.

References

  1. Daniel Addai. 2026. Screen Time on iOS 15 to 27: Mostly Empty Usage Tables and Counts That Change Without the Write-Ahead Log. Lab Technical Report LAB-TR-0009, version 1.0. SectraTEK Lab, Prudential Associates. https://doi.org/10.5281/zenodo.23717933
  2. Android Open Source Project. 2026. platform/external/sqlite, dist/Android.bp, main branch. Retrieved October 27, 2026 from https://android.googlesource.com/platform/external/sqlite/+/refs/heads/main/dist/Android.bp (Compile flags -DSQLITE_SECURE_DELETE and -DSQLITE_DEFAULT_AUTOVACUUM=1 read 2026-10-27.)
  3. Android Open Source Project. 2026. platform/frameworks/base, core/res/res/values/config.xml, main branch. Retrieved October 27, 2026 from https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/res/res/values/config.xml (Values db_default_journal_mode TRUNCATE, db_wal_autocheckpoint 100 and db_journal_size_limit 524288 read 2026-10-27.)
  4. Cosimo Anglano, Massimo Canonico, and Marco Guazzone. 2016. Forensic analysis of the ChatSecure instant messaging application on Android smartphones. Digital Investigation 19 (2016), 44–59. https://doi.org/10.1016/j.diin.2016.10.001 (Read in the arXiv version, arXiv:1610.06721.)
  5. Apple. [n. d.]. Technical Q&A QA1809: New default journaling mode for Core Data SQLite stores in iOS 7 and OS X Mavericks. Apple Developer Documentation Archive. Retrieved October 27, 2026 from https://developer.apple.com/library/archive/qa/qa1809/_index.html
  6. Chromium Authors. 2026. third_party/sqlite/sqlite_common_configuration_flags.gni, main branch. Retrieved October 27, 2026 from https://chromium.googlesource.com/chromium/src/+/main/third_party/sqlite/sqlite_common_configuration_flags.gni
  7. Sangjun Jeon, Jewan Bang, Keunduck Byun, and Sangjin Lee. 2012. A recovery method of deleted record for SQLite database. Personal and Ubiquitous Computing 16, 6 (2012), 707–715. https://doi.org/10.1007/s00779-011-0428-7 (Abstract read; full text not read.)
  8. Sueun Jung and Jungheum Park. 2026. WALRUS: Reconstructing deleted and encrypted SQLite WAL pages from unallocated space. Forensic Science International: Digital Investigation (DFRWS APAC 2026). https://doi.org/10.1016/j.fsidi.2026.302188 (Bibliographic data from Crossref; volume and pages not confirmed; full text not read.)
  9. Seonghyeon Lee, Sooyoung Park, Insoo Lee, and Jongmoo Choi. 2025. A comprehensive analysis and evaluation of SQLite deleted record recovery techniques: a survey. Forensic Science International: Digital Investigation 55 (2025), 302031. https://doi.org/10.1016/j.fsidi.2025.302031 (Bibliographic data from Crossref; abstract not read.)
  10. Christian Meng and Harald Baier. 2019. bring2lite: A structural concept and tool for forensic data analysis and recovery of deleted SQLite records. Digital Investigation 29, Supplement (2019), S31–S41. https://doi.org/10.1016/j.diin.2019.04.017 (Abstract read; full text not read.)
  11. Sebastian Nemetz, Sven Schmitt, and Felix Freiling. 2018. A standardized corpus for SQLite database forensics. Digital Investigation 24, Supplement (2018), S121–S130. https://doi.org/10.1016/j.diin.2018.01.015 (Bibliographic data from Crossref; full text not read.)
  12. Dirk Pawlaszczyk and Christian Hummert. 2021. Making the invisible visible: techniques for recovering deleted SQLite data records. International Journal of Cyber Forensics and Advanced Threat Investigations 1, 1–3 (2021), 27–41. https://doi.org/10.46386/ijcfati.v1i1-3.17
  13. Paul Sanderson. 2018. SQLite Forensics. Independently published. ISBN 978-1980293071. (Not re-verified: details from a citing paper and retailer listings.)
  14. Sven Schmitt. 2018. Introducing anti-forensics to SQLite corpora and tool testing. In Proceedings of the 11th International Conference on IT Security Incident Management & IT Forensics (IMF 2018). IEEE, 89–106. https://doi.org/10.1109/IMF.2018.00014 (Bibliographic record read; full text not read.)
  15. SQLite. [n. d.]. Database File Format. Retrieved October 27, 2026 from https://www.sqlite.org/fileformat2.html
  16. SQLite. [n. d.]. Pragma statements supported by SQLite (secure_delete, auto_vacuum, journal_mode). Retrieved October 27, 2026 from https://www.sqlite.org/pragma.html
  17. SQLite. [n. d.]. Compile-time options (SQLITE_SECURE_DELETE). Retrieved October 27, 2026 from https://www.sqlite.org/compile.html
  18. SQLite. [n. d.]. Write-Ahead Logging. Retrieved October 27, 2026 from https://www.sqlite.org/wal.html

Cite and download

Cite as

Daniel Addai, Kingsley Albert Egyir, Sanders Larweh Tetteh, and Selorm Adjabeng. 2026. Zeroed Cells, Kept Pages, Pending Logs: What SQLite Leaves of a Deleted Row on 47 iOS and Android Devices. Lab Technical Report LAB-TR-0023, version 1.0. SectraTEK Lab, Prudential Associates. Preprint, not peer reviewed. https://lab.sectratek.com/research/reports/lab-tr-0023/

Citation guide
lab-tr-0023.bib
@techreport{addai2026labtr0023,
  author      = {Addai, Daniel and Egyir, Kingsley Albert and Tetteh, Sanders Larweh and Adjabeng, Selorm},
  title       = {Zeroed Cells, Kept Pages, Pending Logs: What SQLite
                 Leaves of a Deleted Row on 47 iOS and Android Devices},
  institution = {SectraTEK Lab, Prudential Associates},
  type        = {Lab Technical Report},
  number      = {LAB-TR-0023},
  year        = {2026},
  month       = oct,
  howpublished = {https://lab.sectratek.com/research/reports/lab-tr-0023/},
  note        = {Version 1.0, preprint, not peer reviewed. CC BY 4.0}
}

Licence: CC BY 4.0. The print layout of this page is set for US Letter in two columns.

Versions and corrections

VersionDateChange
1.02026-10-28First release.

Corrections: none so far. Errors can be reported through the corrections page.