Skip to content

Commit 5066380

Browse files
authored
Merge pull request #665 from jamescater2/fix/pztail-aoor-followups
Follow-ups to pzTail bounds-check: funcptrs_win v3-span backfill + string-overload AOOR guard
2 parents f7ea47e + 73841a9 commit 5066380

3 files changed

Lines changed: 67 additions & 6 deletions

File tree

src/SQLitePCLRaw.core/raw.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,8 @@ static public int sqlite3_prepare_v2(sqlite3 db, string sql, out sqlite3_stmt st
830830
var ba = sql.to_utf8_with_z();
831831
var sp = new ReadOnlySpan<byte>(ba);
832832
int rc = sqlite3_prepare_v2(db, sp, out stmt, out var sp_tail);
833-
tail = utf8_span_to_string(sp_tail.Slice(0, sp_tail.Length - 1));
833+
// Audit #1: provider now returns an empty span on error; guard Slice(0, -1).
834+
tail = sp_tail.Length > 0 ? utf8_span_to_string(sp_tail.Slice(0, sp_tail.Length - 1)) : "";
834835
return rc;
835836
}
836837

@@ -874,7 +875,8 @@ static public int sqlite3_prepare_v3(sqlite3 db, string sql, uint flags, out sql
874875
var ba = sql.to_utf8_with_z();
875876
var sp = new ReadOnlySpan<byte>(ba);
876877
int rc = sqlite3_prepare_v3(db, sp, flags, out stmt, out var sp_tail);
877-
tail = utf8_span_to_string(sp_tail.Slice(0, sp_tail.Length - 1));
878+
// Audit #1: provider now returns an empty span on error; guard Slice(0, -1).
879+
tail = sp_tail.Length > 0 ? utf8_span_to_string(sp_tail.Slice(0, sp_tail.Length - 1)) : "";
878880
return rc;
879881
}
880882

src/SQLitePCLRaw.provider.e_sqlite3/Generated/provider_e_sqlite3_funcptrs_win.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,12 @@ unsafe int ISQLite3Provider.sqlite3_prepare_v3(sqlite3 db, ReadOnlySpan<byte> sq
304304
fixed (byte* p_sql = sql)
305305
{
306306
var rc = NativeMethods.sqlite3_prepare_v3(db, p_sql, sql.Length, flags, out stm, out var p_tail);
307-
var len_consumed = (int) (p_tail - p_sql);
308-
int len_remain = sql.Length - len_consumed;
309-
if (len_remain > 0)
307+
// Follow-up to 854eeb0: funcptrs_win's v3 span variant was missed in that commit — same p_tail bounds guard as the other three provider variants.
308+
if (p_tail != null && p_tail >= p_sql && p_tail <= p_sql + sql.Length)
310309
{
311-
tail = sql.Slice(len_consumed, len_remain);
310+
var len_consumed = (int) (p_tail - p_sql);
311+
int len_remain = sql.Length - len_consumed;
312+
tail = len_remain > 0 ? sql.Slice(len_consumed, len_remain) : ReadOnlySpan<byte>.Empty;
312313
}
313314
else
314315
{

src/common/tests_xunit.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,64 @@ public void test_prepare_v3_span_tolerates_uninitialised_pzTail()
433433
GC.KeepAlive(rolling);
434434
}
435435

436+
// Audit #1: the string overloads of prepare_v2/v3 used to throw
437+
// ArgumentOutOfRangeException on the same MISUSE path because they
438+
// unconditionally sliced the (now empty) tail span. These tests
439+
// assert they return rc cleanly with an empty tail string instead.
440+
[Fact]
441+
public void test_prepare_v2_string_tolerates_uninitialised_pzTail()
442+
{
443+
var rolling = new byte[256][];
444+
var rnd = new Random(42);
445+
for (var i = 0; i < 512; i++)
446+
{
447+
rolling[i % rolling.Length] = new byte[rnd.Next(1024, 128 * 1024)];
448+
449+
var db = ugly.open(":memory:");
450+
db.manual_close_v2();
451+
try
452+
{
453+
int rc = raw.sqlite3_prepare_v2(db, "SELECT 1;", out var stmt, out string tail);
454+
455+
Assert.Equal(raw.SQLITE_MISUSE, rc);
456+
Assert.Equal("", tail);
457+
stmt?.Dispose();
458+
}
459+
finally
460+
{
461+
db.Dispose();
462+
}
463+
}
464+
GC.KeepAlive(rolling);
465+
}
466+
467+
[Fact]
468+
public void test_prepare_v3_string_tolerates_uninitialised_pzTail()
469+
{
470+
var rolling = new byte[256][];
471+
var rnd = new Random(42);
472+
for (var i = 0; i < 512; i++)
473+
{
474+
rolling[i % rolling.Length] = new byte[rnd.Next(1024, 128 * 1024)];
475+
476+
var db = ugly.open(":memory:");
477+
db.manual_close_v2();
478+
try
479+
{
480+
int rc = raw.sqlite3_prepare_v3(db, "SELECT 1;", 0, out var stmt, out string tail);
481+
482+
Assert.Equal(raw.SQLITE_MISUSE, rc);
483+
Assert.Equal("", tail);
484+
stmt?.Dispose();
485+
}
486+
finally
487+
{
488+
db.Dispose();
489+
}
490+
}
491+
GC.KeepAlive(rolling);
492+
}
493+
436494
[Fact]
437495
public void test_bind_parameter_index()
438496
{

0 commit comments

Comments
 (0)