authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-11 19:17:01+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-11 19:17:01+03:00
log0bde5ce369ba59f7bc33e97cd5b03d69913108c5
treeb6a90bfd5bdc0f8c2a5edef506a2b234080c4b39
parentc5d4122684caba76718922f0c286969e8324e05b
parente56ba4cee1040eb0ca4d94e7e4dbfe88a12c171a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8330 from kivikakk/single-limb-bigint-overflow

bigint add failures with aliasing

2 files changed, 60 insertions(+), 7 deletions(-)

lib/std/math/big/int.zig+21-7
......@@ -316,7 +316,9 @@ pub const Mutable = struct {
316316 }
317317
318318 if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
319 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &r.limbs[0])) {
319 var o: Limb = undefined;
320 if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &o)) {
321 r.limbs[0] = o;
320322 r.len = 1;
321323 r.positive = a.positive;
322324 return;
......@@ -333,10 +335,10 @@ pub const Mutable = struct {
333335 }
334336 } else {
335337 if (a.limbs.len >= b.limbs.len) {
336 lladd(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]);
338 lladd(r.limbs[0..], a.limbs, b.limbs);
337339 r.normalize(a.limbs.len + 1);
338340 } else {
339 lladd(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]);
341 lladd(r.limbs[0..], b.limbs, a.limbs);
340342 r.normalize(b.limbs.len + 1);
341343 }
342344
......@@ -1683,12 +1685,14 @@ pub const Managed = struct {
16831685
16841686 /// r = a + scalar
16851687 ///
1686 /// r and a may be aliases.
1688 /// r and a may be aliases. If r aliases a, then caller must call
1689 /// `r.ensureAddScalarCapacity` prior to calling `add`.
16871690 /// scalar is a primitive integer type.
16881691 ///
16891692 /// Returns an error if memory could not be allocated.
16901693 pub fn addScalar(r: *Managed, a: Const, scalar: anytype) Allocator.Error!void {
1691 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
1694 assert((r.limbs.ptr != a.limbs.ptr) or r.limbs.len >= math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
1695 try r.ensureAddScalarCapacity(a, scalar);
16921696 var m = r.toMutable();
16931697 m.addScalar(a, scalar);
16941698 r.setMetadata(m.positive, m.len);
......@@ -1696,11 +1700,13 @@ pub const Managed = struct {
16961700
16971701 /// r = a + b
16981702 ///
1699 /// r, a and b may be aliases.
1703 /// r, a and b may be aliases. If r aliases a or b, then caller must call
1704 /// `r.ensureAddCapacity` prior to calling `add`.
17001705 ///
17011706 /// Returns an error if memory could not be allocated.
17021707 pub fn add(r: *Managed, a: Const, b: Const) Allocator.Error!void {
1703 try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
1708 assert((r.limbs.ptr != a.limbs.ptr and r.limbs.ptr != b.limbs.ptr) or r.limbs.len >= math.max(a.limbs.len, b.limbs.len) + 1);
1709 try r.ensureAddCapacity(a, b);
17041710 var m = r.toMutable();
17051711 m.add(a, b);
17061712 r.setMetadata(m.positive, m.len);
......@@ -1746,6 +1752,14 @@ pub const Managed = struct {
17461752 rma.setMetadata(m.positive, m.len);
17471753 }
17481754
1755 pub fn ensureAddScalarCapacity(r: *Managed, a: Const, scalar: anytype) !void {
1756 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
1757 }
1758
1759 pub fn ensureAddCapacity(r: *Managed, a: Const, b: Const) !void {
1760 try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
1761 }
1762
17491763 pub fn ensureMulCapacity(rma: *Managed, a: Const, b: Const) !void {
17501764 try rma.ensureCapacity(a.limbs.len + b.limbs.len + 1);
17511765 }
lib/std/math/big/int_test.zig+39
......@@ -538,6 +538,17 @@ test "big.int add sign" {
538538 try testing.expect((try a.to(i32)) == -3);
539539}
540540
541test "big.int add scalar" {
542 var a = try Managed.initSet(testing.allocator, 50);
543 defer a.deinit();
544
545 var b = try Managed.init(testing.allocator);
546 defer b.deinit();
547 try b.addScalar(a.toConst(), 5);
548
549 try testing.expect((try b.to(u32)) == 55);
550}
551
541552test "big.int sub single-single" {
542553 var a = try Managed.initSet(testing.allocator, 50);
543554 defer a.deinit();
......@@ -1562,3 +1573,31 @@ test "big.int pow" {
15621573 try testing.expectEqual(@as(i32, 1), try a.to(i32));
15631574 }
15641575}
1576
1577test "big.int regression test for 1 limb overflow with alias" {
1578 // Note these happen to be two consecutive Fibonacci sequence numbers, the
1579 // first two whose sum exceeds 2**64.
1580 var a = try Managed.initSet(testing.allocator, 7540113804746346429);
1581 defer a.deinit();
1582 var b = try Managed.initSet(testing.allocator, 12200160415121876738);
1583 defer b.deinit();
1584
1585 try a.ensureAddCapacity(a.toConst(), b.toConst());
1586 try a.add(a.toConst(), b.toConst());
1587
1588 try testing.expect(a.toConst().orderAgainstScalar(19740274219868223167) == .eq);
1589}
1590
1591test "big.int regression test for realloc with alias" {
1592 // Note these happen to be two consecutive Fibonacci sequence numbers, the
1593 // second of which is the first such number to exceed 2**192.
1594 var a = try Managed.initSet(testing.allocator, 5611500259351924431073312796924978741056961814867751431689);
1595 defer a.deinit();
1596 var b = try Managed.initSet(testing.allocator, 9079598147510263717870894449029933369491131786514446266146);
1597 defer b.deinit();
1598
1599 try a.ensureAddCapacity(a.toConst(), b.toConst());
1600 try a.add(a.toConst(), b.toConst());
1601
1602 try testing.expect(a.toConst().orderAgainstScalar(14691098406862188148944207245954912110548093601382197697835) == .eq);
1603}