Skip to content

Commit

Permalink
types,fmt: Safe RE_VA_ARG helpers (#758)
Browse files Browse the repository at this point in the history
RE_VA_ARG helpers ensure type size safety by using the _Generic keyword (C11 only) to determine the size of the argument based on its type. This ensures that only arguments of the expected types are passed to the function, and prevents type mismatches that could lead to undefined behavior or security vulnerabilities.
  • Loading branch information
sreimers authored Aug 10, 2023
1 parent 663e3d2 commit aedaf42
Show file tree
Hide file tree
Showing 4 changed files with 431 additions and 74 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ else()
endif()

if(CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wshorten-64-to-32)
add_compile_options(
-Wshorten-64-to-32
-Wno-gnu-zero-variadic-macro-arguments
)
endif()

check_c_compiler_flag("-Watomic-implicit-seq-cst" COMPILER_SUPPORTS_WATOMIC)
Expand Down
45 changes: 39 additions & 6 deletions include/re_fmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,45 @@ int re_vsnprintf(char *re_restrict str, size_t size,
const char *re_restrict fmt, va_list ap);
int re_vsdprintf(char **strp, const char *fmt, va_list ap);

int re_hprintf(struct re_printf *pf, const char *fmt, ...);
int re_fprintf(FILE *stream, const char *fmt, ...);
int re_printf(const char *fmt, ...);
int re_snprintf(char *re_restrict str, size_t size,
const char *re_restrict fmt, ...);
int re_sdprintf(char **strp, const char *fmt, ...);
/* Secure va_list print */
int re_vhprintf_s(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg);
int re_vfprintf_s(FILE *stream, const char *fmt, va_list ap);
int re_vprintf_s(const char *fmt, va_list ap);
int re_vsnprintf_s(char *re_restrict str, size_t size,
const char *re_restrict fmt, va_list ap);
int re_vsdprintf_s(char **strp, const char *fmt, va_list ap);

#ifdef HAVE_RE_ARG
#define re_printf(fmt, ...) _re_printf_s((fmt), RE_VA_ARGS(__VA_ARGS__))
#define re_hprintf(pf, fmt, ...) \
_re_hprintf_s((pf), (fmt), RE_VA_ARGS(__VA_ARGS__))
#define re_fprintf(stream, fmt, ...) \
_re_fprintf_s((stream), (fmt), RE_VA_ARGS(__VA_ARGS__))
#define re_snprintf(str, size, fmt, ...) \
_re_snprintf_s((str), (size), (fmt), RE_VA_ARGS(__VA_ARGS__))
#define re_sdprintf(strp, fmt, ...) \
_re_sdprintf_s((strp), (fmt), RE_VA_ARGS(__VA_ARGS__))
#else
#define re_printf(...) _re_printf(__VA_ARGS__)
#define re_hprintf _re_hprintf
#define re_fprintf _re_fprintf
#define re_snprintf _re_snprintf
#define re_sdprintf _re_sdprintf
#endif

int _re_printf(const char *fmt, ...);
int _re_hprintf(struct re_printf *pf, const char *fmt, ...);
int _re_fprintf(FILE *stream, const char *fmt, ...);
int _re_snprintf(char *re_restrict str, size_t size,
const char *re_restrict fmt, ...);
int _re_sdprintf(char **strp, const char *fmt, ...);

int _re_printf_s(const char *fmt, ...);
int _re_hprintf_s(struct re_printf *pf, const char *fmt, ...);
int _re_fprintf_s(FILE *stream, const char *fmt, ...);
int _re_snprintf_s(char *re_restrict str, size_t size,
const char *re_restrict fmt, ...);
int _re_sdprintf_s(char **strp, const char *fmt, ...);


/* Regular expressions */
Expand Down
77 changes: 77 additions & 0 deletions include/re_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,80 @@ typedef int re_sock_t;
#define re_assert(expr) assert(expr)
#define re_assert_se(expr) assert(expr)
#endif


/* RE_VA_ARG SIZE helpers */
#if !defined(DISABLE_RE_ARG) && \
!defined(__STRICT_ANSI__) && /* Needs ## trailing comma fix, with C23 \
we can use __VA_OPT__ */ \
__STDC_VERSION__ >= 201112L /* _Generic C11 support required */

#define HAVE_RE_ARG 1

#define RE_ARG_SIZE(type) \
_Generic((type), \
bool: sizeof(bool), \
char: sizeof(char), \
unsigned char: sizeof(unsigned char), \
short: sizeof(short), \
unsigned short: sizeof(unsigned short), \
int: sizeof(int), \
unsigned int: sizeof(unsigned int), \
long: sizeof(long), \
unsigned long: sizeof(unsigned long), \
long long: sizeof(long long), \
unsigned long long: sizeof(unsigned long long), \
float: sizeof(float), \
double: sizeof(double), \
char const*: sizeof(char const *), \
char*: sizeof(char *), \
void const*: sizeof(void const *), \
void*: sizeof(void *), \
default: sizeof(void*) \
)

#define RE_ARG_0() 0
#define RE_ARG_1(expr) RE_ARG_SIZE(expr), (expr), 0
#define RE_ARG_2(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_1(__VA_ARGS__)
#define RE_ARG_3(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_2(__VA_ARGS__)
#define RE_ARG_4(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_3(__VA_ARGS__)
#define RE_ARG_5(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_4(__VA_ARGS__)
#define RE_ARG_6(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_5(__VA_ARGS__)
#define RE_ARG_7(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_6(__VA_ARGS__)
#define RE_ARG_8(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_7(__VA_ARGS__)
#define RE_ARG_9(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_8(__VA_ARGS__)
#define RE_ARG_10(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_9(__VA_ARGS__)
#define RE_ARG_11(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_10(__VA_ARGS__)
#define RE_ARG_12(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_11(__VA_ARGS__)
#define RE_ARG_13(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_12(__VA_ARGS__)
#define RE_ARG_14(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_13(__VA_ARGS__)
#define RE_ARG_15(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_14(__VA_ARGS__)
#define RE_ARG_16(expr, ...) RE_ARG_SIZE(expr), (expr), RE_ARG_15(__VA_ARGS__)

#define RE_ARG_VA_NUM_2(X, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, \
X5, X4, X3, X2, X1, N, ...) \
N
#define RE_ARG_VA_NUM(...) \
RE_ARG_VA_NUM_2(0, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, \
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

#define RE_ARG_N3(N, ...) RE_ARG_##N(__VA_ARGS__)
#define RE_ARG_N2(N, ...) RE_ARG_N3(N, __VA_ARGS__)
#define RE_VA_ARGS(...) RE_ARG_N2(RE_ARG_VA_NUM(__VA_ARGS__), __VA_ARGS__)
#endif /* End RE_VA_ARG SIZE helpers */

#define RE_VA_ARG(ap, val, type, safe) \
if (likely((safe))) { \
size_t sz = va_arg((ap), size_t); \
if (unlikely(!sz)) { \
re_assert(0 && "RE_VA_ARG: no more arguments"); \
err = EOVERFLOW; \
goto out; \
} \
if (unlikely(sz > sizeof(type))) { \
re_assert(0 && "RE_VA_ARG: arg is not compatible"); \
err = EOVERFLOW; \
goto out; \
} \
} \
(val) = va_arg((ap), type)
Loading

0 comments on commit aedaf42

Please sign in to comment.