LibCrypto: Avoid overly big allocs in intermediate ModularPower results

If we don't limit the sizes of the intermediate results, they will grow
indefinitely, causing each iteration to take longer and longer (in both
memcpy time, and algorithm runtime).
While calculating the trimmed length is fairly expensive, it's a small
cost to pay for uniform iteration times.
This commit is contained in:
AnotherTest 2021-04-01 12:02:14 +04:30 committed by Andreas Kling
parent 2020176f0f
commit 2601441486
Notes: sideshowbarker 2024-07-18 20:53:17 +09:00
3 changed files with 17 additions and 1 deletions

View file

@ -156,6 +156,13 @@ size_t UnsignedBigInteger::trimmed_length() const
return m_cached_trimmed_length.value();
}
void UnsignedBigInteger::clamp_to_trimmed_length()
{
auto length = trimmed_length();
if (m_words.size() > length)
m_words.resize(length);
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
@ -578,7 +585,7 @@ FLATTEN void UnsignedBigInteger::shift_left_without_allocation(
// output += (carry_word << temp_result.length())
// FIXME : Using temp_plus this way to transform carry_word into a bigint is not
// efficient nor pretty. Maybe we should have an "add_with_shift" method ?
// efficient nor pretty. Maybe we should have an "add_with_shift" method ?
temp_plus.set_to_0();
temp_plus.m_words.append(carry_word);
shift_left_by_n_words(temp_plus, temp_result.length(), temp_result);

View file

@ -81,6 +81,8 @@ public:
// The "trimmed length" is the number of words after trimming leading zeroed words
size_t trimmed_length() const;
void clamp_to_trimmed_length();
UnsignedBigInteger plus(const UnsignedBigInteger& other) const;
UnsignedBigInteger minus(const UnsignedBigInteger& other) const;
UnsignedBigInteger bitwise_or(const UnsignedBigInteger& other) const;

View file

@ -150,6 +150,13 @@ UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigIn
UnsignedBigInteger::multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
base.set_to(temp_remainder);
// Note that not clamping here would cause future calculations (multiply, specifically) to allocate even more unused space
// which would then persist through the temp bigints, and significantly slow down later loops.
// To avoid that, we can clamp to a specific max size, or just clamp to the min needed amount of space.
ep.clamp_to_trimmed_length();
exp.clamp_to_trimmed_length();
base.clamp_to_trimmed_length();
}
return exp;
}