Open niekbouman opened 6 years ago
While we are at it, it would be good to also add a regression with __SMACK_value
.
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.
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
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?
I think in the long run we should split SMACK headers into C and C++ specific ones, with maybe a top-level shared header.
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).
FYI, my C++ variant of ct-verif
's Tiny Encryption Algorithm C++ example is as follows:
(note that ct-verif
uses smack
internally)
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
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);
}
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)
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!
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.