Index: include/stdio.h =================================================================== --- include/stdio.h +++ include/stdio.h @@ -36,9 +36,7 @@ #ifndef _STDIO_H_ #define _STDIO_H_ -#include -#include -#include +#include __NULLABILITY_PRAGMA_PUSH @@ -263,6 +261,9 @@ int getc(FILE *); int getchar(void); char *gets(char *); +#if defined(__EXT1_VISIBLE) && __EXT1_VISIBLE == 1 +char *gets_s(char *, rsize_t); +#endif void perror(const char *); int printf(const char * __restrict, ...); int putc(int, FILE *); Index: lib/libc/stdio/Makefile.inc =================================================================== --- lib/libc/stdio/Makefile.inc +++ lib/libc/stdio/Makefile.inc @@ -49,7 +49,8 @@ ferror.3 feof.3 ferror.3 feof_unlocked.3 \ ferror.3 fileno.3 ferror.3 fileno_unlocked.3 MLINKS+=fflush.3 fpurge.3 -MLINKS+=fgets.3 gets.3 +MLINKS+=fgets.3 gets.3 gets_s.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 Index: lib/libc/stdio/gets.c =================================================================== --- lib/libc/stdio/gets.c +++ lib/libc/stdio/gets.c @@ -37,7 +37,9 @@ __FBSDID("$FreeBSD$"); #include "namespace.h" +#include #include +#include #include #include #include "un-namespace.h" @@ -46,22 +48,32 @@ __warn_references(gets, "warning: this program uses gets(), which is unsafe."); +/* ISO/IEC 9899:2011 K.3.7.4.1 */ +static char * -gets(char *buf) +_gets_s(char *buf, rsize_t *n) { int c; + int nn; char *s, *ret; - static int warned; - static const char w[] = - "warning: this program uses gets(), which is unsafe.\n"; + +#define GETS_S_CALL (n != NULL) + if ( GETS_S_CALL ) { + /* gets_s() */ + nn = *n; + } else { + /* Old school gets() call, not a gets_s() call. */ + /* nn could be anything, it's inconsequential at this */ + /* point. It's not used by old school gets(). This is */ + /* done to pet the compiler. */ + nn = 0; + } 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, nn--; ((c = __sgetc(stdin)) != '\n' && + ((nn > 0 && GETS_S_CALL) || ! GETS_S_CALL)); nn--) { if (c == EOF) if (s == buf) { ret = NULL; @@ -71,9 +83,60 @@ else *s++ = c; } + + /****************************************************************/ + /* */ + /* If end of buffer reached, discard until \n or eof. */ + /* Then throw an error. */ + /* */ + /****************************************************************/ + if ( GETS_S_CALL && nn == 0) { + /* discard */ + while (((c = __sgetc(stdin)) != '\n') && c != EOF); + /* throw the error */ + __throw_constraint_handler_s("gets_s : end of buffer", E2BIG); + return(NULL); + } + *s = 0; ret = buf; end: FUNLOCKFILE_CANCELSAFE(); return (ret); } + +/* ISO/IEC 9899:2011 K.3.7.4.1 */ +char * +gets_s(char *buf, rsize_t n) +{ + 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); + } + return(_gets_s(buf, &n)); +} + +#define __GETS_ENABLED +#ifdef __GETS_ENABLED +char * +gets(char *buf) +{ + static int warned; + static const char w[] = + "warning: this program uses gets(), which is unsafe.\n"; + + if (!warned) { + (void) _write(STDERR_FILENO, w, sizeof(w) - 1); + warned = 1; + } + + return(_gets_s(buf, NULL)); +} +#endif