Debian Patches

Status for libstring-compare-constanttime-perl/0.321-3

Patch Description Author Forwarded Bugs Origin Last update
CVE-2024-13939.patch [PATCH] Prevent from revealing a length of the secrect
Previously, equals() function short-cutted a code path if a lenght of
the secret and a lenght of the user-supplied data were different. As
a result, there was a timing side-channel revealing a length of the
secret.

This misfeature could be demonstrated with this code:

use String::Compare::ConstantTime ('equals');
use Benchmark;
timethese(1<<26, {
'0' => sub {equals(q{aaaa}, q{})},
'1' => sub {equals(q{aaaa}, q{a}x1)},
'2' => sub {equals(q{aaaa}, q{a}x2)},
'3' => sub {equals(q{aaaa}, q{a}x3)},
'4' => sub {equals(q{aaaa}, q{a}x4)},
'5' => sub {equals(q{aaaa}, q{a}x5)},
'6' => sub {equals(q{aaaa}, q{a}x6)},
'7' => sub {equals(q{aaaa}, q{a}x7)},
'8' => sub {equals(q{aaaa}, q{a}x8)},
});

The output was able to identify that the secret (first argument of
equals()) had 4 characters (observe the spike in "usr" value for
case #4):

Benchmark: timing 67108864 iterations of 0, 1, 2, 3, 4, 5, 6, 7, 8...
0: 2 wallclock secs ( 1.70 usr + 0.00 sys = 1.70 CPU) @ 39475802.35/s (n=67108864)
1: 4 wallclock secs ( 4.81 usr + 0.00 sys = 4.81 CPU) @ 13951946.78/s (n=67108864)
2: 5 wallclock secs ( 4.81 usr + 0.00 sys = 4.81 CPU) @ 13951946.78/s (n=67108864)
3: 5 wallclock secs ( 4.86 usr + 0.00 sys = 4.86 CPU) @ 13808408.23/s (n=67108864)
4: 5 wallclock secs ( 5.10 usr + 0.00 sys = 5.10 CPU) @ 13158600.78/s (n=67108864)
5: 4 wallclock secs ( 4.85 usr + 0.00 sys = 4.85 CPU) @ 13836879.18/s (n=67108864)
6: 5 wallclock secs ( 4.83 usr + 0.00 sys = 4.83 CPU) @ 13894174.74/s (n=67108864)
7: 5 wallclock secs ( 4.84 usr + 0.00 sys = 4.84 CPU) @ 13865467.77/s (n=67108864)
8: 5 wallclock secs ( 4.80 usr + 0.00 sys = 4.80 CPU) @ 13981013.33/s (n=67108864)

This misfeature was properly documented in the POD, yet it was
assigned CVE-2024-13939
<https://bugzilla.redhat.com/show_bug.cgi?id=2355663>.

There was a similar issue reported against and fixed in Mojolicous
<https://github.com/mojolicious/mojo/issues/1599>.

This patch fixes this misfeature in similar way. After applying the
fix the benchmark becomes proportional to a length of the user's input
if compiled with -O2 GCC option:

Benchmark: timing 67108864 iterations of 0, 1, 2, 3, 4, 5, 6, 7, 8...
0: 2 wallclock secs ( 1.75 usr + 0.00 sys = 1.75 CPU) @ 38347922.29/s (n=67108864)
1: 6 wallclock secs ( 5.01 usr + 0.00 sys = 5.01 CPU) @ 13394982.83/s (n=67108864)
2: 5 wallclock secs ( 5.00 usr + 0.00 sys = 5.00 CPU) @ 13421772.80/s (n=67108864)
3: 5 wallclock secs ( 5.05 usr + -0.01 sys = 5.04 CPU) @ 13315250.79/s (n=67108864)
4: 5 wallclock secs ( 5.08 usr + 0.00 sys = 5.08 CPU) @ 13210406.30/s (n=67108864)
5: 5 wallclock secs ( 5.11 usr + 0.00 sys = 5.11 CPU) @ 13132850.10/s (n=67108864)
6: 5 wallclock secs ( 5.11 usr + 0.00 sys = 5.11 CPU) @ 13132850.10/s (n=67108864)
7: 5 wallclock secs ( 5.12 usr + 0.00 sys = 5.12 CPU) @ 13107200.00/s (n=67108864)
8: 5 wallclock secs ( 5.12 usr + 0.00 sys = 5.12 CPU) @ 13107200.00/s (n=67108864)

Or inconlusive if compiled with -O3 (vertorized code):

Benchmark: timing 67108864 iterations of 0, 1, 2, 3, 4, 5, 6, 7, 8...
0: 2 wallclock secs ( 1.79 usr + 0.00 sys = 1.79 CPU) @ 37490985.47/s (n=67108864)
1: 5 wallclock secs ( 5.09 usr + 0.00 sys = 5.09 CPU) @ 13184452.65/s (n=67108864)
2: 5 wallclock secs ( 4.99 usr + 0.00 sys = 4.99 CPU) @ 13448670.14/s (n=67108864)
3: 5 wallclock secs ( 5.00 usr + 0.00 sys = 5.00 CPU) @ 13421772.80/s (n=67108864)
4: 4 wallclock secs ( 5.01 usr + 0.00 sys = 5.01 CPU) @ 13394982.83/s (n=67108864)
5: 6 wallclock secs ( 5.01 usr + 0.00 sys = 5.01 CPU) @ 13394982.83/s (n=67108864)
6: 5 wallclock secs ( 5.03 usr + 0.00 sys = 5.03 CPU) @ 13341722.47/s (n=67108864)
7: 5 wallclock secs ( 5.01 usr + 0.00 sys = 5.01 CPU) @ 13394982.83/s (n=67108864)
8: 5 wallclock secs ( 4.99 usr + 0.00 sys = 4.99 CPU) @ 13448670.14/s (n=67108864)

Implementation details: Originally, I called do_compare(b, b, b_len,
r) from equals(), but modern compilers (GCC 15) are too smart, and
optimized that call to a constant. So I moved the length comparison
inside do_compare(). That sufficed until -O3. Then the compiler
started to optimize data aliasing inside functions. The final
implementation replaced a ternary compare-and-assign with
a branch-less algebraic expression which works for me on x86_64 even
with -O3 optimization level.

When the compilers start recognizing these tricks and optimizing them
out too, we will need to disable the optimizations with
compiler-specific extensions. (__attribute__((noinline)),
__attribute__((noipa)), or a dummy asm("")). C.f.
<https://doi.org/10.48550/arXiv.2410.13489>
=?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com> no debian https://github.com/hoytech/String-Compare-ConstantTime/pull/21 2025-03-28

All known versions for source package 'libstring-compare-constanttime-perl'

Links