diff options
| author | Frank Denis <124872+jedisct1@users.noreply.github.com> | 2025-09-18 04:54:15 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-17 19:54:15 -0700 |
| commit | 6dd0270a1926661e339d993a825fffa82be6bd51 (patch) | |
| tree | 6f7e7cfa8f35001fea399408cfa39b91c96ee1c9 /lib/std/sort | |
| parent | 4314c9653a874451d1c9b3dbb8be08d966344028 (diff) | |
| download | zig-6dd0270a1926661e339d993a825fffa82be6bd51.tar.gz zig-6dd0270a1926661e339d993a825fffa82be6bd51.zip | |
std.sort.pdq: fix out-of-bounds access in partialInsertionSort (#25253)
* std.sort.pdq: fix out-of-bounds access in partialInsertionSort
When sorting a sub-range that doesn't start at index 0, the
partialInsertionSort function could access indices below the range
start. The loop condition `while (j >= 1)` didn't respect the
arbitrary range boundaries [a, b).
This changes the condition to `while (j > a)` to ensure indices
never go below the range start, fixing the issue where pdqContext
would access out-of-bounds indices.
Fixes #25250
Diffstat (limited to 'lib/std/sort')
| -rw-r--r-- | lib/std/sort/pdq.zig | 49 |
1 files changed, 48 insertions, 1 deletions
diff --git a/lib/std/sort/pdq.zig b/lib/std/sort/pdq.zig index 55bd17ae93..e1f4f72136 100644 --- a/lib/std/sort/pdq.zig +++ b/lib/std/sort/pdq.zig @@ -227,7 +227,7 @@ fn partialInsertionSort(a: usize, b: usize, context: anytype) bool { // shift the smaller element to the left. if (i - a >= 2) { var j = i - 1; - while (j >= 1) : (j -= 1) { + while (j > a) : (j -= 1) { if (!context.lessThan(j, j - 1)) break; context.swap(j, j - 1); } @@ -328,3 +328,50 @@ fn reverseRange(a: usize, b: usize, context: anytype) void { j -= 1; } } + +test "pdqContext respects arbitrary range boundaries" { + // Regression test for issue #25250 + // pdqsort should never access indices outside the specified [a, b) range + var data: [2000]i32 = @splat(0); + + // Fill with data that triggers the partialInsertionSort path + for (0..data.len) |i| { + data[i] = @intCast(@mod(@as(i32, @intCast(i)) * 7, 100)); + } + + const TestContext = struct { + items: []i32, + range_start: usize, + range_end: usize, + + pub fn lessThan(ctx: @This(), a: usize, b: usize) bool { + // Assert indices are within the expected range + testing.expect(a >= ctx.range_start and a < ctx.range_end) catch @panic("index a out of range"); + testing.expect(b >= ctx.range_start and b < ctx.range_end) catch @panic("index b out of range"); + return ctx.items[a] < ctx.items[b]; + } + + pub fn swap(ctx: @This(), a: usize, b: usize) void { + // Assert indices are within the expected range + testing.expect(a >= ctx.range_start and a < ctx.range_end) catch @panic("index a out of range"); + testing.expect(b >= ctx.range_start and b < ctx.range_end) catch @panic("index b out of range"); + mem.swap(i32, &ctx.items[a], &ctx.items[b]); + } + }; + + // Test sorting a sub-range that doesn't start at 0 + const start = 1118; + const end = 1764; + const ctx = TestContext{ + .items = &data, + .range_start = start, + .range_end = end, + }; + + pdqContext(start, end, ctx); + + // Verify the range is sorted + for ((start + 1)..end) |i| { + try testing.expect(data[i - 1] <= data[i]); + } +} |
