std.math.big.int: Initialize limbs in addWrap

When a big.Int.Mutable had more than two limbs, it was possible for
this function to change the `len` field without zeroing limbs in the
active range. These uninitialized limbs would then be used in
`truncate()` and could cause invalid results.

Closes #13571
This commit is contained in:
Stevie Hryciw 2023-01-27 18:16:22 -08:00 committed by Andrew Kelley
parent 7d90410b96
commit e8fdb249b6
2 changed files with 33 additions and 5 deletions

View File

@ -489,6 +489,7 @@ pub const Mutable = struct {
if (msl < req_limbs) { if (msl < req_limbs) {
r.limbs[msl] = 1; r.limbs[msl] = 1;
r.len = req_limbs; r.len = req_limbs;
mem.set(Limb, r.limbs[msl + 1 .. req_limbs], 0);
} else { } else {
carry_truncated = true; carry_truncated = true;
} }

View File

@ -1,4 +1,5 @@
const std = @import("../../std.zig"); const std = @import("../../std.zig");
const builtin = @import("builtin");
const mem = std.mem; const mem = std.mem;
const testing = std.testing; const testing = std.testing;
const Managed = std.math.big.int.Managed; const Managed = std.math.big.int.Managed;
@ -2123,6 +2124,33 @@ test "big.int bitNotWrap signed multi" {
try testing.expect((try a.to(SignedDoubleLimb)) == -1); try testing.expect((try a.to(SignedDoubleLimb)) == -1);
} }
test "big.int bitNotWrap more than two limbs" {
// This test requires int sizes greater than 128 bits.
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
// LLVM: unexpected runtime library name: __umodei4
if (builtin.zig_backend == .stage2_llvm and comptime builtin.target.isWasm()) return error.SkipZigTest; // TODO
var a = try Managed.initSet(testing.allocator, maxInt(Limb));
defer a.deinit();
var res = try Managed.init(testing.allocator);
defer res.deinit();
const bits = @bitSizeOf(Limb) * 4 + 2;
try res.bitNotWrap(&a, .unsigned, bits);
const Unsigned = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = bits } });
try testing.expectEqual((try res.to(Unsigned)), ~@as(Unsigned, maxInt(Limb)));
try res.bitNotWrap(&a, .signed, bits);
const Signed = @Type(.{ .Int = .{ .signedness = .signed, .bits = bits } });
try testing.expectEqual((try res.to(Signed)), ~@as(Signed, maxInt(Limb)));
}
test "big.int bitwise and simple" { test "big.int bitwise and simple" {
var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
defer a.deinit(); defer a.deinit();
@ -2655,11 +2683,10 @@ test "big int popcount" {
try popCountTest(&a, limb_size * 2 - 1, limb_size); try popCountTest(&a, limb_size * 2 - 1, limb_size);
try popCountTest(&a, limb_size * 2, limb_size + 1); try popCountTest(&a, limb_size * 2, limb_size + 1);
try popCountTest(&a, limb_size * 2 + 1, limb_size + 2); try popCountTest(&a, limb_size * 2 + 1, limb_size + 2);
// TODO: These produce incorrect pop count for Mutable try popCountTest(&a, limb_size * 2 + 2, limb_size + 3);
// https://github.com/ziglang/zig/issues/13571 try popCountTest(&a, limb_size * 2 + 3, limb_size + 4);
// try popCountTest(&a, limb_size * 2 + 2, limb_size + 3); try popCountTest(&a, limb_size * 2 + 4, limb_size + 5);
// try popCountTest(&a, limb_size * 2 + 3, limb_size + 4); try popCountTest(&a, limb_size * 4 + 2, limb_size * 3 + 3);
// try popCountTest(&a, limb_size * 2 + 4, limb_size + 5);
} }
fn popCountTest(val: *const Managed, bit_count: usize, expected: usize) !void { fn popCountTest(val: *const Managed, bit_count: usize, expected: usize) !void {