{"record":{"id":"3ab4f991f817b562","repo":"transact-rs/sqlx","slug":"extension-entrypoint-names-passed-to-sqlite-must-n","errorCode":null,"errorMessage":"extension entrypoint names passed to SQLite must not contain nul bytes","messagePattern":"extension entrypoint 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":123,"sourceCode":"        }\n\n        let filename = CString::new(filename).map_err(|_| {\n            io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"filename passed to SQLite must not contain nul bytes\",\n            )\n        })?;\n\n        #[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","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/transact-rs/sqlx/blob/03af8bcc5711a1935580a54bea249c219a0c217d/sqlx-sqlite/src/connection/establish.rs#L105-L141","documentation":"When loading a SQLite extension, sqlx converts the entrypoint name into a `CString` (a NUL-terminated C string) to pass to the SQLite C API. Rust strings may legally contain interior NUL bytes (`\\0`), but C strings cannot, so `CString::new` fails. sqlx maps that failure to an `io::Error` of kind `InvalidData` rather than panicking.","triggerScenarios":"Calling `SqliteConnectOptions::extension` / `SqliteConnectOptions::extension_argument` with an entrypoint string containing an interior `\\0` byte (e.g. built from truncated buffers or C-side data), then connecting via `SqliteConnection::connect` / `AnyPool` which routes through `from_options` in establish.rs.","commonSituations":"Loading extension entrypoints read from binary sources, `String::from_utf8` of buffers that include NUL terminators, or dynamically constructed entrypoint names where a terminator was not stripped.","solutions":["Strip everything from the first NUL byte before passing the entrypoint: `entry.split('\\0').next().unwrap_or(\"\")`.","Validate the string with `entry.contains('\\0')` and return a clear application-level error before building `SqliteConnectOptions`.","If the bytes come from a C API, use `CStr::from_bytes_until_nul` (or `CStr::from_ptr`) and convert with `to_string_lossy` instead of treating the raw buffer as a Rust `String`."],"exampleFix":"// before\nlet entry = std::str::from_utf8(&buf).unwrap().to_string(); // may contain \\0\nopts = opts.extension(ext_name).with_argument(entry);\n\n// after\nlet entry = std::str::from_utf8(&buf)?.split('\\0').next().unwrap().to_string();\nopts = opts.extension(ext_name).with_argument(entry);","handlingStrategy":"validation","validationCode":"fn validate_entrypoint(entry: &str) -> Result<&str, String> {\n    if entry.contains('\\0') {\n        Err(format!(\"extension entrypoint contains NUL byte: {:?}\", entry))\n    } else {\n        Ok(entry)\n    }\n}\n// let entry = validate_entrypoint(&raw_entry)?;","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        // sanitize inputs and retry\n        ...\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always derive extension names/entrypoints from `CStr`/`OsStr` APIs, never from raw byte buffers.","Sanitize with `split('\\0').next()` at the config-parsing boundary.","Add a unit test asserting options construction fails fast on `\\0` inputs."],"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"}