diff --git a/include/errno.h b/include/errno.h index d343d636d9..22d7e455a9 100644 --- a/include/errno.h +++ b/include/errno.h @@ -55,6 +55,19 @@ int *__errno(void); #define errno (*__errno()) #endif +#if (__STDC_WANT_LIB_EXT1__ - 0 == 1) || defined(_NETBSD_SOURCE) +/* + * ISO/IEC 9899:2011 section K.3.2 Errors + * + * Types: + * errno_t + */ +#ifndef errno_t +typedef int errno_t; +#define errno_t errno_t +#endif +#endif /* __STDC_WANT_LIB_EXT1__ */ + #if defined(_NETBSD_SOURCE) #ifndef __LIBC12_SOURCE__ extern const int sys_nerr __RENAME(__sys_nerr14); diff --git a/include/stddef.h b/include/stddef.h index e7635ad14a..dc74bf2449 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -45,6 +45,19 @@ typedef _BSD_SIZE_T_ size_t; #undef _BSD_SIZE_T_ #endif +#if (__STDC_WANT_LIB_EXT1__ - 0 == 1) || defined(_NETBSD_SOURCE) +/* + * ISO/IEC 9899:2011 section K.3.3 Common definitions + * + * Types: + * rsize_t + */ +#ifndef rsize_t +typedef size_t rsize_t; +#define rsize_t rsize_t +#endif +#endif /* __STDC_WANT_LIB_EXT1__ */ + #if defined(_BSD_WCHAR_T_) && !defined(__cplusplus) typedef _BSD_WCHAR_T_ wchar_t; #undef _BSD_WCHAR_T_ diff --git a/include/string.h b/include/string.h index 98825fb705..543a159489 100644 --- a/include/string.h +++ b/include/string.h @@ -97,6 +97,30 @@ __aconst char *strsignal(int); */ #endif +#if (__STDC_WANT_LIB_EXT1__ - 0 == 1) || defined(_NETBSD_SOURCE) +/* + * ISO/IEC 9899:2011 section K.3.7 String handling + * + * Types: + * errno_t + * rsize_t + * + * Functions: + * memset_s + * (and many more, which we do not yet implement) + */ +#ifndef errno_t +typedef int errno_t; +#define errno_t errno_t +#endif +#ifndef rsize_t +typedef size_t rsize_t; +#define rsize_t rsize_t +#endif + +errno_t memset_s(void *, rsize_t, int, rsize_t); +#endif /* __STDC_WANT_LIB_EXT1__ */ + #if defined(_NETBSD_SOURCE) #include /* for backwards-compatibilty */ void *memmem(const void *, size_t, const void *, size_t); diff --git a/lib/libc/string/Makefile.inc b/lib/libc/string/Makefile.inc index ed2c67e97c..ff1dee72fd 100644 --- a/lib/libc/string/Makefile.inc +++ b/lib/libc/string/Makefile.inc @@ -15,7 +15,7 @@ SRCS+= bm.c stpcpy.c stpncpy.c \ strtok_r.c strxfrm.c __strsignal.c strerror_r.c strndup.c \ stresep.c memrchr.c -SRCS+= bcmp.c bcopy.c bzero.c ffs.c memchr.c memcmp.c memset.c +SRCS+= bcmp.c bcopy.c bzero.c ffs.c memchr.c memcmp.c memset.c memset_s.c SRCS+= strcat.c strcmp.c strcpy.c strcspn.c strlen.c SRCS+= strncat.c strncmp.c strncpy.c strpbrk.c strsep.c SRCS+= strspn.c strstr.c swab.c @@ -40,7 +40,7 @@ SRCS+= _strlcat.c _strlcpy.c _strerror_r.c MAN+= bm.3 bcmp.3 bcopy.3 bstring.3 bzero.3 ffs.3 index.3 \ memccpy.3 memchr.3 memcmp.3 memcpy.3 memmem.3 memmove.3 memset.3 \ - popcount.3 \ + memset_s.3 popcount.3 \ rindex.3 strcasecmp.3 strcat.3 strchr.3 strcmp.3 strcoll.3 \ strcpy.3 strcspn.3 strdup.3 strerror.3 string.3 strings.3 strlcpy.3 \ strlen.3 strmode.3 strpbrk.3 strrchr.3 strsep.3 \ diff --git a/lib/libc/string/memset.3 b/lib/libc/string/memset.3 index 91f9d36696..7d58f0ea9f 100644 --- a/lib/libc/string/memset.3 +++ b/lib/libc/string/memset.3 @@ -32,7 +32,7 @@ .\" from: @(#)memset.3 8.1 (Berkeley) 6/4/93 .\" $NetBSD: memset.3,v 1.9 2003/08/07 16:43:49 agc Exp $ .\" -.Dd June 4, 1993 +.Dd February 22, 2012 .Dt MEMSET 3 .Os .Sh NAME @@ -47,13 +47,25 @@ .Sh DESCRIPTION The .Fn memset -function -writes -.Fa len -bytes of value +function copies the value .Fa c -(converted to an unsigned char) to the string +(converted to an unsigned char) +into each of the first +.Fa len +bytes of the memory buffer whose starting address is given by .Fa b . +.Pp +Note that a compiler may +.Dq optimise away +a call to +.Fn memset +if the compiler believes that the memory is not used after the call. +This means that the use of +.Fn memset +to overwrite sensitive information in memory may not work as expected. +The +.Xr memset_s 3 +function does not suffer from this problem. .Sh RETURN VALUES The .Fn memset @@ -62,6 +74,7 @@ returns the original value of .Fa b . .Sh SEE ALSO .Xr bzero 3 , +.Xr memset_s 3 , .Xr swab 3 .Sh STANDARDS The diff --git a/lib/libc/string/memset_s.3 b/lib/libc/string/memset_s.3 new file mode 100644 index 0000000000..1f56c0024c --- /dev/null +++ b/lib/libc/string/memset_s.3 @@ -0,0 +1,147 @@ +.\" +.\" Copyright (c) 2012 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $NetBSD$ +.\" +.Dd February 21, 2012 +.Dt MEMSET_S 3 +.Os +.Sh NAME +.Nm memset_s +.Nd copy a value to all bytes of a memory buffer +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.Fd "#define __STDC_WANT_LIB_EXT1__ 1 +.In string.h +.Ft errno_t +.Fn memset_s "void *s" "rsize_t smax" "int c" "rsize_t n" +.Sh DESCRIPTION +The +.Fn memset_s +function copies the value +.Fa c +(converted to an unsigned char) +into each of the first +.Fa n +bytes of the memory buffer whose starting address is given by +.Fa s . +.Pp +It is a runtime-consrtaints violation if +.Fa s +is a null pointer, +or if either of +.Fa smax +or +.Fa n +is larger than +.Dv RSIZE_MAX , +or if +.Fa smax +is smaller than +.Fa n . +If there is a runtime-constraints violation, and if +.Fa s +is not a null pointer, +and if +.Fa smax +is not larger than +.Dv RSIZE_MAX , +then, before reporting the runtime-constraints violation, +.Fn memset_s +copies +.Fa smax +bytes to the destination. +.Pp +In contrast to the +.Xr memset 3 +function, +calls to +.Fn memset_s +will never be +.Dq optimised away +by a compiler. +This property is required by the following sentences in +section K.3.7.4.1 of +.St -isoC-2011 : +.Bd -filled -offset indent +Unlike +.Fn memset , +any call to the +.Fn memset_s +function shall be evaluated strictly according to the rules of +the abstract machine as described in (5.1.2.3). +That is, any call to the +.Fn memset_s +function shall assume that the memory indicated by +.Fa s +and +.Fa n +may be accessible in the future and thus must contain +the values indicated by +.Fa c . +.Ed +.Sh RETURN VALUES +The +.Fn memset_s +function returns zero for success, or a non-zero error code +if there was a runtime-constraints violation. +.Sh ERRORS +.Fn memset_s +returns the following error codes, +and also stores the error codes in the global +.Va errno +variable: +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa s +argument was a null pointer. +.It Bq Er E2BIG +One or both of +.Fa smax +or +.Fa n +was larger than +.Dv RSIZE_MAX . +.It Bq Er EOVERFLOW +.Fa n +was larger than +.Fa smax . +.El +. +.Sh SEE ALSO +.Xr memset 3 . +.Sh STANDARDS +The +.Fn memset_s +function conforms to +.St -isoC-2011 , +except that the +.Fn set_constraint_handler_s +interface is not supported. diff --git a/lib/libc/string/memset_s.c b/lib/libc/string/memset_s.c new file mode 100644 index 0000000000..6e67471f08 --- /dev/null +++ b/lib/libc/string/memset_s.c @@ -0,0 +1,87 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Alan Barrett + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ISO/IEC 9899:2011 section K.3.7.4.1 The memset_s function + */ + +#include + +__RCSID("$NetBSD$"); + +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include + +/* + * __memset_vp is a volatile pointer to a function. + * It is initialised to point to memset, and should never be changed. + */ +static void * (* const volatile __memset_vp)(void *, int, size_t) + = (memset); + +#undef memset_s /* in case it was defined as a macro */ + +errno_t +memset_s(void *s, rsize_t smax, int c, rsize_t n) +{ + errno_t err = 0; + + if (s == NULL) { + err = EINVAL; + goto out; + } + if (smax > RSIZE_MAX) { + err = E2BIG; + goto out; + } + if (n > RSIZE_MAX) { + err = E2BIG; + n = smax; + } + if (n > smax) { + err = EOVERFLOW; + n = smax; + } + + /* Calling through a volatile pointer should never be optimised away. */ + (*__memset_vp)(s, c, n); + + out: + if (err == 0) + return 0; + else { + errno = err; + /* XXX call runtime-constraint handler */ + return err; + } +} diff --git a/lib/libc/string/string.3 b/lib/libc/string/string.3 index a05a76e5c3..0f4bf5eae9 100644 --- a/lib/libc/string/string.3 +++ b/lib/libc/string/string.3 @@ -145,6 +145,33 @@ strings (without the nul byte check), see Except as noted in their specific manual pages, the string functions do not test the destination for size limitations. +.Pp +In accordance with Annex K of +.St -isoC-2011 , +if the macro definition +.D1 "#define __STDC_WANT_LIB_EXT1__ 1" +appears before +.D1 "#include " +then +.In string.h +also defines the following types: +.Bl -tag +.It Vt errno_t +a type which is the same as +.Vt int . +This type is intended to be used to hold error numbers, +such as are stored in the +.Va errno +variable or returned by some functions. +.It Vt rsize_t +a type which is the same as +.Vt size_t . +This type is intended to be used to represent the sizes of +objects that will not exceed +.Dv RSIZE_MAX +as defined in +.In stdint.h . +.El .Sh SEE ALSO .Xr bstring 3 , .Xr strcat 3 , diff --git a/lib/libc/sys/intro.2 b/lib/libc/sys/intro.2 index 9718746d93..c06740fd2f 100644 --- a/lib/libc/sys/intro.2 +++ b/lib/libc/sys/intro.2 @@ -29,7 +29,7 @@ .\" .\" @(#)intro.2 8.5 (Berkeley) 2/27/95 .\" -.Dd July 23, 2009 +.Dd February 22, 2012 .Dt INTRO 2 .Os .Sh NAME @@ -51,7 +51,7 @@ variable .Va errno . .Va errno is implemented as a macro which expands to a modifiable lvalue of type -.Fa int . +.Vt int . .Pp When a system call detects an error, it returns an integer value @@ -89,6 +89,19 @@ Note also that a number of system calls overload the meanings of these error numbers, and that in these cases the meanings must be interpreted according to the type and circumstances of the call. .Pp +In accordance with Annex K of +.St -isoC-2011 , +if the macro definition +.D1 "#define __STDC_WANT_LIB_EXT1__ 1" +appears before +.D1 "#include " +then +.In errno.h +defines the type +.Vt errno_t +as another name for the type +.Vt int . +.Pp The following is a complete list of the errors and their names as given in .In errno.h . diff --git a/share/man/man3/stddef.3 b/share/man/man3/stddef.3 index 3c913b5091..bbcc654099 100644 --- a/share/man/man3/stddef.3 +++ b/share/man/man3/stddef.3 @@ -1,6 +1,6 @@ .\" $NetBSD: stddef.3,v 1.8 2011/04/10 10:02:34 jruoho Exp $ .\" -.\" Copyright (c) 2010 The NetBSD Foundation, Inc. +.\" Copyright (c) 2010,2011,2012 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd April 10, 2011 +.Dd February 22, 2012 .Dt STDDEF 3 .Os .Sh NAME @@ -39,34 +39,50 @@ The .In stddef.h header defines the following types and macros: -.Bl -enum -offset 4n -.It -.Vt ptrdiff_t , +.Bl -tag +.It Vt ptrdiff_t a signed integer type of the result of subtracting two pointers; -.It -.Vt size_t , +.It Vt size_t an unsigned integer type of the result of the .Fn sizeof operator; -.It -.Vt wchar_t , +.It Vt wchar_t an integer type whose range of values can represent distinct wide-character codes for all members of the largest character set specified among the supported locales: the null character has the code value 0 and each member of the character set has a code value equal to its value when used as the lone character in an integer character constant; -.It -.Dv NULL , -which expands to an implementation-defined null pointer constant; and -.It -.Fn offsetof , +.It Dv NULL +a macro that expands to an implementation-defined null pointer constant; +.It Fn offsetof "type" "member" a macro that expands to an integer constant as described in .Xr offsetof 3 . .El .Pp -Some of the described types and macros may appear also in other headers. +In accordance with Annex K of +.St -isoC-2011 , +if the macro definition +.D1 "#define __STDC_WANT_LIB_EXT1__ 1" +appears before +.D1 "#include " +then +.In stddef.h +also defines the following type: +.Bl -tag +.It Vt rsize_t +a type which is the same as +.Vt size_t . +This type is intended to be used to represent the sizes of +objects that will not exceed +.Dv RSIZE_MAX +as defined in +.In stdint.h . +.El +.Pp +Some of the described types and macros may also appear in other headers. .Sh SEE ALSO .Xr offsetof 3 , +.Xr stdint 3 , .Xr stdlib 3 , .Xr unistd 3 .Sh STANDARDS @@ -76,6 +92,8 @@ header conforms to .St -isoC-99 and .St -p1003.1-2001 . +Some of the types and macros conform to +.St -isoC-2011. Some of the types and macros conform to earlier standards such as .St -ansiC . .Sh HISTORY diff --git a/share/man/man3/stdint.3 b/share/man/man3/stdint.3 index 3d75508300..f2703506b9 100644 --- a/share/man/man3/stdint.3 +++ b/share/man/man3/stdint.3 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD: src/share/man/man7/stdint.7,v 1.5 2003/09/08 19:57:21 ru Exp $ .\" -.Dd August 9, 2011 +.Dd February 22, 2012 .Dt STDINT 3 .Os .Sh NAME @@ -120,6 +120,33 @@ provides an unsigned integer type with the ability to hold a pointer to .Vt void , that can later be converted back to a pointer to .Vt void . +.Pp +In accordance with Annex K of +.St -isoC-2011 , +if the macro definition +.D1 "#define __STDC_WANT_LIB_EXT1__ 1" +appears before +.D1 "#include " +then +.In stdint.h +also defines the following macro: +.Bl -tag +.It Dv RSIZE_MAX +A value of type +.Vt size_t +which represents the largest reasonable size for an object. +.Dv RSIZE_MAX +may be smaller than the size of the largest possible object, +but it is large enough that an attempt to use an object larger than +.Dv RSIZE_MAX +is likely to be the result of a programming error. +Functions that take parameters of type +.Vt rsize_t , +representing the size of an object, +should consider it a runtime-constraints violation +if the size exceeds +.Dv RSIZE_MAX . +.El .Sh SEE ALSO .Xr inttypes 3 , .Xr limits 3 , @@ -132,6 +159,8 @@ header conforms to .St -isoC-99 and .St -p1003.1-2001 . +Some of the types and macros conform to +.St -isoC-2011 . .Sh HISTORY The .In stdint.h diff --git a/sys/sys/stdint.h b/sys/sys/stdint.h index a6ae2407ea..2ca222d4b9 100644 --- a/sys/sys/stdint.h +++ b/sys/sys/stdint.h @@ -33,7 +33,7 @@ #define _SYS_STDINT_H_ #include -#include +#include #ifndef int8_t typedef __int8_t int8_t; @@ -89,7 +89,31 @@ typedef __uintptr_t uintptr_t; #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) #include + +#if (__STDC_WANT_LIB_EXT1__ - 0 == 1) || defined(_NETBSD_SOURCE) +/* + * ISO/IEC 9899:2011 section K.3.4 Integer types + * + * Macros: + * RSIZE_MAX + * + * RSIZE_MAX is the largest size that it's reasonable to pass to the + * bounds-checking "foo_s" functions defined in Annex K of the C2011 + * standard. We set this to 2 gigabytes (minus one byte), even on + * machines that have much more memory than that. + * + * RSIZE_MAX has to be the same type as size_t, but size_t might + * not yet be defined. We shouldn't really define size_t here, + * but there's no easy way to avoid it. + */ +#ifdef _BSD_SIZE_T_ +typedef _BSD_SIZE_T_ size_t; +#undef _BSD_SIZE_T_ #endif +#define RSIZE_MAX ((size_t)0x7fffffffUL) +__CTASSERT(/*CONSTCOND*/ RSIZE_MAX < SIZE_MAX) +#endif /* __STDC_WANT_LIB_EXT1__ */ +#endif /* __STDC_LIMIT_MACROS */ #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) #include diff --git a/tests/lib/libc/string/Makefile b/tests/lib/libc/string/Makefile index 9bd1959488..33a653ad77 100644 --- a/tests/lib/libc/string/Makefile +++ b/tests/lib/libc/string/Makefile @@ -8,6 +8,7 @@ TESTS_C+= t_memchr TESTS_C+= t_memcpy TESTS_C+= t_memmem TESTS_C+= t_memset +TESTS_C+= t_memset_s TESTS_C+= t_popcount TESTS_C+= t_strcat TESTS_C+= t_strchr @@ -22,6 +23,12 @@ TESTS_C+= t_strrchr TESTS_C+= t_strspn TESTS_C+= t_swab +TESTS_SH+= t_memset_optimise + +FILESDIR= ${TESTSDIR} +FILES+= h_memset_caller.c +FILES+= h_memset_s_caller.c + WARNS= 4 .include diff --git a/tests/lib/libc/string/h_memset_caller.c b/tests/lib/libc/string/h_memset_caller.c new file mode 100644 index 0000000000..b37ff6be22 --- /dev/null +++ b/tests/lib/libc/string/h_memset_caller.c @@ -0,0 +1,15 @@ +#include +#include + +/* + * The call to memset may be optimised away, because the compiler + * can see that the memory is freed immediataly. + */ +void f(void) +{ + size_t len = 10; + char *buf = malloc(len); + memset(buf, 0, len); + free(buf); +} + diff --git a/tests/lib/libc/string/h_memset_s_caller.c b/tests/lib/libc/string/h_memset_s_caller.c new file mode 100644 index 0000000000..e362bf3d7a --- /dev/null +++ b/tests/lib/libc/string/h_memset_s_caller.c @@ -0,0 +1,15 @@ +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include + +/* + * The call to memset_s must never be optimised away. + */ +void f(void) +{ + size_t len = 10; + char *buf = malloc(len); + memset_s(buf, len, 0, len); + free(buf); +} + diff --git a/tests/lib/libc/string/t_memset_optimise.sh b/tests/lib/libc/string/t_memset_optimise.sh new file mode 100644 index 0000000000..0e050bd2f8 --- /dev/null +++ b/tests/lib/libc/string/t_memset_optimise.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env atf-sh +# +# $NetBSD$ +# +# Copyright (c) 2012 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Alan Barrett +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +# test_with_compiler -- test how a compiler optimises memset and memset_s calls +# +# compiler - the compiler command, e.g. "/usr/bin/gcc", or just "gcc". +# common_flags - flags to pass to every invocation of the compiler, +# to make it output a *.o file, e.g. "-c". +# remaining args - a list of optimisation flags, to be used in +# one or more invocations of the compiler. Put the most aggressive +# optimisation flags first so that the test has a chance of exiting +# early. e.g. "-O3" "-Os" "-O2" "-O1" +# +# We compile two test functions, one that does malloc/memset/free, +# and another that does malloc/memset_s/free. The memset call +# may be optimised away, because the compiler may think that the +# memory is not used after memset returns. However, ISO/IEC 9899:2011 +# requires that the call to memset_s must not be optimised away. +# +test_with_compiler() +{ + local compiler="$1" ; shift + local common_flags="$1" ; shift + + local compile_failed=0 + local memset_used=0 + local memset_gone=0 + local memset_s_used=0 + local memset_s_gone=0 + + local infile outfile + local used + + if ! command -v "$compiler" >/dev/null; then + atf_skip "$compiler not found" + return + fi + + for f in memset memset_s ; do + for optimisation_flags in "$@"; do + infile="$(atf_get_srcdir)/h_${f}_caller.c" + outfile="h_${f}_caller.o" + + eval "$compiler $common_flags $optimisation_flags \"\$infile\"" \ + || compile_failed=$(( compile_failed + 1 )) + + if nm "$outfile" | grep "U.*${f}" >/dev/null; then + used=true + else + used=false + fi + + rm -f "$outfile" + + if $used; then + echo "$compiler $optimisation_flags: ${f} is called" + eval "${f}_used=\$(( ${f}_used + 1 ))" + else + echo "$compiler $optimisation_flags: ${f} optimised away" + eval "${f}_gone=\$(( ${f}_gone + 1 ))" + # As soon as we find a set of optimisation flags that + # causes the call to be optimised away, we don't need to + # repeat with any other optimisation flags. + break + fi + + done + done + + if [ $compile_failed != 0 ]; then + # If the compiler reported an error, then the test failed. + # + atf_fail "$compiler failed to compile test case" + elif [ $memset_s_gone != 0 ]; then + # If any call to memset_s was optimised away, then the test failed. + # + atf_fail "$compiler optimised away call to memset_s" + elif [ $memset_gone != 0 ] && [ $memset_s_used != 0 ]; then + # If the compiler optimised away at least one call to memset, + # but did not optimise away any call to memset_s, then the + # test passed. + # + atf_pass + else + # If the compiler did not optimise away any of the calls + # to memset or to memset_s, then the test is inconclusive; + # Perhaps different optimisation flags would make it optimise + # some of the calls away? + # + # XXX Is there something better than "atf_skip" to indicate + # a test that did not produce a conclusive result? + # + atf_skip "$compiler did not optimise away any calls" + fi +} + +atf_test_case gcc_optimise_memset_s +gcc_optimise_memset_s_head() +{ + atf_set "descr" "gcc must not optimise away calls to memset_s" +} +gcc_optimise_memset_s_body() +{ + test_with_compiler "gcc" "-c" "-O3" "-Os" "-O2" "-O1" "-O0" +} + +atf_test_case clang_optimise_memset_s +clang_optimise_memset_s_head() +{ + atf_set "descr" "clang must not optimise away calls to memset_s" +} +clang_optimise_memset_s_body() +{ + test_with_compiler "clang" "-c" "-O3" "-Os" +} + +atf_test_case pcc_optimise_memset_s +pcc_optimise_memset_s_head() +{ + atf_set "descr" "pcc must not optimise away calls to memset_s" +} +pcc_optimise_memset_s_body() +{ + test_with_compiler "pcc" "-c" "-O3" "-Os" +} + +atf_init_test_cases() +{ + atf_add_test_case gcc_optimise_memset_s + atf_add_test_case clang_optimise_memset_s + atf_add_test_case pcc_optimise_memset_s +} diff --git a/tests/lib/libc/string/t_memset_s.c b/tests/lib/libc/string/t_memset_s.c new file mode 100644 index 0000000000..268c1972f5 --- /dev/null +++ b/tests/lib/libc/string/t_memset_s.c @@ -0,0 +1,162 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2011,2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jukka Ruohonen and Alan Barrett. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD"); + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include + +#include +#include +#include +#include +#include + +static long page = 0; +static void fill(char *, size_t, char); + +ATF_TC(memset_s_basic); +ATF_TC_HEAD(memset_s_basic, tc) +{ + atf_tc_set_md_var(tc, "descr", "A basic test of memset_s(3)"); +} + +ATF_TC_BODY(memset_s_basic, tc) +{ + char *buf, *ret; + errno_t status; + + buf = malloc(page); + ret = malloc(page); + + ATF_REQUIRE(buf != NULL); + ATF_REQUIRE(ret != NULL); + + fill(ret, page, 0); + status = memset_s(buf, page, 0, page); + + ATF_REQUIRE(status == 0); + ATF_REQUIRE(memcmp(ret, buf, page) == 0); + + fill(ret, page, 'x'); + status = memset_s(buf, page, 'x', page); + + ATF_REQUIRE(status == 0); + ATF_REQUIRE(memcmp(ret, buf, page) == 0); + + free(buf); + free(ret); +} + +ATF_TC(memset_s_errors); +ATF_TC_HEAD(memset_s_errors, tc) +{ + atf_tc_set_md_var(tc, "descr", "A test of memset_s(3) error handling"); +} + +ATF_TC_BODY(memset_s_errors, tc) +{ + char *buf, *ret; + errno_t status; + + buf = malloc(page); + ret = malloc(page); + + ATF_REQUIRE(buf != NULL); + ATF_REQUIRE(ret != NULL); + + /* + * s is a null pointer: + * It should just return an error. + */ + status = memset_s(NULL, page, 0, page); + + ATF_REQUIRE(status != 0); + + /* + * n < smax, n < RSIZE_MAX, smax == RSIZE_MAX: + * Not an error. It should write n bytes. + */ + fill(ret, page, 0); + fill(buf, page, 'x'); + status = memset_s(buf, RSIZE_MAX, 0, page); + + ATF_REQUIRE(status == 0); + ATF_REQUIRE(memcmp(ret, buf, page) == 0); + + /* + * n > smax, n > RSIZE_MAX, smax < RSIZE_MAX: + * it should write smax bytes, and return an error. + */ + fill(ret, page, 0); + fill(buf, page, 'x'); + status = memset_s(buf, page - 1, 0, RSIZE_MAX + 1); + + ATF_REQUIRE(status != 0); + ATF_REQUIRE(memcmp(ret, buf, page - 1) == 0); + ATF_REQUIRE(buf[page-1] == 'x'); + + /* + * n > smax, n < RSIZE_MAX, smax > RSIZE_MAX: + * it should return an error, without writing anything. + */ + fill(ret, page, 'x'); + fill(buf, page, 'x'); + status = memset_s(buf, RSIZE_MAX + 1, 0, page); + + ATF_REQUIRE(status != 0); + ATF_REQUIRE(memcmp(ret, buf, page) == 0); + + free(buf); + free(ret); +} + +static void +fill(char *buf, size_t len, char x) +{ + size_t i; + + for (i = 0; i < len; i++) + buf[i] = x; +} + +ATF_TP_ADD_TCS(tp) +{ + + page = sysconf(_SC_PAGESIZE); + ATF_REQUIRE(page >= 0); + + ATF_TP_ADD_TC(tp, memset_s_basic); + ATF_TP_ADD_TC(tp, memset_s_errors); + + return atf_no_error(); +}