Reputation: 161
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
Reputation: 133978
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:
- 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
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