23

Lets say that you have a function which generates some security token for your application, such as some hash salt, or maybe a symetric or asymetric key.

Now lets say that you have this function in your C++ as a constexpr and that you generate keys for your build based on some information (like, the build number, a timestamp, something else).

You being a diligent programmer make sure and call this in the appropriate ways to ensure it's only called at compile time, and thus the dead stripper removes the code from the final executable.

However, you can't ever be sure that someone else isn't going to call it in an unsafe way, or that maybe the compiler won't strip the function out, and then your security token algorithm will become public knowledge, making it more easy for would be attackers to guess future tokens.

Or, security aside, let's say the function takes a long time to execute and you want to make sure it never happens during runtime and causes a bad user experience for your end users.

Are there any ways to ensure that a constexpr function can never be called at runtime? Or alternately, throwing an assert or similar at runtime would be ok, but not as ideal obviously as a compile error would be.

I've heard that there is some way involving throwing an exception type that doesn't exist, so that if the constexpr function is not deadstripped out, you'll get a linker error, but have heard that this only works on some compilers.

Distantly related question: Force constexpr to be evaluated at compile time

cigien
  • 57,834
  • 11
  • 73
  • 112
Alan Wolfe
  • 615
  • 4
  • 16
  • 1
    One possible solution: you implement that function strictly in terms of `template <...> struct xyz { static constexpr long long value=...; }`. No, really, I mean do not use the `constexpr function` but implement the computations strictly in struct templates. – Adrian Colomitchi Sep 22 '16 at 23:05
  • 6
    Note that it's generally agreed that if knowing your algorithm is enough for it to be broken, then your algorithm is crap. – user253751 Sep 22 '16 at 23:15
  • That's a good general comment to make for folks who might come across this question and want to give it a try, but FWIW my needs are not security related. – Alan Wolfe Sep 22 '16 at 23:19
  • 2
    Might be easier and more maintainable to simply run the algorithm in the build system and export it's value to the program. In CMake, this would be with `config_file()`. This way your algorithm isn't ever in the compiled code at all. – Richard Hodges Sep 23 '16 at 06:29

5 Answers5

17

In C++20 you can just replace constexpr by consteval to enforce a function to be always evaluated at compile time.

Example:

          int    rt_function(int v){ return v; }
constexpr int rt_ct_function(int v){ return v; }
consteval int    ct_function(int v){ return v; }

int main(){
    constexpr int ct_value = 1; // compile value
    int           rt_value = 2; // runtime value

    int a = rt_function(ct_value);
    int b = rt_ct_function(ct_value);
    int c = ct_function(ct_value);

    int d = rt_function(rt_value);
    int e = rt_ct_function(rt_value);
    int f = ct_function(rt_value); // ERROR: runtime value

    constexpr int g = rt_function(ct_value); // ERROR: runtime function
    constexpr int h = rt_ct_function(ct_value);
    constexpr int i = ct_function(ct_value);
}

Pre C++20 workaround

You can enforce the use of it in a constant expression:

#include<utility>

template<typename T, T V>
constexpr auto ct() { return V; }

template<typename T>
constexpr auto func() {
    return ct<decltype(std::declval<T>().value()), T{}.value()>();
}

template<typename T>
struct S {
    constexpr S() {}
    constexpr T value() { return T{}; }
};

template<typename T>
struct U {
    U() {}
    T value() { return T{}; }
};

int main() {
    func<S<int>>();
    // won't work
    //func<U<int>>();
}

