From 7cf77fb35b9f5cf66e7f065ee57d552d34f909e0 Mon Sep 17 00:00:00 2001 From: Luca Muscat Date: Fri, 20 Jun 2025 10:46:32 +0200 Subject: [PATCH] Fix fuzz issue #1763 by using the `log2` & `log10` functions where applicable It is easy to chalk this fuzzer issue to erratic floating point behaviour, but this is not the case here. Currently, `exec_math_log` calculates log with arbitrary bases by using the following formula: `log_a(b) ~= ln(b) / ln(a)`. This calculation is an approximation with lots of its floating point precision lost to dividing the results of natural logarithms. By using the specialized versions of the log functions (`log2` & `log10`), we can avoid this loss of precision. SQLite also uses these specialized log functions when possible, so it doesn't hurt to do the same thing when aiming for parity. --- core/vdbe/execute.rs | 6 ++++++ tests/integration/fuzz/mod.rs | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 737c3d00a..6bc0c30f5 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -6133,6 +6133,12 @@ impl Value { None => 10.0, }; + if base == 2.0 { + return Value::Float(libm::log2(f)); + } else if base == 10.0 { + return Value::Float(libm::log10(f)); + }; + if f <= 0.0 || base <= 0.0 || base == 1.0 { return Value::Null; } diff --git a/tests/integration/fuzz/mod.rs b/tests/integration/fuzz/mod.rs index 863f7fdfa..484ac89da 100644 --- a/tests/integration/fuzz/mod.rs +++ b/tests/integration/fuzz/mod.rs @@ -24,6 +24,22 @@ mod tests { (rng, seed) } + /// [See this issue for more info](https://github.com/tursodatabase/limbo/issues/1763) + #[test] + pub fn fuzz_failure_issue_1763() { + let db = TempDatabase::new_empty(); + let limbo_conn = db.connect_limbo(); + let sqlite_conn = rusqlite::Connection::open_in_memory().unwrap(); + let offending_query = "SELECT ((ceil(pow((((2.0))), (-2.0 - -1.0) / log(0.5)))) - -2.0)"; + let limbo_result = limbo_exec_rows(&db, &limbo_conn, offending_query); + let sqlite_result = sqlite_exec_rows(&sqlite_conn, offending_query); + assert_eq!( + limbo_result, sqlite_result, + "query: {}, limbo: {:?}, sqlite: {:?}", + offending_query, limbo_result, sqlite_result + ); + } + #[test] pub fn arithmetic_expression_fuzz_ex1() { let db = TempDatabase::new_empty();