diff mbox series

[v2] block-sha1: remove use of assembly

Message ID 20220308022240.2809483-1-sandals@crustytoothpaste.net (mailing list archive)
State Accepted
Commit 544d93bc3b459f6e40526acdcf36a14c3d5dfec6
Headers show
Series [v2] block-sha1: remove use of assembly | expand

Commit Message

brian m. carlson March 8, 2022, 2:22 a.m. UTC
In the block SHA-1 code, we have special assembly code for i386 and
amd64 to perform rotations with assembly.  This is supposed to help pick
the correct rotation operation depending on which rotation is smaller,
which can help some systems perform slightly better, since any circular
rotation can be specified as either a rotate left or a rotate right.
However, this isn't needed, so we should remove it.

First, SHA-1, like SHA-2, uses fixed constant rotates.  Thus, all
rotation amounts are known at compile time and are in fact baked into
the code.  Fortunately, peephole optimizers recognize rotations
specified in the normal way and automatically emit the correct code,
including a preference for choosing a rotate left versus a rotate right.
This has been the case for well over a decade, and is a standard example
of the utility of a peephole optimizer.

Moreover, all modern CPUs, with the exception of extremely limited
embedded CPUs such as some Cortex-M processors, provide a barrel
shifter, which lets the CPU perform rotates of any bit amount in
constant time.  This is valuable for many cryptographic algorithms to
improve performance, and is required to prevent timing attacks in
algorithms which use data-dependent rotations (which don't include the
hash algorithms we use).  As a result, even though the compiler does the
correct optimization, it isn't even needed here and either a left or a
right rotate is equally acceptable.

In fact, the SHA-256 code already takes this into account and just
writes the simple code using an inline function to let the compiler
optimize it for us.

The downside of using this code, however, is that it uses a GCC
extension, which makes the compiler complain when using -pedantic unless
it's prefixed with __extension__.  We could fix that, but since it's
not needed, let's just remove it.  We haven't noticed this because
almost everyone uses the SHA1DC code instead, but it still shows up for
some people.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
Changes from v1:
* Add sign-off.

 block-sha1/sha1.c | 17 -----------------
 1 file changed, 17 deletions(-)

Comments

Ævar Arnfjörð Bjarmason March 8, 2022, 1:38 p.m. UTC | #1
On Tue, Mar 08 2022, brian m. carlson wrote:

I think the $subject of the patch needs updating. It's not removing all
the assemply from the file, after this patch we still have the
ARM-specific assembly.

I don't have a box to test that on, but I wonder if that also triggers
the pedantic mode?

Perhaps:

    block-sha1: remove superfluous i386 and x86-64 assembly

?
brian m. carlson March 9, 2022, 10:10 p.m. UTC | #2
On 2022-03-08 at 13:38:06, Ævar Arnfjörð Bjarmason wrote:
> 
> On Tue, Mar 08 2022, brian m. carlson wrote:
> 
> I think the $subject of the patch needs updating. It's not removing all
> the assemply from the file, after this patch we still have the
> ARM-specific assembly.
> 
> I don't have a box to test that on, but I wonder if that also triggers
> the pedantic mode?
> 
> Perhaps:
> 
>     block-sha1: remove superfluous i386 and x86-64 assembly

I suspect it has the same problem.  My inclination is to just remove it,
because my guess is that the compiler has gotten smarter between 2009
and now.

I honestly intend to just remove this code in a future version because
everyone not using SHA1DC has a security problem and we shouldn't offer
insecure options.

However, I think for now, I'm just going to reroll this with the new
title and then I can remove it in a future version unless somebody with
an ARM system can relatively quickly tell me whether it's necessary.
Taylor Blau March 9, 2022, 10:32 p.m. UTC | #3
On Wed, Mar 09, 2022 at 10:10:33PM +0000, brian m. carlson wrote:
> On 2022-03-08 at 13:38:06, Ævar Arnfjörð Bjarmason wrote:
> >
> > On Tue, Mar 08 2022, brian m. carlson wrote:
> >
> > I think the $subject of the patch needs updating. It's not removing all
> > the assemply from the file, after this patch we still have the
> > ARM-specific assembly.
> >
> > I don't have a box to test that on, but I wonder if that also triggers
> > the pedantic mode?
> >
> > Perhaps:
> >
> >     block-sha1: remove superfluous i386 and x86-64 assembly
>
> I suspect it has the same problem.  My inclination is to just remove it,
> because my guess is that the compiler has gotten smarter between 2009
> and now.

