From afbf22d62076868d6db30411ad1924d99fbdb05d Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Wed, 13 May 2020 13:48:32 -0400 Subject: Don’t pass `char` to `va_start` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passing `va_start` an argument of a type that undergoes promotion (`char`, `float`, etc.) triggers undefined behavior. Make `_cat_with` take an `int c` instead, and assert that `c` can be stored in a `char` before using it as one. --- brightnessctl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/brightnessctl.c b/brightnessctl.c index 31a066d..922a3c4 100644 --- a/brightnessctl.c +++ b/brightnessctl.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -31,7 +32,7 @@ enum operation; static void fail(char *, ...); static void usage(void); #define cat_with(...) _cat_with(__VA_ARGS__, NULL) -static char *_cat_with(char, ...); +static char *_cat_with(int, ...); static char *dir_child(char *, char*); static char *device_path(struct device *); static char *class_path(char *); @@ -601,12 +602,16 @@ bool ensure_dev_dir(struct device *dev) { return ret; } -char *_cat_with(char c, ...) { +char *_cat_with(int c, ...) { + // We'd like c to be a char, but passing a char to va_start triggers + // undefined behavior. Take it as an int instead, and assert that it can + // fit in a char before using it as one. + assert(c >= CHAR_MIN && c <= CHAR_MAX); size_t size = 32; size_t length = 0; char *buf = calloc(1, size + 1); char *curr; - char split[2] = {c, '\0'}; + char split[2] = {(char) c, '\0'}; va_list va; va_start(va, c); curr = va_arg(va, char *); -- cgit v1.2.3