Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8e464c4 breaks clang 12 -O2 (make test failed) #4786

Closed
Benau opened this issue Jul 18, 2021 · 13 comments · Fixed by #4941, #4947 or #4948
Closed

8e464c4 breaks clang 12 -O2 (make test failed) #4786

Benau opened this issue Jul 18, 2021 · 13 comments · Fixed by #4941, #4947 or #4948
Labels
bug component-crypto Crypto primitives and low-level interfaces

Comments

@Benau
Copy link

Benau commented Jul 18, 2021

Summary

seems that 8e464c4 (bisected) confused clang 12 when you compile with -O2....

System information

Mbed TLS version (number or commit id): 3.0.0 or 2.27 branch
Operating system and version: archlinux x86_64 with clang 12, happens with llvm-mingw (windows cross-compiler) too
Configuration (if not default, please attach mbedtls_config.h): default
Compiler and options (if you used a pre-built binary, please indicate how you obtained it):
Additional environment information:

Expected behavior

make test succeeded

Actual behavior

make test failed

Steps to reproduce

CC=clang cmake .. -DCMAKE_BUILD_TYPE=Release
make -j14
make -j14 test

Additional information

@gilles-peskine-arm
Copy link
Contributor

gilles-peskine-arm commented Jul 18, 2021

I can reproduce the failure with Ubuntu clang version 12.0.1-++20210630032618+fed41342a82f-1exp120210630133332.127 from the LLVM apt repository, with Ubuntu clang version 12.0.0-1ubuntu1 from Ubuntu 21.04, and withUbuntu clang version 13.0.0-++20210717052618+5df48493f089-1exp120210717153411.478 from the LLVM apt repository, all on x86_64 and compiling with -O2. I can't reproduce it with Clang 11, with -O1 or for x86_32.

Thanks for doing the bisection. I'm staring at this patch and I really can't see how it might cause undefined behavior. So this is looking like a compiler bug.

Based on the logs from test_suite_mpi, it seems that mpi_mul_hlp works correctly when called from mbedtls_mpi_mul_mpi or mbedtls_mpi_mul_int, but not when called by mpi_montmul, so exponentiation calculates incorrect result (and then of course so do things that depend on it).

Base test mbedtls_mpi_exp_mod #1 .................................. FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Base test mbedtls_mpi_exp_mod #2 (Even N) ......................... PASS
Base test mbedtls_mpi_exp_mod #2 (N = 0 (null)) ................... PASS
Base test mbedtls_mpi_exp_mod #3 (Negative N) ..................... PASS
Base test mbedtls_mpi_exp_mod #4 (Negative base) .................. FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Base test mbedtls_mpi_exp_mod #5 (Negative exponent) .............. PASS
Base test mbedtls_mpi_exp_mod #6 (Negative base + exponent) ....... PASS
Test mbedtls_mpi_exp_mod: 0 (null) ^ 0 (null) mod 9 ............... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 0 (null) ^ 0 (1 limb) mod 9 ............. FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 0 (null) ^ 1 mod 9 ...................... PASS
Test mbedtls_mpi_exp_mod: 0 (null) ^ 2 mod 9 ...................... PASS
Test mbedtls_mpi_exp_mod: 0 (1 limb) ^ 0 (null) mod 9 ............. FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 0 (1 limb) ^ 0 (1 limb) mod 9 ........... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 0 (1 limb) ^ 1 mod 9 .................... PASS
Test mbedtls_mpi_exp_mod: 0 (1 limb) ^ 2 mod 9 .................... PASS
Test mbedtls_mpi_exp_mod: 1 ^ 0 (null) mod 9 ...................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 4 ^ 0 (null) mod 9 ...................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 10 ^ 0 (null) mod 9 ..................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 1 ^ 0 (1 limb) mod 9 .................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 4 ^ 0 (1 limb) mod 9 .................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: 10 ^ 0 (1 limb) mod 9 ................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod: MAX_SIZE exponent ....................... PASS
Test mbedtls_mpi_exp_mod: MAX_SIZE + 1 exponent ................... PASS
Test mbedtls_mpi_exp_mod: MAX_SIZE modulus ........................ PASS
Test mbedtls_mpi_exp_mod: MAX_SIZE + 1 modulus .................... PASS
Test mbedtls_mpi_exp_mod: MAX_SIZE exponent and modulus ........... PASS
Test mbedtls_mpi_exp_mod: MAX_SIZE + 1 exponent and modulus ....... PASS
Test mbedtls_mpi_exp_mod #1 ....................................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod (Negative base) [#1] ..................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_exp_mod (Negative base) [#2] ..................... FAILED
  mbedtls_mpi_cmp_mpi( &Z, &X ) == 0
  at line 1009, ../source/tests/suites/test_suite_mpi.function
…
Base test mbedtls_mpi_is_prime #1 ................................. PASS
Base test mbedtls_mpi_is_prime #2 ................................. PASS
Base test mbedtls_mpi_is_prime #3 ................................. PASS
Base test mbedtls_mpi_is_prime #4 ................................. PASS
Base test mbedtls_mpi_is_prime #5 ................................. PASS
Base test mbedtls_mpi_is_prime #6 ................................. PASS
Base test mbedtls_mpi_is_prime #7 ................................. PASS
Base test mbedtls_mpi_is_prime #8 ................................. PASS
Test mbedtls_mpi_is_prime #1a ..................................... PASS
Test mbedtls_mpi_is_prime #1b ..................................... PASS
Test mbedtls_mpi_is_prime #2a ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #2b ..................................... PASS
Test mbedtls_mpi_is_prime #3 ...................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #4 ...................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #5 [#1] ................................. FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #5 [#2] ................................. FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #6 ...................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #7 ...................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #8 ...................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #9 ...................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #10 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #11 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #12 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #13 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #14 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #15 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #16 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #17 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #18 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #19 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime #20 ..................................... FAILED
  res == div_result
  at line 1104, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime_det (4 non-witnesses) ................... FAILED
  res == 0
  at line 1128, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_is_prime_det (39 non-witnesses) .................. FAILED
  res == 0
  at line 1128, ../source/tests/suites/test_suite_mpi.function
