In fact, if I knew how to enable such a message on my systems I *would*
enable it and remove any SW that used gets. I would especially like such
a warning enabled on the various servers I have some control over, since
I take security of servers very seriously and getting rid of
fundamentally unsafe SW is a good step towards security.
Ditto! I was only recently tempted to use library preloading and
override the standard gets() function with a function of my own, which
can be tuned to do one of the following:
1. Unconditionally convert any call to gets() to abort().
2. Unconditionally convert any call to gets() to an error message on
stderr, and a call to _exit().
3. Unconditionally convert any call to gets() to a warning message
(like the one printed by the C runtime library on FreeBSD).
4. Do nothing, but keep the 'standard' behavior of gets(), even if the
result is not optimal performance-wise.
The gets() implementation attached below seems to work for me on
FreeBSD, and I am about to test it with some larger programs on Lixux
and Solaris too in a few hours (I know this is far from a full
portability test, but it would be enough to make this modified gets()
function useful for my own work):
%%%
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define GETS_MODE_DEFAULT 0
#define GETS_MODE_WARN 1
#define GETS_MODE_EXIT 2
#define GETS_MODE_ABORT 3
char *
gets(char *buf)
{
char *options;
char *p;
int ch;
int mode;
static int warned = 0;
static char w[] =
"this program uses gets(), which is unsafe.";
mode = GETS_MODE_DEFAULT;
options = getenv("GETS_OPTIONS");
if (options != NULL) {
for (p = options; *p != '\0'; p++) {
switch (*p) {
case 'w':
case 'W':
mode = GETS_MODE_WARN;
break;
case 'e':
case 'E':
mode = GETS_MODE_EXIT;
break;
case 'a':
case 'A':
mode = GETS_MODE_ABORT;
break;
case 'd':
case 'D':
mode = GETS_MODE_DEFAULT;
break;
default:
break;
}
}
}
if (mode == GETS_MODE_ABORT) {
abort();
} else if (mode == GETS_MODE_EXIT) {
fprintf(stderr, "error: %s\n", w);
_exit(EXIT_FAILURE);
} else if (mode == GETS_MODE_WARN && warned == 0) {
fprintf(stderr, "warning: %s\n", w);
warned = 1;
}
p = buf;
while (ferror(stdin) == 0 && (ch = getchar()) != EOF && ch != '\n')
*p++ = (char)ch;
if (ferror(stdin) != 0)
return NULL;
*p = '\0';
return (buf);
}
%%%
The result so far seem to be ok, since a small test program which does
use gets() appears to fail in the expected ways when the modified gets()
is preloaded:
% $ pwd
% /home/keramida
%
% $ cat foo.c
% #include <stdio.h>
% #include <stdlib.h>
%
% int
% main(void)
% {
% char buf[100];
%
% gets(buf);
% printf("%s\n", buf);
% return EXIT_SUCCESS;
% }
%
% $ cc -fPIC -c -o gets.o gets.c
%
% $ ld -G -o libcompat.so gets.o
%
% $ echo foo bar | \
% env GETS_OPTIONS='' LD_PRELOAD=/home/keramida/libcompat.so ./a.out
% foo bar
%
% $ echo foo bar | \
% env GETS_OPTIONS='w' LD_PRELOAD=/home/keramida/libcompat.so ./a.out
% warning: this program uses gets(), which is unsafe.
% foo bar
%
% $ echo foo bar | \
% env GETS_OPTIONS='e' LD_PRELOAD=/home/keramida/libcompat.so ./a.out
% error: this program uses gets(), which is unsafe.
%
% $ echo foo bar | \
% env GETS_OPTIONS='a' LD_PRELOAD=/home/keramida/libcompat.so ./a.out
% Abort trap: 6 (core dumped)
%
% $ gdb a.out a.out.core
% [...]
% (gdb) bt
% #0 0x28151d73 in kill () at kill.S:2
% #1 0x28151d10 in __raise (s=6) at /home/build/src/lib/libc/gen/raise.c:46
% #2 0x28150a20 in abort () at /home/build/src/lib/libc/stdlib/abort.c:65
% #3 0x2807d52e in gets () from /home/keramida/libcompat.so
% #4 0x0804853e in main ()
% Current language: auto; currently asm
% (gdb)