From 3d33925e51a024bb9b7a583116034e81aa8a2d7e Mon Sep 17 00:00:00 2001 From: jvoisin Date: Mon, 20 May 2024 14:48:35 +0200 Subject: [PATCH] Add vasprintf/asprintf The only hardening being done here is to set the char** parameter to thos functions to NULL in case of an error, to prevent it from being used should people forget to check return values. This is already done on some BSD, as well as in Rocky Linux. --- README.md | 2 ++ include/stdio.h | 40 ++++++++++++++++++++++++++++++++++++++++ tests/Makefile | 8 +++++--- tests/test_asprintf.c | 20 ++++++++++++++++++++ tests/test_vasprintf.c | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 tests/test_asprintf.c create mode 100644 tests/test_vasprintf.c diff --git a/README.md b/README.md index 9b9a956..f47df41 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ At this point, the program will safely crash. - `FD_CLR` - `FD_SET` +- `asprintf` - `bcopy` - `bzero` - `calloc` @@ -121,6 +122,7 @@ At this point, the program will safely crash. - `umask` - `vfprintf` - `vprintf` +- `vasprintf` - `vsnprintf` - `vsprintf` - `wcrtomb` diff --git a/include/stdio.h b/include/stdio.h index c6817fa..e1e8da2 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -220,6 +220,26 @@ _FORTIFY_FN(vprintf) int vprintf(const char *__f, __builtin_va_list __v) return __orig_vprintf(__f, __v); #endif } + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +#if __has_builtin(__builtin_vasprintf) +__diagnose_as_builtin(__builtin_vasprintf, 1, 2, 3) +#endif +_FORTIFY_FN(vasprintf) int vasprintf(char **restrict strp, const char *restrict fmt, __builtin_va_list ap) +{ +#if __has_builtin(__builtin___vasprintf_chk) && USE_NATIVE_CHK + return __builtin___vasprintf_chk(_FORTIFY_SOURCE, strp, fmt, ap); +#else + int ret = __orig_vasprintf(strp, fmt, ap); + if (ret < 0) + *strp = NULL; + return ret; +#endif +} + + +#endif // defined(_GNU_SOURCE) || defined(_BSD_SOURCE) + // #endif // __clang__ @@ -312,6 +332,26 @@ _FORTIFY_FN(fprintf) int fprintf(FILE *__s, const char *__f, ...) #endif } +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +#undef asprintf +__fh_access(read_only, 2) +__fh_format(printf, 2, 0) +#if __has_builtin(__builtin_asprintf) +__diagnose_as_builtin(__builtin_asprintf, 2, 3) +#endif +_FORTIFY_FN(asprintf) int asprintf(char **restrict strp, const char *restrict fmt, ...) +{ +#if __has_builtin(__builtin___asprintf_chk) && USE_NATIVE_CHK + return __builtin___asprintf_chk(_FORTIFY_SOURCE, strp, fmt, __builtin_va_arg_pack()); +#else + int ret = __orig_asprintf(strp, fmt, __builtin_va_arg_pack()); + if (ret<0) + *strp = NULL; + return ret; +#endif +} +#endif + #pragma GCC diagnostic pop #endif /* __has_builtin(__builtin_va_arg_pack) */ diff --git a/tests/Makefile b/tests/Makefile index c18898e..32b17b0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,4 @@ -CFLAGS+=-I../include/ -D_FORTIFY_SOURCE=3 -static -O2 -DPEDANTIC_CHECKS +CFLAGS+=-I../include/ -D_FORTIFY_SOURCE=3 -static -O2 -DPEDANTIC_CHECKS -Wno-format COMPTIME_TARGETS= \ test_memcpy_overwrite_under \ @@ -12,6 +12,7 @@ RUNTIME_TARGETS= \ test_FD_SET_negative \ test_FD_ISSET_SETSIZE \ test_FD_ISSET_negative \ + test_asprintf \ test_bcopy_dynamic_read \ test_bcopy_dynamic_write \ test_bcopy_static_read \ @@ -126,11 +127,12 @@ RUNTIME_TARGETS= \ test_ttyname_r_dynamic \ test_ttyname_r_static \ test_umask \ + test_vasprintf \ + test_vfprintf \ + test_vprintf \ test_vsnprintf_dynamic \ test_vsnprintf_static \ test_vsprintf \ - test_vfprintf \ - test_vprintf \ test_wcscat_static_write \ test_wcscpy_static_write \ test_wcsncat_static_write \ diff --git a/tests/test_asprintf.c b/tests/test_asprintf.c new file mode 100644 index 0000000..a02d110 --- /dev/null +++ b/tests/test_asprintf.c @@ -0,0 +1,20 @@ +#define _GNU_SOURCE +#include "common.h" + +#include +#include +#include + +int main(int argc, char** argv) { + char* buf; + asprintf(&buf, "total: %d+%d=%d", 1, 2, 3); + puts(buf); + free(buf); + +#ifndef __clang__ + asprintf(&buf, "total: %", 1); + assert(buf == NULL); +#endif + + return 0; +} diff --git a/tests/test_vasprintf.c b/tests/test_vasprintf.c new file mode 100644 index 0000000..2f71714 --- /dev/null +++ b/tests/test_vasprintf.c @@ -0,0 +1,36 @@ +#define _GNU_SOURCE +#include "common.h" + +#include +#include +#include +#include + +void test(const char *fmt, ...) +{ + char* buf; + va_list args; + va_start(args, fmt); + vasprintf(&buf, fmt, args); + va_end(args); + puts(buf); + free(buf); +} + +void test2(const char *fmt, ...) +{ + char* buf; + va_list args; + va_start(args, fmt); + vasprintf(&buf, fmt, args); + va_end(args); + assert(buf == NULL); +} + +int main(int argc, char** argv) { + test("Total: %d+%d=%d", 1, 2, 3); +#ifndef __clang__ + test2("Total: %", 1, 2, 3); +#endif + return 0; +}