Test mbedtls_mpi_gen_prime (Too small) ............................ PASS
Test mbedtls_mpi_gen_prime (OK, minimum size) ..................... PASS
Test mbedtls_mpi_gen_prime (corner case limb size -1 bits) ........ ^Cmake: *** [Makefile:17090: tests/test_suite_mpi.run] Interrupt

@gilles-peskine-arm
Copy link
Contributor

The easy fix is to revert the patch and we'll probably do that, but I want to 1. make sure this is indeed a compiler bug and not undefined behavior in our code, and 2. if so be a good citizen and make sure the Clang bug is reported.

@davidhorstmann-arm
Copy link
Contributor

I couldn't reproduce this on clang 13.0.0, I'll try version 12 shortly.

@gilles-peskine-arm
Copy link
Contributor

@davidhorstmann-arm Which build of Clang 13 was that? It's a development version, so maybe the bug has been fixed recently.

@davidhorstmann-arm
Copy link
Contributor

I built it from source - the llvm-project main branch, so should have all of the latest fixes.

@gilles-peskine-arm
Copy link
Contributor

Still failing with Ubuntu clang version 13.0.0-++20210721052618+968899ad9cf1-1exp120210721153410.482, i.e. the latest official nightly for Ubuntu.

@ronald-cron-arm
Copy link
Contributor

ronald-cron-arm commented Jul 23, 2021

I think we should investigate completely what is going on here. I can see that the function mpi_mul_hlp() had already some weird issue with some compiler:

#if defined(__APPLE__) && defined(__arm__)                                      
/*                                                                              
 * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)               
 * appears to need this to prevent bad ARM code generation at -O3.              
 */                                                                             
__attribute__ ((noinline))                                                      
#endif 

Furthermore, this function uses assembly code which is probably very difficult to make right especially for multiple compiler versions, compilation options ...

@davidhorstmann-arm
Copy link
Contributor

Apologies for the delay - I can confirm that I've reproduced the tests hanging on clang-12 with the following exact version:

$ clang --version
clang version 12.0.1 (https://github.com/llvm/llvm-project.git fed41342a82f5a3a9201819a82bf7a48313e296b)

The tests hang on 53/89:

...

50/89 Test #50: md-suite ...................................   Passed    0.00 sec
      Start 51: mdx-suite
51/89 Test #51: mdx-suite ..................................   Passed    0.00 sec
      Start 52: memory_buffer_alloc-suite
52/89 Test #52: memory_buffer_alloc-suite ..................   Passed    0.00 sec
      Start 53: mpi-suite
(No further output)

@davidhorstmann-arm
Copy link
Contributor

I'm not sure exactly what happened previously (I may have forgotten to remove some state), but I can now reproduce the problem with clang-13. Specifically:

$ clang --version
clang version 13.0.0 (https://github.com/llvm/llvm-project.git 3396377743939f35856b2ee4ac47d64270e822f1)

One small note: to get this to compile I had to modify library/psa_crypto_cipher.c because of an unused parameter warning (it would seem that clang-12 doesn't mind about this, but clang-13 takes issue with it).

@davidhorstmann-arm
Copy link
Contributor

As @ronald-cron-arm pointed out, there is already some code generation issue. When I commented out the #if and #endif to make the following:

/*#if defined(__APPLE__) && defined(__arm__)*/
/*
 * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)
 * appears to need this to prevent bad ARM code generation at -O3.
 */
__attribute__ ((noinline))
/*#endif*/

(i.e. making the __attribute__ ((noinline)) unconditional) the tests no longer failed.

@daverodgman daverodgman changed the title 8e464c407a1ce8b88412c6a8cc8aafa8d2cf1b0f breaks clang 12 -O2 (make test failed) 8e464c4 breaks clang 12 -O2 (make test failed) Sep 13, 2021
@gilles-peskine-arm
Copy link
Contributor

gilles-peskine-arm commented Sep 13, 2021

Given that this bug only arises when mpi_mul_hlp calls assembly code, before concluding that this is a compiler bug, we need to carefully review whether we're declaring the behavior of the assembly code correctly.

I think we are declaring register usage correctly: the code uses 7 registers:

  • %%r8 (written): clobber "r8"
  • %%rax (written): clobber "rax"
  • %%rbx (read): input "b"
  • %%rcx (read and written): output "+c"
  • %%rdi (read and written): constraint "+D"
  • %%rdx (written): constraint "rdx"
  • %%rsi (read and written): constraint "+S"

However, there are no constraints on memory. Shouldn't there be? The code reads from *s, and reads and writes to *d. If I add the clobber "memory", the tests pass.

@davidhorstmann-arm
Copy link
Contributor

I have been able to reproduce this result locally. Would it be worthwhile to see if this also removes the need for the previous __attribute__ ((noinline)) annotation from this commit?

@gilles-peskine-arm
Copy link
Contributor

I only changed the x86_64 code, so My patch doesn't do anything about fc4f46f, but it may well be due to a similar bug. See #4943

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment