broth-itk
broth-itk

Reputation: 161

Declare C function to accept only constant numbers

Is it possible to declare a function in C which allows passing only a numeric const (no variables)?

Example:

uint8_t gmul256(uint8_t x, uint8_t y);

Parameter "y" shall be accepted as numeric constant only. Variables, pointers etc. shall generate an error.

res = gmul256(var, 5); // -> OK
res = gmul256(var1, var2); // -> Shall generate compile error

Compiler is gcc.

Background: Mitigation of timing and power analysis attacks in AES code, generate more performant code.

Upvotes: 8

Views: 290

Answers (2)

You can use a macro with a bit field (C99+) to check that the argument is an integer constant expression. Here we declare an anonymous structure with a bitfield whose size depends on the given value. If you pass in a variable, the construct is invalid. Naturally a compiler can compile it, but a conforming compiler must produce at least a warning. C11 6.7.2.1p4:

  1. The expression that specifies the width of a bit-field shall be an integer constant expression with a nonnegative value that does not exceed the width of an object of the type that would be specified were the colon and expression omitted. [122] If the value is zero, the declaration shall have no declarator.

We're using the value of y here to calculate the size of an anonymous structure with a bit-field, whose width depends on the value of y, producing either width 1 or 2 depending on the value of y; if y is not an integer constant expression, the compiler must report a constraint violation. As an added bonus this should work on any compiler that supports C99 bit-fields, not just GCC.

#include <inttypes.h>

extern int gmul256(uint8_t, uint8_t);

#define assert_int_constant(x) ((void)sizeof(struct {int dummy: 1 + !(x);}))
#define gmul256(x, y) (assert_int_constant(y), gmul256((x), (y)))

int main() {
    uint8_t x = 5, y = 42;
    gmul256(x, 5);
    gmul256(x, 5 * 5);
    gmul256(x, y);
}

Upon compilation would produce

% gcc constanttest.c
# or,  gcc constanttest.c -std=c11 -pedantic-errors -Wall -Wextra alike
constanttest.c: In function ‘main’:
constanttest.c:5:52: error: bit-field ‘dummy’ width not an integer constant
 #define assert_int_constant(x) ((void)sizeof(struct {int dummy: 1 + !(x));}))
                                                    ^
constanttest.c:6:24: note: in expansion of macro ‘assert_int_constant’
 #define gmul256(x, y) (assert_int_constant(y), gmul256((x), (y)))
                        ^~~~~~~~~~~~~~~~~~~
constanttest.c:12:5: note: in expansion of macro ‘gmul256’
     gmul256(x, y);
     ^~~~~~~

Upvotes: 7

broth-itk
broth-itk

Reputation: 161

Based on your valuable input I did following:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
#define gmul256(x, y) (BUILD_BUG_ON(!__builtin_constant_p((y))), _gmul256((x), (y)))

uint8_t _gmul256(uint8_t x, uint8_t y);

This will generate a compile error if "y" is not a constant. Thank you very much!

Upvotes: 1

Related Questions