Almost certainly. I don't have a machine to test it on, either, but I
would be shocked if `make BLK_SHA=YesPlease DEVELOPER=1` worked on
master today on an arm machine.

> I honestly intend to just remove this code in a future version because
> everyone not using SHA1DC has a security problem and we shouldn't offer
> insecure options.
>
> However, I think for now, I'm just going to reroll this with the new
> title and then I can remove it in a future version unless somebody with
> an ARM system can relatively quickly tell me whether it's necessary.

I wonder if a good stop-gap for arm systems might be to do something
like:

--- 8< ---

diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
index 1bb6e7c069..7402d02875 100644
--- a/block-sha1/sha1.c
+++ b/block-sha1/sha1.c
@@ -57,7 +57,7 @@
 #if defined(__i386__) || defined(__x86_64__)
   #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
 #elif defined(__GNUC__) && defined(__arm__)
-  #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+  #define setW(x, val) do { W(x) = (val); __extension__ __asm__("":::"memory"); } while (0)
 #else
   #define setW(x, val) (W(x) = (val))
 #endif

--- >8 ---

in the meantime in a separate patch. There it seems like the memory
barrier is useful for machines with fewer than 25-ish registers. Though
obviously moot if your ultimate goal is to get rid of the block sha1
code.

But in the meantime, a stop-gap patch may be useful. If you use that
diff, feel free to forge my Signed-off-by.

Thanks,
Taylor
Ævar Arnfjörð Bjarmason March 9, 2022, 11:52 p.m. UTC | #4
On Wed, Mar 09 2022, Taylor Blau wrote:

> On Wed, Mar 09, 2022 at 10:10:33PM +0000, brian m. carlson wrote:
>> On 2022-03-08 at 13:38:06, Ævar Arnfjörð Bjarmason wrote:
>> >
>> > On Tue, Mar 08 2022, brian m. carlson wrote:
>> >
>> > I think the $subject of the patch needs updating. It's not removing all
>> > the assemply from the file, after this patch we still have the
>> > ARM-specific assembly.
>> >
>> > I don't have a box to test that on, but I wonder if that also triggers
>> > the pedantic mode?
>> >
>> > Perhaps:
>> >
>> >     block-sha1: remove superfluous i386 and x86-64 assembly
>>
>> I suspect it has the same problem.  My inclination is to just remove it,
>> because my guess is that the compiler has gotten smarter between 2009
>> and now.
>
> Almost certainly. I don't have a machine to test it on, either, but I
> would be shocked if `make BLK_SHA=YesPlease DEVELOPER=1` worked on
> master today on an arm machine.

Why is that? The -pedantic error is specifically about
"gnu-statement-expression", i.e. the bracket syntax, not the inline
assembly per-se.

The ARM assembly isn't using that, and we have other code __asm__ code
compiled with -pedantic. E.g. I get the __asm__ in "compat/bswap.h" by
default, and it passes -pedantic (the code starting around line 38).

>> I honestly intend to just remove this code in a future version because
>> everyone not using SHA1DC has a security problem and we shouldn't offer
>> insecure options.
>>
>> However, I think for now, I'm just going to reroll this with the new
>> title and then I can remove it in a future version unless somebody with
>> an ARM system can relatively quickly tell me whether it's necessary.
>
> I wonder if a good stop-gap for arm systems might be to do something
> like:
>
> --- 8< ---
>
> diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
> index 1bb6e7c069..7402d02875 100644
> --- a/block-sha1/sha1.c
> +++ b/block-sha1/sha1.c
> @@ -57,7 +57,7 @@
>  #if defined(__i386__) || defined(__x86_64__)
>    #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
>  #elif defined(__GNUC__) && defined(__arm__)
> -  #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
> +  #define setW(x, val) do { W(x) = (val); __extension__ __asm__("":::"memory"); } while (0)
>  #else
>    #define setW(x, val) (W(x) = (val))
>  #endif
>
> --- >8 ---

