Commit e8fdb249b6

Stevie Hryciw <codroid@gmail.com>
2023-01-28 03:16:22
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
1 parent 7d90410
Changed files (2)
lib
std
lib/std/math/big/int.zig
@@ -489,6 +489,7 @@ pub const Mutable = struct {
             if (msl < req_limbs) {
                 r.limbs[msl] = 1;
                 r.len = req_limbs;
+                mem.set(Limb, r.limbs[msl + 1 .. req_limbs], 0);
             } else {
                 carry_truncated = true;
             }
lib/std/math/big/int_test.zig
@@ -1,4 +1,5 @@
 const std = @import("../../std.zig");
+const builtin = @import("builtin");
 const mem = std.mem;
 const testing = std.testing;
 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);
 }
 
+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" {
     var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
     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, limb_size + 1);
     try popCountTest(&a, limb_size * 2 + 1, limb_size + 2);
-    // TODO: These produce incorrect pop count for Mutable
-    // https://github.com/ziglang/zig/issues/13571
-    // try popCountTest(&a, limb_size * 2 + 2, limb_size + 3);
-    // try popCountTest(&a, limb_size * 2 + 3, limb_size + 4);
-    // try popCountTest(&a, limb_size * 2 + 4, limb_size + 5);
+    try popCountTest(&a, limb_size * 2 + 2, limb_size + 3);
+    try popCountTest(&a, limb_size * 2 + 3, limb_size + 4);
+    try popCountTest(&a, limb_size * 2 + 4, limb_size + 5);
+    try popCountTest(&a, limb_size * 4 + 2, limb_size * 3 + 3);
 }
 
 fn popCountTest(val: *const Managed, bit_count: usize, expected: usize) !void {