1

I'm trying to solve a timing leak by removing an if statement in my code but because of c++'s interpretation of integer inputs in if statements I am stuck.

Note that I assume the compiler does create a conditional branch, which results in timing information being leaked!

The original code is:

int s
if (s)
   r = A
else
   r = B

Now I'm trying to rewrite it as:

int s;
r = sA+(1-s)B

Because s is not bound to [0,1] I run into the problem that it multiplies by A and B incorrectly if s is out of [0,1]. What can I do, without using an if-statement on s to solve this?

Thanks in advance

Kasper
  • 53
  • 7
  • 6
    Use `!!s` to normalize? – nwp Mar 19 '17 at 13:36
  • On some compiler/architecture combinations, `r= s ? A : B;` will be translated to branchless code (using conditional assignment). –  Mar 19 '17 at 13:38
  • Following @nwp, `!!s * A + !s * B` or better `A + !s * (B - A)`. –  Mar 19 '17 at 13:42
  • Isn't this just saying: s = not not s? If it does, why does this help? It HAS solved my error. – Kasper Mar 19 '17 at 13:45
  • or just `r = s ? A : B` to begin with and skip all of that... – David Mar 19 '17 at 13:47
  • @David that is exactly what introduces the timing leak, the condition on s. – Kasper Mar 19 '17 at 13:50
  • `!s` turns an int into a bool obviously, true if s is zero. `bool`s can be operated with `int`s and `false` is treated as 0, true as 1. `!!s` therefore turns an int into a bool that can be treated as a 0 or 1 where it's 0 if s is 0 and 1 if s is non-zero – David Mar 19 '17 at 13:50
  • I've never heard the term timing leak in my life, and I've been doing this a while – David Mar 19 '17 at 13:51
  • @David Thanks for the explanation, I'm not much of a c++ guy. ;) – Kasper Mar 19 '17 at 13:53
  • 1
    @David I haven't either, but I have heard of [timing attack](https://en.wikipedia.org/wiki/Timing_attack) (also in the tags), and timing leak appears to be precisely what allows a timing attack to succeed. –  Mar 19 '17 at 14:17
  • @YvesDaoust Note that with `A + !s * (B - A)` may cause `int` overflow with `B-A`, which is undefined behavior. `!!s * A + !s * B` is better. – chux - Reinstate Monica Mar 19 '17 at 23:31
  • @chux: "is better" is questionable, depending on the criteria. The OP is after speed. If you look at the computational cost, the first expression costs 4 operations (3 if `B - A` can be precomputed), vs. 6 for the second (possibly reduced to 5 by optimization ?). In practical situations, `B - A` can very well be overflow-safe. –  Mar 19 '17 at 23:38
  • @YvesDaoust The first example in the C spec is "An example of undefined behavior is the behavior on integer overflow" - practically or not, is is UB over the range of `A,B` in `int` where as `!!s * A + !s * B` is fine. OP is _not_ after speed. OP wants no speed _difference_ given different values of `s,A,B`. as doing timing difference leak information. – chux - Reinstate Monica Mar 19 '17 at 23:54

3 Answers3

1

What evidence do you have that the if statement is resulting in the timing leak?

If you use a modern compiler with optimizations turned on, that code should not produce a branch. You should check what your compiler is doing by looking at the assembly language output.

For instance, g++ 5.3.0 compiles this code:

int f(int s, int A, int B) {
  int r;
  if (s)
    r = A;
  else
    r = B;

  return r;
}

to this assembly:

movl    %esi, %eax
testl   %edi, %edi
cmove   %edx, %eax
ret

Look, ma! No branches! ;)

alonamaloh
  • 64
  • 3
  • 1
    This task was part of an school exercise where we had to assume the compiler does not make smart optimizations and actually branches, resulting in a timing leak on the secret variable s. – Kasper Mar 19 '17 at 14:02
  • @Kasper Then your question is unanswerable, as it does not define what does and does not branch. For instance, the suggested `!!s` is functionally equivalent to `s ? 1 : 0` and depending on the compiler in question, may branch as well. It does with Microsoft's compiler. –  Mar 19 '17 at 14:13
  • Even if they are equivalent, `s ? 1 : 0` does not leak timing information of s, unlike the code in my question. I could have been more clear about what supposedly branches and what doesn't, but my question is on something more concise: How to reduce an integer to 1 if it is not equal to 0? Which certainly IS answerable. – Kasper Mar 19 '17 at 14:28
  • @Kasper On real CPUs, when `s ? 1 : 0` branches, it *does* leak timing information, due to branch prediction making one branch faster than the other. And the answer to making an integer 1 if it's non-zero would ordinarily simply be `if (x) x = 1;`, but that won't be a useful answer to you specifically of the timing. –  Mar 19 '17 at 16:08
1

If you know the number of bits in the integer, it's pretty easy, although there are a few complications making it standards-clean with the possibility of unusual integer representations.

Here's one simple solution for 32-bit integers:

uint32_t mask = s;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
mask &= 1;
r = b ^ (-mask & (a ^ b)):

The five shift-and-or statements propagate any set bit in mask so that in the end the low-order bit is 1 unless the mask was originally 0. Then we isolate the low-order bit, resulting in a 1 or 0. The last statement is a bit-hacking equivalent of your two multiplies and add.

Here is a faster one based on the observation that if you subtract one from a number and the sign bit changes from 0 to 1, then the number was 0:

uint32_t mask = ((uint32_t(s)-1U)&~uint32_t(s))>>31) - 1U;

That is essentially the same computation as subtracting 1 and then using the carry bit, but unfortunately the carry bit is not exposed to the C language (except possibly through compiler-specific intrinsics).

Other variations are possible.

rici
  • 234,347
  • 28
  • 237
  • 341
-1

The only way to do it without branches when the optimization is not available is to resort to inline assembly. Assuming 8086:

mov ax, s
neg ax        ; CF = (ax != 0)
sbb ax, ax    ; ax = (s != 0 ? -1 : 0)
neg ax        ; ax = (s != 0 ? 1 : 0)
mov s, ax     ; now use s at will, it will be: s = (s != 0 ? 1 : 0)
Tomek
  • 4,554
  • 1
  • 19
  • 19