Isn't that __extension__ only needed *if* it warns under -pedantic,
which AFAICT doesn't apply to all uses of __asm__ (but your compiler
version etc. may be different...).
Carlo Marcelo Arenas Belón March 10, 2022, 1:59 a.m. UTC | #5
On Thu, Mar 10, 2022 at 12:52:31AM +0100, Ævar Arnfjörð Bjarmason wrote:
> 
> On Wed, Mar 09 2022, Taylor Blau wrote:
> 
> > On Wed, Mar 09, 2022 at 10:10:33PM +0000, brian m. carlson wrote:
> >> On 2022-03-08 at 13:38:06, Ævar Arnfjörð Bjarmason wrote:
> >> >
> >> > On Tue, Mar 08 2022, brian m. carlson wrote:
> >> >
> >> > I think the $subject of the patch needs updating. It's not removing all
> >> > the assemply from the file, after this patch we still have the
> >> > ARM-specific assembly.
> >> >
> >> > I don't have a box to test that on, but I wonder if that also triggers
> >> > the pedantic mode?
> >> >
> >> > Perhaps:
> >> >
> >> >     block-sha1: remove superfluous i386 and x86-64 assembly
> >>
> >> I suspect it has the same problem.  My inclination is to just remove it,
> >> because my guess is that the compiler has gotten smarter between 2009
> >> and now.
> >
> > Almost certainly. I don't have a machine to test it on, either, but I
> > would be shocked if `make BLK_SHA=YesPlease DEVELOPER=1` worked on
> > master today on an arm machine.
> 
> Why is that? The -pedantic error is specifically about
> "gnu-statement-expression", i.e. the bracket syntax, not the inline
> assembly per-se.

not sure how gcc version (as mentioned elsewhere) might affect this, but
had built it successfully in aarch64 with gcc 4.8.4, and arm32v6 with
gcc 10.3.1.

Carlo
Taylor Blau March 10, 2022, 2:34 a.m. UTC | #6
On Thu, Mar 10, 2022 at 12:52:31AM +0100, Ævar Arnfjörð Bjarmason wrote:
> >> I suspect it has the same problem.  My inclination is to just remove it,
> >> because my guess is that the compiler has gotten smarter between 2009
> >> and now.
> >
> > Almost certainly. I don't have a machine to test it on, either, but I
> > would be shocked if `make BLK_SHA=YesPlease DEVELOPER=1` worked on
> > master today on an arm machine.
>
> Why is that? The -pedantic error is specifically about
> "gnu-statement-expression", i.e. the bracket syntax, not the inline
> assembly per-se.
>
> The ARM assembly isn't using that, and we have other code __asm__ code
> compiled with -pedantic. E.g. I get the __asm__ in "compat/bswap.h" by
> default, and it passes -pedantic (the code starting around line 38).

You're right, I had this completely mixed up in my mind. In GitHub's
fork there is a spot I have been working near for the past couple of
days where there is inline assembly right below a statement expression.

The statement expression is what causes the -pedantic builds to fail,
not the inline __asm__. Indeed, if you just stick a memory barrier
anywhere in Git's codebase, we'll still compile under the DEVELOPER=1
builds.

> Isn't that __extension__ only needed *if* it warns under -pedantic,
> which AFAICT doesn't apply to all uses of __asm__ (but your compiler
> version etc. may be different...).

Yes, disregard that last suggestion :-).

Thanks,
Taylor
diff mbox series

Patch

diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
index 1bb6e7c069..5974cd7dd3 100644
--- a/block-sha1/sha1.c
+++ b/block-sha1/sha1.c
@@ -11,27 +11,10 @@ 
 
 #include "sha1.h"
 
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
-/*
- * Force usage of rol or ror by selecting the one with the smaller constant.
- * It _can_ generate slightly smaller code (a constant of 1 is special), but
- * perhaps more importantly it's possibly faster on any uarch that does a
- * rotate with a loop.
- */
-
-#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
-#define SHA_ROL(x,n)	SHA_ASM("rol", x, n)
-#define SHA_ROR(x,n)	SHA_ASM("ror", x, n)
-
-#else
-
 #define SHA_ROT(X,l,r)	(((X) << (l)) | ((X) >> (r)))
 #define SHA_ROL(X,n)	SHA_ROT(X,n,32-(n))
 #define SHA_ROR(X,n)	SHA_ROT(X,32-(n),n)
 
-#endif
-
 /*
  * If you have 32 registers or more, the compiler can (and should)
  * try to change the array[] accesses into registers. However, on