smackers / smack

SMACK Software Verifier and Verification Toolchain
http://smackers.github.io
Other
431 stars 82 forks source link

Problem with __SMACK_value() in C++ #353

Open niekbouman opened 6 years ago

niekbouman commented 6 years ago

In C, a function may be declared without explicitly giving the number of parameters, as explained in this stackexchange article

C++ does not support this C-style functions with unspecified number of arguments.

smack.h uses this C-style declaration for __SMACK_value, which poses a problem for verifying C++ code with Smack.

zvonimir commented 6 years ago

While we are at it, it would be good to also add a regression with __SMACK_value.

niekbouman commented 6 years ago

Regression test (test.cpp)

extern "C" {
#include <smack.h>
}

int main()
{
    int x = 10;
    __SMACK_value(x);
    return 0;
}

compiling with

clang++-3.9 -std=c++14 -c -emit-llvm -o test.bc test.cpp

gives

regression.cpp:8:2: error: no matching function for call to '__SMACK_value'
        __SMACK_value(x);
        ^~~~~~~~~~~~~
smack.h:34:15: note: candidate function not viable: requires 0 arguments, but 1 was provided
smack_value_t __SMACK_value();
              ^
1 error generated.
niekbouman commented 6 years ago

In C++, one typically uses a template-function for taking an argument of arbitrary type

template<typename T>
void __SMACK_value(T x); // accepts 1 arg of arbitrary type

Not sure though if templates work well for Smack's use case, i.e., to merely label the variable x in LLVM's IR

jjgarzella commented 6 years ago

It seems, based on SmackRep::valueAnnotation, that __SMACK_value() can be used with only one or two arguments. Can we just make proper declarations for these two cases?

zvonimir commented 6 years ago

I think in the long run we should split SMACK headers into C and C++ specific ones, with maybe a top-level shared header.

niekbouman commented 6 years ago

As a workaround, I've had success with the following 'hack':

extern "C" {

//...

#ifdef __cplusplus
smack_value_t __SMACK_value(unsigned int*, ...);
#else
smack_value_t __SMACK_value();
#endif

//...

} // end of extern "C"

i.e., by declaring __SMACK_value for C++ as a variadic function (with C linkage to prevent name mangling). The benefit of this declaration is that clang++ produces LLVM IR code for this variadic function that closely resembles the IR code that clang produces for the __SMACK_value() function. Hence, smack can parse the IR code; no changes toSmackRep.cpp are necessary. With this hack, I have successfully tested a C++ variant of ct-verif's Tiny Encryption Algorithm example (see post below for details).

The problem with this workaround is that the type that __SMACK_value takes is now hard-coded into the declaration, in other words, it currently only works for unsigned int* (which I happened to need for the Tiny Encryption Algorithm example).

One idea to circumvent this problem would be to declare __SMACK_value as follows:

smack_value_t __SMACK_value(int, ...);

where int is a dummy parameter, which is needed only because variadic functions need at least one named parameter. Then smack would need to be updated (probably by altering SmackRep.cpp) such that it always ignores the first parameter to __SMACK_value; the second parameter would be the actual value, which can then be of any type. One would then use it as

__SMACK_value(0, v);

where 0 is the dummy int, and the actual value is the parameter v. I have tried to implement this, currently without succes (my change seemed to break the ct-verif toolchain: bam ran into an error while processing smack's output).

niekbouman commented 6 years ago

FYI, my C++ variant of ct-verif's Tiny Encryption Algorithm C++ example is as follows: (note that ct-verif uses smack internally)

invoking ct-verif

The entry point should now be the C++-mangled name (which is in my case _Z15encrypt_wrapperPjS_)

ct-verif.rb -e _Z15encrypt_wrapperPjS_ tea.cpp

filename: tea.cpp

Note that C++ language mode is determined from the.cpp extension

/* From https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm */
extern "C" {
#include "ct-verif.h" // is a C header
}

#include <stdint.h>

void encrypt (uint32_t* v, uint32_t* k) {
  uint32_t v0=v[0], v1=v[1], sum=0, i;           /* set up */
  uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
  uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */

  /* Code */
  for (i=0; i < 32; i++) {                       /* basic cycle start */
    sum += delta;
    v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
    v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
  }                                              /* end cycle */
  v[0]=v0; v[1]=v1;
}

void decrypt (uint32_t* v, uint32_t* k) {
  uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */
  uint32_t delta=0x9e3779b9;                     /* a key schedule constant */
  uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */

  /* Code */
  for (i=0; i<32; i++) {                         /* basic cycle start */
    v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
    sum -= delta;
  }                                              /* end cycle */
  v[0]=v0; v[1]=v1;
}

void encrypt_wrapper (uint32_t* v, uint32_t* k) {
  /* Boilerplate */
  public_in(__SMACK_value(v));
  public_in(__SMACK_value(k));

  /* Useful */
  // declassified_out_object(__SMACK_object(v,2 * sizeof(*v)));

  encrypt(v,k);
}

void decrypt_cpa_wrapper (uint32_t* v, uint32_t* k) {
  /* Boilerplate */
  public_in(__SMACK_value(v));
  public_in(__SMACK_value(k));

  /* Useful */
  public_in(__SMACK_values(v,2));

  decrypt(v,k);
}

void decrypt_cca_wrapper (uint32_t* v, uint32_t* k) {
  /* Boilerplate */
  public_in(__SMACK_value(v));
  public_in(__SMACK_value(k));

  /* Useful */
  public_in(__SMACK_values(v,2));
  //  declassified_out_object(__SMACK_object(v,2 * sizeof(*v)));

  decrypt(v,k);
}
niekbouman commented 6 years ago

FYI, I've forked the ct-verif repo and created a Dockerfile for building a ct-verif environment (including the __SMACK_value work-around for Smack) that can verify the above C++ example. See:

https://github.com/niekbouman/verifying-constant-time/tree/cplusplus

My commits related to SMACK can be found here (in the variadic branch) https://github.com/niekbouman/smack/tree/variadic

(the commits in the bouman branch are obsolete)

michael-emmi commented 6 years ago

Excellent. I think the jury is still out on the right interface for __SMACK_value or something similar, but we will keep an eye on this. Your ideas are welcome!