fortify-headers

standalone fortify-source implementation
git clone git://git.2f30.org/fortify-headers
Log | Files | Refs | README | LICENSE

commit 1becad43298e74ba73bc66f9d44523e5d121c667
parent 92c611ad8abb146ed548301de8bc011c2b17bccd
Author: jvoisin <julien.voisin@dustri.org>
Date:   Mon, 20 May 2024 14:48:35 +0200

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.

Diffstat:
MREADME.md | 2++
Minclude/stdio.h | 42++++++++++++++++++++++++++++++++++++++++++
Mtests/Makefile | 8+++++---
Atests/test_asprintf.c | 20++++++++++++++++++++
Atests/test_vasprintf.c | 36++++++++++++++++++++++++++++++++++++
5 files changed, 105 insertions(+), 3 deletions(-)

diff --git 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 @@ -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) +#undef vasprintf +#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,28 @@ _FORTIFY_FN(fprintf) int fprintf(FILE *__s, const char *__f, ...) #endif } +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +#ifndef __clang__ +#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 // __clang__ +#endif + #pragma GCC diagnostic pop #endif /* __has_builtin(__builtin_va_arg_pack) */ diff --git 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 @@ -0,0 +1,20 @@ +#define _GNU_SOURCE +#include "common.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +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 @@ -0,0 +1,36 @@ +#define _GNU_SOURCE +#include "common.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +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; +}