{"record":{"id":"e376bfa7fdd18eff","repo":"transact-rs/sqlx","slug":"extension-names-passed-to-sqlite-must-not-contain","errorCode":null,"errorMessage":"extension names passed to SQLite must not contain nul bytes","messagePattern":"extension names passed to SQLite must not contain nul bytes","errorType":"exception","errorClass":"io::Error (InvalidData)","httpStatus":null,"severity":"error","filePath":"sqlx-sqlite/src/connection/establish.rs","lineNumber":132,"sourceCode":"        #[cfg(feature = \"load-extension\")]\n        let extensions = options\n            .extensions\n            .iter()\n            .map(|(name, entry)| {\n                let entry = entry\n                    .as_ref()\n                    .map(|e| {\n                        CString::new(e.as_bytes()).map_err(|_| {\n                            io::Error::new(\n                                io::ErrorKind::InvalidData,\n                                \"extension entrypoint names passed to SQLite must not contain nul bytes\"\n                            )\n                        })\n                    })\n                    .transpose()?;\n                Ok((\n                    CString::new(name.as_bytes()).map_err(|_| {\n                        io::Error::new(\n                            io::ErrorKind::InvalidData,\n                            \"extension names passed to SQLite must not contain nul bytes\",\n                        )\n                    })?,\n                    entry,\n                ))\n            })\n            .collect::<Result<IndexMap<CString, Option<CString>>, io::Error>>()?;\n\n        let thread_id = THREAD_ID.fetch_add(1, Ordering::AcqRel);\n\n        Ok(Self {\n            filename,\n            open_flags: flags,\n            busy_timeout: options.busy_timeout,\n            statement_cache_capacity: options.statement_cache_capacity,\n            log_settings: options.log_settings.clone(),\n            #[cfg(feature = \"load-extension\")]","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/transact-rs/sqlx/blob/03af8bcc5711a1935580a54bea249c219a0c217d/sqlx-sqlite/src/connection/establish.rs#L114-L150","documentation":"Identical in cause to the entrypoint case, but for the extension's *name*: sqlx converts each extension name into a `CString` for `sqlite3_load_extension`. A name containing an interior NUL byte cannot be represented as a C string, so the connection setup fails early with an `InvalidData` I/O error instead of calling into SQLite.","triggerScenarios":"Passing an extension name with an embedded `\\0` to `SqliteConnectOptions::extension` (or `extension_argument`'s name pair) and then establishing the connection, e.g. `SqliteConnectOptions::from_url(...)` + `connect`, which runs the mapping loop in `from_options`.","commonSituations":"Extension names read from raw byte buffers, filenames assembled from fixed-size C arrays that keep NUL padding, or config parsing that does not trim terminators.","solutions":["Sanitize the name: `name.split('\\0').next().unwrap_or(\"\")` before calling `.extension(...)`.","Pre-validate with `name.contains('\\0')` and reject the configuration at startup with your own error.","Convert C buffers with `CStr` APIs (`from_bytes_until_nul`) rather than `String::from_utf8` on raw bytes."],"exampleFix":"// before\nlet name = String::from_utf8_lossy(&raw_name).to_string(); // \"mod\\0pad\"\nlet opts = opts.extension(name);\n\n// after\nlet name = String::from_utf8_lossy(&raw_name).split('\\0').next().unwrap().to_string();\nlet opts = opts.extension(name);","handlingStrategy":"validation","validationCode":"fn validate_extension_name(name: &str) -> Result<&str, String> {\n    if name.contains('\\0') {\n        Err(format!(\"extension name contains NUL byte: {:?}\", name))\n    } else if name.is_empty() {\n        Err(\"extension name is empty\".into())\n    } else {\n        Ok(name)\n    }\n}\n// let name = validate_extension_name(&raw_name)?;","typeGuard":"fn is_cstring_safe(s: &str) -> bool {\n    !s.as_bytes().contains(&b'\\0')\n}","tryCatchPattern":"match SqliteConnection::connect_with(&opts).await {\n    Ok(conn) => conn,\n    Err(e) if e.to_string().contains(\"must not contain nul bytes\") => {\n        // strip NUL padding and rebuild options\n        ...\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Normalize names coming from C sources with `CStr::from_bytes_until_nul(...).to_string_lossy()`.","Validate all extension options once at startup, before building connection pools.","Keep extension config in UTF-8 text files/vars, not fixed-size binary buffers."],"tags":["rust","sqlx","sqlite","input-validation","nul-byte"],"backgroundTag":"interior-nul-byte-in-cstring","analyzedSha":"03af8bcc5711a1935580a54bea249c219a0c217d","analyzedAt":"2026-09-03T15:01:28.752Z","contentChangedAt":"2026-09-03T15:01:28.752Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}