Repository navigation
Description
This is one of a handful of bugs identified via fuzzing, as described in #277 and as requested there I am opening them as issues. This one I would also label as low priority.
A brand-new, schema-less database keyed with aes256cbc, legacy=1, switched to journal_mode=WAL, and then VACUUMed once, closes without error, but cannot be reopened with the identical passphrase. Every statement on the second connection fails SQLITE_NOTADB, including sqlite_dbpage reads of page 1 itself.
It is similar to #278 as it shares a similar symptom but reproduce through different mechanisms. #278 needs aes128cbc under the default rollback journal, with a specific nine-VACUUM, two-table sequence. This one needs aes256cbc under journal_mode=WAL, reproducing with a single bare VACUUM and no CREATE TABLE at all. Mixing up their configurations to try and find common denominators does not reproduce any error at all.
#include <stdio.h>
#include <string.h>
#include "sqlite3mc_amalgamation.h"
static const unsigned char PASSWORD[] = {
213, 7, 196, 255, 27, 150, 247, 25, 31, 96, 232, 195, 183, 169, 211, 101, 114, 94, 69, 18, 82,
231, 140, 76, 154, 42, 148, 226, 255, 57, 48, 93,
};
static void run_sql(sqlite3 *db, const char *sql) {
char *err = NULL;
int rc = sqlite3_exec(db, sql, NULL, NULL, &err);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL FAILED (%d): %s -- %s\n", rc, sql, err ? err : "?");
sqlite3_free(err);
}
}
static void configure(sqlite3 *db) {
int cipher = sqlite3mc_cipher_index("aes256cbc");
sqlite3mc_config(db, "cipher", cipher);
sqlite3mc_config_cipher(db, "aes256cbc", "legacy", 1);
sqlite3_key(db, PASSWORD, (int) sizeof(PASSWORD));
}
int main(void) {
remove("wal_repro.db");
remove("wal_repro.db-wal");
remove("wal_repro.db-shm");
sqlite3 *db;
sqlite3_open("wal_repro.db", &db);
configure(db);
run_sql(db, "PRAGMA journal_mode=WAL");
run_sql(db, "VACUUM");
sqlite3_close(db);
sqlite3 *db2;
sqlite3_open("wal_repro.db", &db2);
configure(db2);
char *err = NULL;
int rc = sqlite3_exec(db2, "PRAGMA integrity_check", NULL, NULL, &err);
printf("reopen integrity_check: rc=%d (%s) err=%s\n", rc, sqlite3_errstr(rc), err ? err : "none");
sqlite3_free(err);
sqlite3_close(db2);
return 0;
}$ cc -O0 -g -DSQLITE_TEMP_STORE=2 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_CORE \
-DHAVE_CIPHER_AES_128_CBC -DHAVE_CIPHER_AES_256_CBC -DHAVE_CIPHER_CHACHA20 -DHAVE_CIPHER_SQLCIPHER \
-DHAVE_CIPHER_RC4 -DHAVE_CIPHER_ASCON128 -DHAVE_CIPHER_AEGIS \
-o repro repro.c sqlite3mc_amalgamation.c -lpthread -ldl -lm
$ ./repro
reopen integrity_check: rc=26 (file is not a database) err=file is not a database
- Needs
journal_mode=WAL. The identical sequence under the default rollback journal never fails. - Needs
aes256cbcspecifically, andlegacy=1specifically. Neitheraes128cbc/chacha20norlegacy=0reproduce. mc_legacy_walis irrelevant. The defect reproduces identically whether it is explicitly0or1.- Needs exactly one
VACUUMand nothing else. NoCREATE TABLE, no transaction. - No
sqlite3_rekey, no cipher change, no passphrase change anywhere in the sequence.
Ciao,
Luca
Activity
utelle commentedon Oct 3, 2026
The reason for the failure is exactly the same as described for issue #278. It's the curse of the legacy mode.
BTW, I doubt that any database file encrypted with
aes128cbcoraes256cbcin legacy mode is practically in existence anymore nowadays. Back then when this format was replaced by the non-legacy mode there was no way to select the legacy mode other than using an older version of wxSQLite3. - The use of the cipher schemesaes128cbcandaes256cbcis strongly discouraged for new applications, not to mention legacy mode.As I see it the issue is not related to WAL journal mode. It could happen in all configurations. This time SQLite identifies a valid page size of 64 kB (because the bytes in the database file denote the value
x0001(= 64 KB)), but in reality the page size is 4 kB.As already said in my response in #278 the cipher schemes
sqlcipherandchacha20in legacy mode are also affected by this curse.Unfortunately, there is absolutely nothing that can be done to solve the issue, except for not using legacy mode.