From edfd7658c3664ab6453a9ad4174d676c1090e489 Mon Sep 17 00:00:00 2001 From: gwenn Date: Tue, 29 Oct 2019 19:24:18 +0100 Subject: [PATCH] Segmentation fault on `prepare_cached` with an empty query With an empty query is prepared, sqlite3 returns no error but a null pointer. And then `sqlite3_sql` returns null. Which make `CStr::from_ptr` crash. --- src/cache.rs | 11 ++++++++++- src/inner_connection.rs | 12 ++++++++++-- src/lib.rs | 7 ++++++- src/raw_statement.rs | 12 ++++++++++-- src/statement.rs | 6 +++++- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index a6d1727..9f4bba7 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -135,9 +135,12 @@ impl StatementCache { // Return a statement to the cache. fn cache_stmt(&self, stmt: RawStatement) { + if stmt.is_null() { + return; + } let mut cache = self.0.borrow_mut(); stmt.clear_bindings(); - let sql = String::from_utf8_lossy(stmt.sql().to_bytes()) + let sql = String::from_utf8_lossy(stmt.sql().unwrap().to_bytes()) .trim() .to_string(); cache.insert(sql, stmt); @@ -339,4 +342,10 @@ mod test { } assert_eq!(1, cache.len()); } + + #[test] + fn test_empty_stmt() { + let conn = Connection::open_in_memory().unwrap(); + conn.prepare_cached("").unwrap(); + } } diff --git a/src/inner_connection.rs b/src/inner_connection.rs index e48623d..90d985e 100644 --- a/src/inner_connection.rs +++ b/src/inner_connection.rs @@ -90,9 +90,17 @@ impl InnerConnection { } else { let mut e = error_from_handle(db, r); if let Error::SqliteFailure( - ffi::Error{code: ffi::ErrorCode::CannotOpen, extended_code: _}, Some(msg)) = e { + ffi::Error { + code: ffi::ErrorCode::CannotOpen, + extended_code: _, + }, + Some(msg), + ) = e + { e = Error::SqliteFailure( - ffi::Error::new(r), Some(format!("{}: {}", msg, c_path.to_string_lossy()))); + ffi::Error::new(r), + Some(format!("{}: {}", msg, c_path.to_string_lossy())), + ); } ffi::sqlite3_close(db); e diff --git a/src/lib.rs b/src/lib.rs index ed74003..f2363be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -966,7 +966,12 @@ mod test { if let Error::SqliteFailure(e, Some(msg)) = err { assert_eq!(ErrorCode::CannotOpen, e.code); assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code); - assert!(msg.contains(filename), "error message '{}' does not contain '{}'", msg, filename); + assert!( + msg.contains(filename), + "error message '{}' does not contain '{}'", + msg, + filename + ); } else { panic!("SqliteFailure expected"); } diff --git a/src/raw_statement.rs b/src/raw_statement.rs index 1a7a8e7..67d2a67 100644 --- a/src/raw_statement.rs +++ b/src/raw_statement.rs @@ -14,6 +14,10 @@ impl RawStatement { RawStatement(stmt, tail) } + pub fn is_null(&self) -> bool { + self.0.is_null() + } + pub unsafe fn ptr(&self) -> *mut ffi::sqlite3_stmt { self.0 } @@ -95,8 +99,12 @@ impl RawStatement { unsafe { ffi::sqlite3_clear_bindings(self.0) } } - pub fn sql(&self) -> &CStr { - unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) } + pub fn sql(&self) -> Option<&CStr> { + if self.0.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(ffi::sqlite3_sql(self.0)) }) + } } pub fn finalize(mut self) -> c_int { diff --git a/src/statement.rs b/src/statement.rs index 8ba1cfa..785548a 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -625,7 +625,11 @@ impl Into for Statement<'_> { impl fmt::Debug for Statement<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let sql = str::from_utf8(self.stmt.sql().to_bytes()); + let sql = if self.stmt.is_null() { + Ok("") + } else { + str::from_utf8(self.stmt.sql().unwrap().to_bytes()) + }; f.debug_struct("Statement") .field("conn", self.conn) .field("stmt", &self.stmt)