By using the result of the function as a template argument, you got an error if it can't be solved at compile-time.

Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • 1
    That's a nice trick! That is useful, but do you know if there's a way to make it so the function can't be called at runtime? – Alan Wolfe Sep 23 '16 at 05:39
  • 2
    @AlanWolfe This way it can't be called at runtime, you'll got an error at compile-time time. You simply have to hide the details behind a nicely defined interface, of course. – skypjack Sep 23 '16 at 05:44
  • 4
    ct can be replaced by std::integral_constant in C++11. See http://en.cppreference.com/w/cpp/types/integral_constant – mabraham Sep 24 '16 at 06:30
  • I'm not getting from this code what it does or how to use it... Can you provide a simple example for body e.g. `return x/3;` ? I'm not sure where to put it. – Serge Rogatch Jul 08 '17 at 19:59
  • @SergeRogatch I saw your question. Would you mean something like [this](http://coliru.stacked-crooked.com/a/75adaf3b5d667786)? The fact is that you should use your `StaticFloorLog2` function through a class template to enforce the fact that it is used in a constant expression. In the example above, you can get the result using the `value` data member of the `Get` struct and you are sure that it is computed at compile time. – skypjack Jul 08 '17 at 20:08
  • @skypjack , it doesn't work for `double` type and it doesn't really enforce... someone may forget about `Get` structure and use `StaticFloorLog2` directly. – Serge Rogatch Jul 08 '17 at 20:11
  • 1
    @SergeRogatch It works only with those type you can use as non-type template parameters, I agree. That being said, if you use your `StaticFloorLog2` directly, you cannot ensure that it is called in a constant expression. I'm sorry. That's how `constexpr` functions work. – skypjack Jul 08 '17 at 20:16
7

A theoretical solution (as templates should be Turing complete) - don't use constexpr functions and fall back onto the good-old std=c++0x style of computing using exclusively struct template with values. For example, don't do

constexpr uintmax_t fact(uint n) {
  return n>1 ? n*fact(n-1) : (n==1 ? 1 : 0);
}

but

template <uint N> struct fact {
  uintmax_t value=N*fact<N-1>::value;
}
template <> struct fact<1>
  uintmax_t value=1;
}
template <> struct fact<0>
  uintmax_t value=0;
}

The struct approach is guaranteed to be evaluated exclusively at compile time.

The fact the guys at boost managed to do a compile time parser is a strong signal that, albeit tedious, this approach should be feasible - it's a one-off cost, maybe one can consider it an investment.


For example:

to power struct:

// ***Warning: note the unusual order of (power, base) for the parameters
// *** due to the default val for the base
template <unsigned long exponent, std::uintmax_t base=10>
struct pow_struct
{
private:
  static constexpr uintmax_t at_half_pow=pow_struct<exponent / 2, base>::value;
public:
  static constexpr uintmax_t value=
      at_half_pow*at_half_pow*(exponent % 2 ? base : 1)
  ;
};

// not necessary, but will cut the recursion one step
template <std::uintmax_t base>
struct pow_struct<1, base>
{
  static constexpr uintmax_t value=base;
};


template <std::uintmax_t base>
struct pow_struct<0,base>
{
  static constexpr uintmax_t value=1;
};

The build token

template <uint vmajor, uint vminor, uint build>
struct build_token {
  constexpr uintmax_t value=
       vmajor*pow_struct<9>::value 
     + vminor*pow_struct<6>::value 
     + build_number
  ;
}
Adrian Colomitchi
  • 3,974
  • 1
  • 14
  • 23
7

In the upcoming C++20 there will be consteval specifier.

consteval - specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant

magras
  • 1,709
  • 21
  • 32
5

Since now we have C++17, there is an easier solution:

template <auto V>
struct constant {
    constexpr static decltype(V) value = V;
};

The key is that non-type arguments can be declared as auto. If you are using standards before C++17 you may have to use std::integral_constant. There is also a proposal about the constant helper class.

An example:

template <auto V>
struct constant {
    constexpr static decltype(V) value = V;
};

constexpr uint64_t factorial(int n) {
    if (n <= 0) {
        return 1;
    }
    return n * factorial(n - 1);
}

int main() {
    std::cout << "20! = " << constant<factorial(20)>::value << std::endl;
    return 0;
}
ipid
  • 560
  • 1
  • 8
  • 13
  • 5
    There is no need for `struct` here, you can do `template inline constexpr auto value = V;` and ditch the `::value`. – HolyBlackCat Feb 06 '19 at 16:48
  • 1
    You can also omit "inline" since it's implied by constexpr. – Not Saying Nov 20 '20 at 01:08
  • @NotSaying That is not the case for non-static variables. Refer to [this](https://stackoverflow.com/a/57407675/7107236) answer for more information. – 303 Oct 31 '21 at 01:21
1

Have your function take template parameters instead of arguments and implement your logic in a lambda.

#include <iostream>

template< uint64_t N >
constexpr uint64_t factorial() {
    // note that we need to pass the lambda to itself to make the recursive call
    auto f = []( uint64_t n, auto& f ) -> uint64_t {
        if ( n < 2 ) return 1;
        return n * f( n - 1, f );
    };
    return f( N, f );
}

using namespace std;

int main() {
    cout << factorial<5>() << std::endl;
}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Michael
  • 1,263
  • 1
  • 12
  • 28
  • This is limited to the constraints of non-type template parameters. For example, floating point types are [not allowed](https://stackoverflow.com/a/11518757/7107236). – 303 Oct 31 '21 at 01:28