-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQLite: Spurrious SEGFAULTs while reading Strings #634
Comments
While I haven't been able to reproduce this bug locally, while looking through sqlx's code I did find a code that seemed pretty unsound to me: // src/sqlite/value.rs:88
impl<'r> SqliteValueRef<'r> {
pub(super) fn text(&self) -> Result<&'r str, BoxDynError> {
match self.0 {
SqliteValueData::Statement {
statement, index, ..
} => statement.column_text(index),
SqliteValueData::Value(v) => v.text(),
}
}
} ... this code invokes // src/sqlite/statement/handle.rs:263
impl StatementHandle {
pub(crate) fn column_blob(&self, index: usize) -> &[u8] {
/* ... */
let ptr = unsafe { sqlite3_column_blob(self.0.as_ptr(), index) } as *const u8;
/* ... */
}
} The catch here lays in the fact that, according to SQLite's docs, the pointer returned from |
I'm not sure if this is a load issue or what, since you'd think "reading strings causes segfaults" would have been noticed by now ;) |
I'm reasonably sure how we handle The lifetime I appreciate the minimal example you provided. I'll see if I can't dig into that soon. |
@mehcode I'm quite sure lifetimes are wrong though (or at least inflating isn't invoked sufficiently enough) - here's a simple PoC showing this bug: use sqlx::{Connection, Decode, Row};
use tokio::stream::StreamExt;
const TEXT: &str = "
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor \
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis \
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore \
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt \
in culpa qui officia deserunt mollit anim id est laborum.
";
#[tokio::main]
async fn main() {
let mut db = sqlx::SqliteConnection::connect(":memory:")
.await
.unwrap();
sqlx::query("CREATE TABLE logs (log TEXT NOT NULL)")
.execute(&mut db)
.await
.unwrap();
sqlx::query(&format!("INSERT INTO logs VALUES ('{}')", TEXT))
.execute(&mut db)
.await
.unwrap();
let mut rows_a = sqlx::query("SELECT * FROM logs").fetch(&mut db);
let row1 = rows_a.try_next().await.unwrap().unwrap();
let value1 = row1.try_get_raw(0).unwrap();
let value1: &str = Decode::decode(value1).unwrap();
drop(rows_a);
let mut rows_b = sqlx::query("SELECT * FROM logs").fetch(&mut db);
let row2 = rows_b.try_next().await.unwrap().unwrap();
let value2 = row2.try_get_raw(0).unwrap();
let value2: &str = Decode::decode(value2).unwrap();
println!("{}", value1);
println!("{}", value2);
} In my case, it randomly prints something like:
Edit: the bug is gone when you execute two different queries though - following code doesn't trigger the use-after-free (I think) anymore: /* ... */
let mut rows_a = sqlx::query("SELECT * FROM logs WHERE 1=1").fetch(&mut db);
/* ... */
let mut rows_b = sqlx::query("SELECT * FROM logs WHERE 2=2").fetch(&mut db);
/* ... */ |
You're not wrong, but you're also showcasing a different bug there. The The real problem here is a lot of what's done in SQLite here is an experiment to remove the lifetime on Row, which we had in 0.3, which kept it sound easily. The problem with that approach is it broke the type system because Rust has neither GATs nor lazy normalization. Fixing this by threading the lifetime correctly in the current design requires a nested HRTB with type parameters. Your example is easily "fixed" by removing the unsound impl of |
Okie, got it - just thought it might be related :-) |
Definitely related, if only tangentially. I'm just providing context. Honestly, looking into this issue makes me think we really need to figure out how to have |
Do you mind trying the changes in #1551 to see if this segfault still happens? I got rid of the use of |
Hi there, I'm running
0.4.0-beta.1
(actually, this branch of it to fix the CPU issue). I've been doing some throughput testing, but have been getting a decent amount of segfaults. Sometimes these just crash the server without giving any trace, and sometimes they give messages like this:The contents of my DB string fields are definitely ASCII, so I'm not sure what's happening here.
My code is available here, where I'm combining Tide and sqlx.
The single row in my database is just:
The text was updated successfully, but these errors were encountered: