Thread (12 messages) flat view 12 messages, 6 authors, 2017-06-02

Re: Unaligned accesses in sha1dc

From: Martin Ågren <hidden>
Date: 2017-06-02 09:50:05

Possibly related (same subject, not in this thread)

On 2 June 2017 at 10:51, Ævar Arnfjörð Bjarmason [off-list ref] wrote:
On Fri, Jun 2, 2017 at 2:15 AM, Junio C Hamano [off-list ref] wrote:
quoted
Martin Ågren [off-list ref] writes:
quoted
I looked into this some more. It turns out it is possible to trigger
undefined behavior on "next". Here's what I did:
...

This "fixes" the problem:
...
diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c
index 3dff80a..d6f4c44 100644
--- a/sha1dc/sha1.c
+++ b/sha1dc/sha1.c
@@ -66,9 +66,9 @@
...
With this diff, various tests which seem relevant for SHA-1 pass,
including t0013, and the UBSan-error is gone. The second diff is just
a monkey-patch. I have no reason to believe I will be able to come up
with a proper and complete patch for sha1dc. And I guess such a thing
would not really be Git's patch to carry, either. But at least Git
could consider whether to keep relying on undefined behavior or not.

There's a fair chance I've mangled the whitespace. I'm using gmail's
web interface... Sorry about that.
Thanks.  I see Marc Stevens is CC'ed in the thread, so I'd expect
that the final "fix" would come from his sha1collisiondetection
repository via Ævar.

In the meantime, I am wondering if it makes sense to merge the
earlier update with #ifdef ALLOW_UNALIGNED_ACCESS and #ifdef
SHA1DC_FORCE_LITTLEENDIAN for the v2.13.x maintenance track, which
would at least unblock those on platforms v2.13.0 did not work
correctly at all.

Ævar, thoughts?
I think we're mixing up several things here, which need to be untangled:

1) The sha1dc works just fine on most platforms even with undefined
behavior, as evidenced by 2.13.0 working.
Right, with "platform" meaning "combination of hardware-architecture
and compiler". Nothing can be said about how the current code behaves
on "x86". Such statements can only be made with regard to "x86 and
this or that compiler". Even then, short of studying the compiler
implementation/documentation in detail, one cannot be certain that
seemingly unrelated changes in Git don't make the code do something
else entirely.
2) There was a bug in practice with unaligned access on SPARC. It's
not clear to me whether anyone (Andreas, Liam?) still has any issues
in practice on any platform without specifying compile flags like what
Martin Ågren suggested above.
True.
3) Now we have another issue reported by Martin Ågren here, which is
that while the code works in practice on most platforms it's using
undefined behavior.
...
I think that this is definitely something worth looking into /
coordinating with upstream, but I haven't seen anything to suggest
that we need to be rushing to get a patch in to fix this given 1) and
nobody saying yet that 2) doesn't solve their issue as long as they're
not supplying some -fsanitize=* flags.

Now, stepping a bit back from this whole thing: I didn't read the
entire discussion back in February when sha1dc was integrated, but I
really don't see given all this churn / bug reporting we're getting
now why another acceptable solution wouldn't be to just revert
e6b07da278 ("Makefile: make DC_SHA1 the default", 2017-03-17) &
release 2.13.1 with that.

Clearly there are outstanding issues with it, and needing to do a
memcpy() as my `next` patch does will harm performance on some
platforms, and something like Martin's patch on top will slow it down
even more.
The only thing in my second "patch" which could possibly affect
performance as I see it would be the call to memcpy(.. ,.. ,4),
including pointer-calculation. Focusing on x86, I would not say that
it "will" slow it down until I'd measured performance. I wouldn't even
rule out that the compiled assembler could be identical. I would just
say the patch "would most likely slow it down even more on some
architectures, with some compilers and/or with some
compiler-settings".

If undefined behavior is avoided with memcpy(.., .., 4) then there
should be no formal need for your "big" memcpy where things are copied
into a known-to-be-aligned buffer. The behavior will be defined on all
architectures, anyway. Then your memcpy would simply be part of an
optimization to prefer one big memcpy and many loads instead of many
small memcpy-calls. On some architectures, that might be a very good
optimization. But on others, if the small memcpy is compiled to a
simple load, then I believe such an optimization would most likely be
a slow-down (modulo crazy-clever compiler optimizations).
It seems to me that we should give it more time to cook, and better
understand the various trade-offs involved. The shattered attack is
very unlikely to impact anything in practice, and users who are
paranoid about it can opt-in to this extra protection.
Regarding reverting and cooking, I don't feel like I'm in a position
to express an opinion. Thanks for thinking about this undefined
behavior, though, and I hope I'm contributing in some way, although
I'm aware I'm just standing at the side-line, waving my hands, and not
contributing any actual code.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help