Compiler Lowering Outline

I implemented (read: copy & pasted from wiki ) the XXTEA cipher into a C ++ project. For clarity, I shared encryption and decryption in separate functions: (NOTE: this is not a cryptographic question! Please do not comment on the selected cipher)

#define DELTA 0x9e3779b9 #define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z))) static void btea_enc( unsigned int *v, unsigned n, const unsigned int* key ) { unsigned int y, z, sum; unsigned p, rounds, e; rounds = 16 + 52/n; sum = 0; z = v[n-1]; do { sum += DELTA; e = (sum >> 2) & 3; for (p=0; p<n-1; p++) { y = v[p+1]; z = v[p] += MX; } y = v[0]; z = v[n-1] += MX; } while (--rounds); } static void btea_dec( unsigned int *v, unsigned n, const unsigned int* key ) { unsigned int y, z, sum; unsigned p, rounds, e; rounds = 16 + 52/n; sum = rounds*DELTA; y = v[0]; do { e = (sum >> 2) & 3; for (p=n-1; p>0; p--) { z = v[p-1]; y = v[p] -= MX; } z = v[n-1]; y = v[0] -= MX; } while ((sum -= DELTA) != 0); } #undef MX #undef DELTA 

When this code is compiled into Debug, it works great. However, when I compile this code with (by default) optimization using Visual Studio 2013 (v120), btea_dec loses its outer loop (causing decryption to create garbage).

Collapsible encryption listing and decryption . Note the missing outer loop during decryption! (if you want the code to be text, I would be happy to download it, it's just a wall with text)

Looking at the actual code, the final condition is an overflowing unsigned int 'sum':
while ((sum -= DELTA) != 0)
I don’t understand what the compiler did to make him think that he can get rid of this loop (afaik overflow is only undefined for integers, unsigned overflow is fine).

Question: Why does the compiler "optimize" the outer contour? And how to fix it?

MCVE: (insert the previous code block containing btea_enc and btea_dec between include and main)

 #define _CRT_RAND_S #include <cstdlib> int main(int argc, char* argv[]) { // Random key unsigned int key[4]; rand_s(&key[0]); rand_s(&key[1]); rand_s(&key[2]); rand_s(&key[3]); // Buffer we'll be encrypting unsigned int utext[4]; memcpy(utext, "SecretPlaintext", 16); // Encrypt btea_enc(utext, 4, key); // Decrypt btea_dec(utext, 4, key); // Should still be equal! bool s = !strcmp((char*)utext, "SecretPlaintext"); // Print message printf("Compared: %s\n", s ? "equal" : "falsly"); return s?0:1; } 
+6
source share
3 answers

WIth / GL, the compiler knows that n == 4 and therefore rounds == 29 . He probably pre-calculated the initial value of sum , which is equal to rounds*DELTA .

Further, he can try to calculate the number of iterations of the loop and deploy the outer loop. If he does it wrong (as in my other answer), it could be uint32_t(rounds * DELTA) / DELTA , which is one. Add a first iteration to do-while and where the outer loop went.

The gnasher loop control code is much simpler for the compiler, and there are exactly rounds (29) iterations that it may or may not decide to deploy, but there is very little space to spoil the number of iterations.

+4
source

Why is this happening outside of me. You can try replacing

 } while ((sum -= DELTA) != 0); 

with

  sum -= DELTA; } while ((--rounds) != 0); 
+1
source

Step 1: This MACRO is a very bad programming style. Rewrite it with arguments like

 #define MX(key,sum,p,e,y,z) (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z))) 

so that someone reading your code can see which variables appear in the expression.

Better yet, use the built-in function.


The compiler is correct for optimizing the nested loop, since visible side effects are predictable.

The only visible side effect is v[p] -= MX; and this happens a predictable number of times. Thus, the compiler can replace nested loops with v[p] -= loopcount * MX;

e , z and y are re-written, but never read, so the compiler can completely exclude them and their calculations.

Please note that such optimization of impotent operations may expose you to temporary attacks, which, in your opinion, you have carefully eliminated.

The whole body of the function becomes

 int p=n; int subtrahend = rounds * DELTA / DELTA * MX; do { v[--p] -= subtrahend; } while (p); 

Strike>

+1
source

Source: https://habr.com/ru/post/976139/


All Articles