Skip to content

bigint add failures with aliasing #8330

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 11, 2021
Merged

bigint add failures with aliasing #8330

merged 3 commits into from
Jun 11, 2021

Conversation

kivikakk
Copy link
Contributor

@kivikakk kivikakk commented Mar 22, 2021

A Discord user came to us with a failing use of bigint which was just calculating the Fibonacci sequence.

It turns out it was miscalculating on the jump between one and two limbs. See attached a test that fails currently, but should succeed. It is not written very elegantly, but it's easy to verify that it should work.

It looks like the offending code is this optimisation in Mutable.add:

if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) {
if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &r.limbs[0])) {
r.len = 1;
r.positive = a.positive;
return;
}
}

Note that, if @addWithOverflow does overflow, add continues on to perform Knuth's algorithm in ldadd, but it has also overwritten the contents of r.limbs[0] here. If r is aliased with one of its inputs, this causes the subsequent non-optimised calculation to fail.

I've confirmed the test passes if this is removed. We may want to do an alias check and skip the optimisation if overwriting r with garbage here will be a problem.

edit: I've added a suggested fix in case it's the right one, which skips this @addWithOverflow attempt if r.limbs.ptr is equal to a.limbs.ptr or b.limbs.ptr.

/cc @alexnask fyi

@kivikakk
Copy link
Contributor Author

CI green (modulo FreeBSD CI outage), should be good now.

@kivikakk kivikakk marked this pull request as draft March 23, 2021 11:54
@kivikakk
Copy link
Contributor Author

Our Discord friend has found another bug of the same class, so marking as a draft until that's fixed too. Seems to happen when we hit the 2192 limit, again only with aliasing.

@kivikakk
Copy link
Contributor Author

zig/lib/std/math/big/int.zig

Lines 1699 to 1707 in 749c3f0

/// r, a and b may be aliases.
///
/// Returns an error if memory could not be allocated.
pub fn add(r: *Managed, a: Const, b: Const) Allocator.Error!void {
try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1);
var m = r.toMutable();
m.add(a, b);
r.setMetadata(m.positive, m.len);
}

If r is aliased with a or b and r.ensureCapacity needs to realloc:

zig/lib/std/math/big/int.zig

Lines 1472 to 1477 in 749c3f0

pub fn ensureCapacity(self: *Managed, capacity: usize) !void {
if (capacity <= self.limbs.len) {
return;
}
self.limbs = try self.allocator.realloc(self.limbs, capacity);
}

The memory pointed to by the alias is no longer valid.

@kivikakk
Copy link
Contributor Author

Ref #6167 (comment).

@kivikakk kivikakk changed the title bigint add fails to carry correctly when both operands have one limb, the add overflows, and the output is aliased with one of them bigint add failures with aliasing Mar 24, 2021
@kivikakk kivikakk marked this pull request as ready for review March 24, 2021 09:21
@kivikakk
Copy link
Contributor Author

CI green again. add is now a lot more correct. Other functions probably still need work.

@kivikakk
Copy link
Contributor Author

kivikakk commented Jun 3, 2021

Rebased and brought back up to date.

@Vexu Vexu merged commit 0bde5ce into ziglang:master Jun 11, 2021
andrewrk pushed a commit that referenced this pull request Jun 11, 2021
@kivikakk kivikakk deleted the single-limb-bigint-overflow branch March 20, 2023 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants