Index: lib/libc/stdio/Makefile.inc =================================================================== --- lib/libc/stdio/Makefile.inc +++ lib/libc/stdio/Makefile.inc @@ -14,7 +14,7 @@ fputwc.c fputws.c fread.c freopen.c fscanf.c fseek.c fsetpos.c \ ftell.c funopen.c fvwrite.c fwalk.c fwide.c fwprintf.c fwscanf.c \ fwrite.c getc.c getchar.c getdelim.c getline.c \ - gets.c getw.c getwc.c getwchar.c makebuf.c mktemp.c \ + gets.c gets_s.c getw.c getwc.c getwchar.c makebuf.c mktemp.c \ open_memstream.c open_wmemstream.c \ perror.c printf.c printf-pos.c putc.c putchar.c \ puts.c putw.c putwc.c putwchar.c \ @@ -50,6 +50,7 @@ ferror.3 fileno.3 ferror.3 fileno_unlocked.3 MLINKS+=fflush.3 fpurge.3 MLINKS+=fgets.3 gets.3 +MLINKS+=fgets.3 gets_s.3 MLINKS+=flockfile.3 ftrylockfile.3 flockfile.3 funlockfile.3 MLINKS+=fopen.3 fdopen.3 fopen.3 freopen.3 fopen.3 fmemopen.3 MLINKS+=fputs.3 puts.3 Index: lib/libc/stdio/Symbol.map =================================================================== --- lib/libc/stdio/Symbol.map +++ lib/libc/stdio/Symbol.map @@ -165,6 +165,7 @@ FBSD_1.4 { fdclose; fopencookie; + gets_s; }; FBSDprivate_1.0 { Index: lib/libc/stdio/fgets.3 =================================================================== --- lib/libc/stdio/fgets.3 +++ lib/libc/stdio/fgets.3 @@ -46,6 +46,8 @@ .Ft char * .Fn fgets "char * restrict str" "int size" "FILE * restrict stream" .Ft char * +.Fn gets_s "char *str" "rsize_t size" +.Ft char * .Fn gets "char *str" .Sh DESCRIPTION The @@ -65,6 +67,17 @@ character is appended to end the string. .Pp The +.Fn gets_s +function +is equivalent to +.Fn fgets +with a +.Fa stream +of +.Dv stdin , +except that the newline character (if any) is not stored in the string. +.Pp +The .Fn gets function is equivalent to @@ -80,7 +93,8 @@ if any, is sufficiently short to fit in the string. .Sh RETURN VALUES Upon successful completion, -.Fn fgets +.Fn fgets , +.Fn gets_s and .Fn gets return @@ -94,7 +108,8 @@ .Dv NULL and the buffer contents are indeterminate. The -.Fn fgets +.Fn fgets , +.Fn gets_s and .Fn gets functions @@ -141,6 +156,13 @@ .Fn gets conform to .St -isoC-99 . +.Fn gets_s +conforms to +.St -isoC-2011 +K.3.7.4.1. +.Fn gets +has been removed from +.St -isoC-2011 . .Sh SECURITY CONSIDERATIONS The .Fn gets Index: lib/libc/stdio/gets_s.c =================================================================== --- lib/libc/stdio/gets_s.c +++ lib/libc/stdio/gets_s.c @@ -3,6 +3,8 @@ * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. + * Copyright (c) 2017, 2018 + * Cyril S. E. Schubert. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. @@ -32,49 +34,69 @@ * SUCH DAMAGE. */ -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)gets.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" +#include #include +#include #include #include "un-namespace.h" #include "libc_private.h" #include "local.h" -__warn_references(gets, "warning: this program uses gets(), which is unsafe."); - -char * -gets(char *buf) +static inline char * +_gets_s(char *buf, rsize_t n) { int c; - char *s, *ret; - static int warned; - static const char w[] = - "warning: this program uses gets(), which is unsafe.\n"; + char *s; - FLOCKFILE_CANCELSAFE(stdin); ORIENT(stdin, -1); - if (!warned) { - (void) _write(STDERR_FILENO, w, sizeof(w) - 1); - warned = 1; - } - for (s = buf; (c = __sgetc(stdin)) != '\n'; ) { + for (s = buf, n--; (c = __sgetc(stdin)) != '\n' && n > 0 ; n--) { if (c == EOF) { if (s == buf) { - ret = NULL; - goto end; + return (NULL); } else break; } else *s++ = c; } + + /* + * If end of buffer reached, discard until \n or eof. + * Then throw an error. + */ + if (n == 0) { + /* discard */ + while ((c = __sgetc(stdin)) != '\n' && c != EOF); + /* throw the error after lock released prior to exit */ + __throw_constraint_handler_s("gets_s : end of buffer", E2BIG); + return (NULL); + } *s = 0; - ret = buf; -end: + return (buf); +} + +/* ISO/IEC 9899:2011 K.3.7.4.1 */ +char * +gets_s(char *buf, rsize_t n) +{ + char *ret; + if (buf == NULL) { + __throw_constraint_handler_s("gets_s : str is NULL", EINVAL); + return(NULL); + } else if (n > RSIZE_MAX) { + __throw_constraint_handler_s("gets_s : n > RSIZE_MAX", + EINVAL); + return(NULL); + } else if (n == 0) { + __throw_constraint_handler_s("gets_s : n == 0", EINVAL); + return(NULL); + } + + FLOCKFILE_CANCELSAFE(stdin); + ret = _gets_s(buf, n); FUNLOCKFILE_CANCELSAFE(); return (ret); } Index: lib/libc/tests/stdio/Makefile =================================================================== --- lib/libc/tests/stdio/Makefile +++ lib/libc/tests/stdio/Makefile @@ -7,6 +7,7 @@ ATF_TESTS_C+= fopen2_test ATF_TESTS_C+= freopen_test ATF_TESTS_C+= getdelim_test +ATF_TESTS_C+= gets_s_test ATF_TESTS_C+= mkostemp_test ATF_TESTS_C+= open_memstream2_test ATF_TESTS_C+= open_wmemstream_test Index: lib/libc/tests/stdio/gets_s_test.c =================================================================== --- lib/libc/tests/stdio/gets_s_test.c +++ lib/libc/tests/stdio/gets_s_test.c @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2017 Cyril S. E. Schubert. All rights reserved. + * + * 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 REGENTS 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 REGENTS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +static errno_t error_code; +static const char * message; + +void +h(const char * msg, void * ptr __unused, errno_t error) +{ + error_code = error; + message = msg; +} + +/* null ptr */ +ATF_TC_WITHOUT_HEAD(null_ptr); +ATF_TC_BODY(null_ptr, tc) +{ + ATF_CHECK_MSG(gets_s(NULL, 1) == NULL, + "gets_s() failed to handle NULL pointer"); +} + +/* normal */ +ATF_TC_WITHOUT_HEAD(normal); +ATF_TC_BODY(normal, tc) +{ + pid_t kidpid; + int fd[2]; + int nfd; + + // close(STDIN_FILENO); + // close(STDOUT_FILENO); + pipe(fd); + + if ((kidpid = fork()) == 0) { + char b[10]; + + close(fd[1]); + nfd = dup2(fd[0], 0); + close(fd[0]); + stdin = fdopen(nfd, "r"); + ATF_CHECK_MSG(gets_s(b, sizeof(b)) == 0, "gets_s() normal failed"); + fclose(stdin); + } else { + int stat; + + close(fd[0]); + stdout = fdopen(fd[1], "w"); + puts("a sting"); + fclose(stdout); + (void) waitpid(kidpid, &stat, WEXITED); + } +} + +/* n > rmax */ +ATF_TC_WITHOUT_HEAD(n_gt_rmax); +ATF_TC_BODY(n_gt_rmax, tc) +{ + char b; + + ATF_CHECK_MSG(gets_s(&b, RSIZE_MAX + 1) == NULL, + "gets_s() n > RSIZE_MAX"); +} + +/* n == 0 */ +ATF_TC_WITHOUT_HEAD(n_eq_zero); +ATF_TC_BODY(n_eq_zero, tc) +{ + char b; + + ATF_CHECK_MSG(gets_s(&b, 0) == NULL, "gets_s() n is zero"); +} + +/* n > rmax, handler */ +ATF_TC_WITHOUT_HEAD(n_gt_rmax_handler); +ATF_TC_BODY(n_gt_rmax_handler, tc) +{ + char b; + + error_code = 0; + message = NULL; + set_constraint_handler_s(h); + ATF_CHECK_MSG(gets_s(&b, RSIZE_MAX + 1) == NULL, "gets_s() n > RSIZE_MAX"); + ATF_CHECK_MSG(error_code > 0, "gets_s() error code is %d", error_code); + ATF_CHECK_MSG(strcmp(message, "gets_s : n > RSIZE_MAX") == 0, "gets_s(): incorrect error message"); +} + +/* n == 0, handler */ +ATF_TC_WITHOUT_HEAD(n_eq_zero_handler); +ATF_TC_BODY(n_eq_zero_handler, tc) +{ + char b; + + error_code = 0; + message = NULL; + set_constraint_handler_s(h); + ATF_CHECK(gets_s(&b, 0) == NULL); + ATF_CHECK_MSG(error_code > 0, "gets_s() error code is %d", error_code); + ATF_CHECK_MSG(strcmp(message, "gets_s : n == 0") == 0, "gets_s(): incorrect error message"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, null_ptr); + ATF_TP_ADD_TC(tp, normal); + ATF_TP_ADD_TC(tp, n_gt_rmax); + ATF_TP_ADD_TC(tp, n_eq_zero); + ATF_TP_ADD_TC(tp, n_gt_rmax_handler); + ATF_TP_ADD_TC(tp, n_eq_zero_handler); + return (atf_no_error()); +}