Remove the confusing extra word in BN_div
This extra word was allocated so that the fixup portion of quotient
estimation could read from wnump[-2] without checking if div_n > 1. This
was actually subtle because the value it got back was wrong. It just
didn't matter because the loop was a no-op.
As a result of all this, all the indices into snum were off, and the
remainder needed to be shifted down by one word to compensate.
Really, if div_n > 1, we could just call BN_div_word, but the calling
conventions are different enough that it didn't seem worth the effort.
Bug: 358687140
Change-Id: Id694a33003f51536ee836a5bdb75ff8006b11a51
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/70179
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/bn/div.c b/crypto/fipsmodule/bn/div.c
index 5d059e5..f1588c1 100644
--- a/crypto/fipsmodule/bn/div.c
+++ b/crypto/fipsmodule/bn/div.c
@@ -215,29 +215,22 @@
// This ensures, in Knuth's terminology, that v1 >= b/2, needed for the
// quotient estimation step.
int norm_shift = BN_BITS2 - (BN_num_bits(divisor) % BN_BITS2);
- if (!BN_lshift(sdiv, divisor, norm_shift)) {
+ if (!BN_lshift(sdiv, divisor, norm_shift) ||
+ !BN_lshift(snum, numerator, norm_shift)) {
goto err;
}
bn_set_minimal_width(sdiv);
- sdiv->neg = 0;
-
- // TODO(crbug.com/358687140): We also shift the numerator up by one extra
- // word. This was done for convenience so that |wnump[-2]| below always
- // exists, but makes the offsets confusing. Remove it.
- norm_shift += BN_BITS2;
- if (!BN_lshift(snum, numerator, norm_shift)) {
- goto err;
- }
bn_set_minimal_width(snum);
+ sdiv->neg = 0;
snum->neg = 0;
// Extend |snum| with zeros to satisfy the long division invariants:
- // - |snum|, minus the extra word, must have at least |div_n| + 1 words.
+ // - |snum| must have at least |div_n| + 1 words.
// - |snum|'s most significant word must be zero to guarantee the first loop
// iteration works with a prefix greater than |sdiv|. (This is the extra u0
// digit in Knuth step D1.)
int div_n = sdiv->width;
- int num_n = snum->width <= div_n + 1 ? div_n + 2 : snum->width + 1;
+ int num_n = snum->width <= div_n ? div_n + 1 : snum->width + 1;
if (!bn_resize_words(snum, num_n)) {
goto err;
}
@@ -267,10 +260,10 @@
// for later.
const int numerator_neg = numerator->neg;
res->neg = (numerator_neg ^ divisor->neg);
- if (!bn_wexpand(res, loop - 1)) {
+ if (!bn_wexpand(res, loop)) {
goto err;
}
- res->width = loop - 1;
+ res->width = loop;
if (!bn_wexpand(tmp, div_n + 1)) {
goto err;
@@ -281,10 +274,10 @@
// our index is reversed. Each loop iteration computes res->d[i] of the
// quotient and updates snum with the running remainder. Before each loop
// iteration, wnum <= sdiv.
- for (int i = loop - 2; i >= 0; i--, wnump--) {
+ for (int i = loop - 1; i >= 0; i--, wnump--) {
// TODO(crbug.com/358687140): Remove these running pointers.
wnum.d--;
- assert(wnum.d == snum->d + i + 1);
+ assert(wnum.d == snum->d + i);
assert(wnump == wnum.d + div_n);
// Knuth step D3: Compute q', an estimate of q = floor(wnum / sdiv) by
@@ -331,47 +324,43 @@
// 20, and 21. Although only one iteration is needed to correct q + 2 to
// q + 1, Knuth uses a loop. A loop will often also correct q + 1 to q,
// saving the slightly more expensive underflow handling below.
- //
- // TODO(crbug.com/358687140): This assumes div_n is at least 2, otherwise
- // there is no wnump[-2] (u_(j+2) in Knuth) term to read. We avoid going
- // out of bounds because of the extra word, and t2 will be zero in this
- // case, so the loop is a no-op. Still, this is confusing.
+ if (div_n > 1) {
#ifdef BN_ULLONG
- BN_ULLONG t2 = (BN_ULLONG)d1 * q;
- for (;;) {
- if (t2 <= ((((BN_ULLONG)rm) << BN_BITS2) | wnump[-2])) {
- break;
+ BN_ULLONG t2 = (BN_ULLONG)d1 * q;
+ for (;;) {
+ if (t2 <= ((((BN_ULLONG)rm) << BN_BITS2) | wnump[-2])) {
+ break;
+ }
+ q--;
+ rm += d0;
+ if (rm < d0) {
+ // If rm overflows, the true value exceeds BN_ULONG and the next
+ // t2 comparison should exit the loop.
+ break;
+ }
+ t2 -= d1;
}
- q--;
- rm += d0;
- if (rm < d0) {
- // If rm overflows, the true value exceeds BN_ULONG and the next
- // t2 comparison should exit the loop.
- break;
+#else // !BN_ULLONG
+ BN_ULONG t2l, t2h;
+ BN_UMULT_LOHI(t2l, t2h, d1, q);
+ for (;;) {
+ if (t2h < rm || (t2h == rm && t2l <= wnump[-2])) {
+ break;
+ }
+ q--;
+ rm += d0;
+ if (rm < d0) {
+ // If rm overflows, the true value exceeds BN_ULONG and the next
+ // t2 comparison should exit the loop.
+ break;
+ }
+ if (t2l < d1) {
+ t2h--;
+ }
+ t2l -= d1;
}
- t2 -= d1;
- }
-#else // !BN_ULLONG
- BN_ULONG t2l, t2h;
- BN_UMULT_LOHI(t2l, t2h, d1, q);
- for (;;) {
- if (t2h < rm ||
- (t2h == rm && t2l <= wnump[-2])) {
- break;
- }
- q--;
- rm += d0;
- if (rm < d0) {
- // If rm overflows, the true value exceeds BN_ULONG and the next
- // t2 comparison should exit the loop.
- break;
- }
- if (t2l < d1) {
- t2h--;
- }
- t2l -= d1;
- }
#endif // !BN_ULLONG
+ }
}
// Knuth step D4 through D6: Now q' = q or q' = q + 1, and