diff --git a/regression/cbmc-library/snprintf-01/main.c b/regression/cbmc-library/snprintf-01/main.c new file mode 100644 index 00000000000..eaafa2432ef --- /dev/null +++ b/regression/cbmc-library/snprintf-01/main.c @@ -0,0 +1,10 @@ +#include +#include + +int main() +{ + char result[10]; + int bytes = snprintf(result, 10, "%d\n", 42); + assert(bytes <= 10); + return 0; +} diff --git a/regression/cbmc-library/snprintf-01/test.desc b/regression/cbmc-library/snprintf-01/test.desc new file mode 100644 index 00000000000..980d8bba0d6 --- /dev/null +++ b/regression/cbmc-library/snprintf-01/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--pointer-check --bounds-check --unwind 10 --no-unwinding-assertions +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ +-- +^warning: ignoring diff --git a/regression/cbmc-library/vsnprintf-01/main.c b/regression/cbmc-library/vsnprintf-01/main.c new file mode 100644 index 00000000000..08f0ccd992e --- /dev/null +++ b/regression/cbmc-library/vsnprintf-01/main.c @@ -0,0 +1,20 @@ +#include +#include +#include + +int xsnprintf(char *ptr, size_t size, const char *format, ...) +{ + va_list list; + va_start(list, format); + int result = vsnprintf(ptr, size, format, list); + va_end(list); + return result; +} + +int main() +{ + char result[10]; + int bytes = xsnprintf(result, 10, "%d\n", 42); + assert(bytes <= 10); + return 0; +} diff --git a/regression/cbmc-library/vsnprintf-01/test.desc b/regression/cbmc-library/vsnprintf-01/test.desc new file mode 100644 index 00000000000..980d8bba0d6 --- /dev/null +++ b/regression/cbmc-library/vsnprintf-01/test.desc @@ -0,0 +1,8 @@ +CORE +main.c +--pointer-check --bounds-check --unwind 10 --no-unwinding-assertions +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ +-- +^warning: ignoring diff --git a/src/ansi-c/library/stdio.c b/src/ansi-c/library/stdio.c index 99f1cc236da..628c9038481 100644 --- a/src/ansi-c/library/stdio.c +++ b/src/ansi-c/library/stdio.c @@ -1584,6 +1584,154 @@ int vasprintf(char **ptr, const char *fmt, va_list ap) return i; } +/* FUNCTION: snprintf */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +# include +# define __CPROVER_STDIO_H_INCLUDED +#endif + +#ifndef __CPROVER_STDARG_H_INCLUDED +# include +# define __CPROVER_STDARG_H_INCLUDED +#endif + +#undef snprintf + +int snprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list list; + va_start(list, fmt); + int result = vsnprintf(str, size, fmt, list); + va_end(list); + return result; +} + +/* FUNCTION: __builtin___snprintf_chk */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +# include +# define __CPROVER_STDIO_H_INCLUDED +#endif + +#ifndef __CPROVER_STDARG_H_INCLUDED +# include +# define __CPROVER_STDARG_H_INCLUDED +#endif + +int __builtin___vsnprintf_chk( + char *str, + size_t size, + int flag, + size_t bufsize, + const char *fmt, + va_list ap); + +int __builtin___snprintf_chk( + char *str, + size_t size, + int flag, + size_t bufsize, + const char *fmt, + ...) +{ + va_list list; + va_start(list, fmt); + int result = __builtin___vsnprintf_chk(str, size, flag, bufsize, fmt, list); + va_end(list); + return result; +} + +/* FUNCTION: vsnprintf */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +# include +# define __CPROVER_STDIO_H_INCLUDED +#endif + +#ifndef __CPROVER_STDARG_H_INCLUDED +# include +# define __CPROVER_STDARG_H_INCLUDED +#endif + +#undef vsnprintf + +char __VERIFIER_nondet_char(void); + +int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) +{ + (void)*fmt; + + while((__CPROVER_size_t)__CPROVER_POINTER_OFFSET(*(void **)&ap) < + __CPROVER_OBJECT_SIZE(ap)) + + { + (void)va_arg(ap, int); + __CPROVER_precondition( + __CPROVER_POINTER_OBJECT(str) != __CPROVER_POINTER_OBJECT(ap), + "vsnprintf object overlap"); + } + + size_t i = 0; + for(; i < size; ++i) + { + char c = __VERIFIER_nondet_char(); + str[i] = c; + if(c == '\0') + break; + } + + return i; +} + +/* FUNCTION: __builtin___vsnprintf_chk */ + +#ifndef __CPROVER_STDIO_H_INCLUDED +# include +# define __CPROVER_STDIO_H_INCLUDED +#endif + +#ifndef __CPROVER_STDARG_H_INCLUDED +# include +# define __CPROVER_STDARG_H_INCLUDED +#endif + +char __VERIFIER_nondet_char(void); + +int __builtin___vsnprintf_chk( + char *str, + size_t size, + int flag, + size_t bufsize, + const char *fmt, + va_list ap) +{ + (void)flag; + (void)bufsize; + (void)*fmt; + + while((__CPROVER_size_t)__CPROVER_POINTER_OFFSET(*(void **)&ap) < + __CPROVER_OBJECT_SIZE(ap)) + + { + (void)va_arg(ap, int); + __CPROVER_precondition( + __CPROVER_POINTER_OBJECT(str) != __CPROVER_POINTER_OBJECT(ap), + "vsnprintf object overlap"); + } + + size_t i = 0; + for(; i < size; ++i) + { + char c = __VERIFIER_nondet_char(); + str[i] = c; + if(c == '\0') + break; + } + + return i; +} + /* FUNCTION: __acrt_iob_func */ #ifdef _WIN32 @@ -1645,6 +1793,49 @@ int __stdio_common_vfprintf( #endif +/* FUNCTION: __stdio_common_vsprintf */ + +#ifdef _WIN32 + +# ifndef __CPROVER_STDIO_H_INCLUDED +# include +# define __CPROVER_STDIO_H_INCLUDED +# endif + +# ifndef __CPROVER_STDARG_H_INCLUDED +# include +# define __CPROVER_STDARG_H_INCLUDED +# endif + +char __VERIFIER_nondet_char(void); + +int __stdio_common_vsprintf( + unsigned __int64 options, + char *str, + size_t size, + char const *fmt, + _locale_t locale, + va_list args) +{ + (void)options; + (void)*fmt; + (void)locale; + (void)args; + + size_t i = 0; + for(; i < size; ++i) + { + char c = __VERIFIER_nondet_char(); + str[i] = c; + if(c == '\0') + break; + } + + return i; +} + +#endif + /* FUNCTION: __srget */ #ifdef __FreeBSD__ diff --git a/src/ansi-c/library_check.sh b/src/ansi-c/library_check.sh index a2c11eb9443..6fbef4cc5dc 100755 --- a/src/ansi-c/library_check.sh +++ b/src/ansi-c/library_check.sh @@ -38,12 +38,15 @@ perl -p -i -e 's/^_munmap\n//' __functions # mumap, macOS perl -p -i -e 's/^_pipe\n//' __functions # pipe, macOS perl -p -i -e 's/^_setjmp\n//' __functions # pipe, macOS perl -p -i -e 's/^_time(32|64)\n//' __functions # time, Windows +perl -p -i -e 's/^__builtin___snprintf_chk\n//' __functions # snprintf, macOS +perl -p -i -e 's/^__builtin___vsnprintf_chk\n//' __functions # vsnprintf, macOS perl -p -i -e 's/^__inet_(addr|aton|ntoa|network)\n//' __functions # inet_*, FreeBSD perl -p -i -e 's/^__isoc99_v?fscanf\n//' __functions # fscanf, Linux perl -p -i -e 's/^__isoc99_v?scanf\n//' __functions # scanf, Linux perl -p -i -e 's/^__isoc99_v?sscanf\n//' __functions # sscanf, Linux perl -p -i -e 's/^__sigsetjmp\n//' __functions # sigsetjmp, Linux perl -p -i -e 's/^__stdio_common_vfscanf\n//' __functions # fscanf, Windows +perl -p -i -e 's/^__stdio_common_vsprintf\n//' __functions # snprintf, Windows perl -p -i -e 's/^__stdio_common_vsscanf\n//' __functions # sscanf, Windows perl -p -i -e 's/^__srget\n//' __functions # gets, FreeBSD perl -p -i -e 's/^__swbuf\n//' __functions # putc, FreeBSD