gets, fgets, scanf none is safe...

X

Xavoux

Hello all...
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...

i compiled that code with gcc version 2.95.2, on windows 2000

char tmp0[10] = "ABCDEFGHI\0";
char buff[5]; /* Input buffer. */
char tmp1[10] = "ABCDEFGHI\0";

/* Get data from the keyboard. */
printf("\nGETS : please enter text => \n");

gets(buff);

printf("\nGETS : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

/* Get data from the keyboard. */
printf("\nFGETS : please enter text => \n");

fgets(buff, sizeof(buff), stdin);

printf("\nFGETS : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

printf("\nSCANF : please enter text => \n");

scanf("%s", buff);

printf("\nSCANF : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);
 
I

Ian Collins

Xavoux said:
Hello all...
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...
Look at their prototypes, which one has an input size limit?
 
P

pete

Xavoux said:
Hello all...
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...

grade.c shows how to use fscanf for the safe input of strings
for the specific case of when it is OK to ignore
input that excedes the size of the input buffer.
The strings can then be converted to numeric data.

pops_device.c also shows how to use fscanf
for the safe input of strings
for the specific case of when it is OK to ignore
input that excedes the size of the input buffer.

line_to_string.c shows how to read strings of arbitrary
length into a linked list, using dynamic allocation.

/* BEGIN grade.c */

#include <stdlib.h>
#include <stdio.h>

#define LENGTH 3
#define str(x) # x
#define xstr(x) str(x)

int main(void)
{
int rc;
char array[LENGTH + 1];
long number;
const char letter[4] = "DCBA";

fputs("Enter the Numeric grade: ", stdout);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getc(stdin);
}
while (rc == 1) {
number = strtol(array, NULL, 10);
if (number > 59) {
if (number > 99) {
number = 99;
}
array[0] = letter[(number - 60) / 10];
switch (number % 10) {
case 0:
case 1:
array[1] = '-';
array[2] = '\0';
break;
case 8:
case 9:
array[1] = '+';
array[2] = '\0';
break;
default:
array[1] = '\0';
break;
}
} else {
array[0] = 'F';
array[1] = '\0';
}
printf("The Letter grade is: %s\n", array);
fputs("Enter the Numeric grade: ", stdout);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getc(stdin);
}
}
return 0;
}

/* END grade.c */

/* BEGIN pops_device.c */
/*
** If rc equals 0, then an empty line was entered
** and the array contains garbage values.
** If rc equals EOF, then the end of file was reached,
** or there is some output problem.
** If rc equals 1, then there is a string in array.
** Up to LENGTH number of characters are read
** from a line of a text stream.
** If the line is longer than LENGTH,
** then the extra characters are discarded.
*/
#include <stdio.h>

#define LENGTH 50
#define str(x) # x
#define xstr(x) str(x)

int main(void)
{
int rc;
char array[LENGTH + 1];

puts("The LENGTH macro is " xstr(LENGTH));
fputs("Enter a string with spaces:", stdout);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getc(stdin);
}
while (rc == 1) {
printf("Your string is:%s\n\n"
"Hit the Enter key to end,\nor enter "
"another string to continue:", array);
fflush(stdout);
rc = fscanf(stdin, "%" xstr(LENGTH) "[^\n]%*[^\n]", array);
if (!feof(stdin)) {
getc(stdin);
}
if (rc == 0) {
*array = '\0';
}
}
return 0;
}

/* END pops_device.c */

/* BEGIN line_to_string.c */

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
/*
** INITIAL_BUFFER_SIZE can be any number.
** Lower numbers are more likely
** to get a non-NULL return value from malloc.
** Higher numbers are more likely to prevent
** any further allocation from being needed.
*/
#define INITIAL_BUFFER_SIZE 0

struct list_node {
struct list_node *next;
void *data;
};

int line_to_string(FILE *fp, char **line, size_t *size);
struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data);
void list_free(struct list_node *node, void (*free_data)(void *));
int list_fputs(FILE *stream, struct list_node *node);

int main(void)
{
struct list_node *head, *tail;
int rc;
char *buff_ptr;
size_t buff_size;
long unsigned line_count;

buff_size = INITIAL_BUFFER_SIZE;
buff_ptr = malloc(buff_size);
if (buff_ptr == NULL && buff_size != 0) {
printf("malloc(%lu) == NULL\n", (long unsigned)buff_size);
exit(EXIT_FAILURE);
}
tail = head = NULL;
line_count = 0;
puts(
"\nThis program makes and prints a list of all the lines\n"
"of text entered from standard input.\n"
"Just hit the Enter key to end,\n"
"or enter any line of characters to continue."
);
while ((rc = line_to_string(stdin, &buff_ptr, &buff_size)) > 1) {
++line_count;
tail = string_node(&head, tail, buff_ptr);
if (tail == NULL) {
break;
}
puts(
"\nJust hit the Enter key to end,\n"
"or enter any other line of characters to continue."
);
}
switch (rc) {
case EOF:
if (buff_ptr != NULL && strlen(buff_ptr) != 0) {
puts("rc equals EOF\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
case 0:
puts("realloc returned a null pointer value");
if (buff_size > 1) {
puts("rc equals 0\nThe string in buff_ptr is:");
puts(buff_ptr);
++line_count;
tail = string_node(&head, tail, buff_ptr);
}
break;
default:
/*
** If rc were to be evaluated at this point in the code,
** the value of rc
** would now be equal to (1 + strlen(buff_ptr)).
*/
break;
}
if (line_count != 0 && tail == NULL) {
puts("Node allocation failed.");
puts("The last line entered didn't make it onto the list:");
puts(buff_ptr);
}
free(buff_ptr);
puts("\nThe line buffer has been freed.\n");
printf("%lu lines of text were entered.\n", line_count);
puts("They are:\n");
list_fputs(stdout, head);
list_free(head, free);
puts("\nThe list has been freed.\n");
return 0;
}

int line_to_string(FILE *fp, char **line, size_t *size)
{
int rc;
void *p;
size_t count;

count = 0;
while ((rc = getc(fp)) != EOF) {
++count;
if (count + 2 > *size) {
p = realloc(*line, count + 2);
if (p == NULL) {
if (*size > count) {
(*line)[count] = '\0';
(*line)[count - 1] = (char)rc;
} else {
ungetc(rc, fp);
}
count = 0;
break;
}
*line = p;
*size = count + 2;
}
if (rc == '\n') {
(*line)[count - 1] = '\0';
break;
}
(*line)[count - 1] = (char)rc;
}
if (rc != EOF) {
rc = count > INT_MAX ? INT_MAX : count;
} else {
if (*size > count) {
(*line)[count] = '\0';
}
}
return rc;
}

struct list_node *string_node(struct list_node **head,
struct list_node *tail,
char *data)
{
struct list_node *node;

node = malloc(sizeof *node);
if (node != NULL) {
node -> next = NULL;
node -> data = malloc(strlen(data) + 1);
if (node -> data != NULL) {
if (*head == NULL) {
*head = node;
} else {
tail -> next = node;
}
strcpy(node -> data, data);
} else {
free(node);
node = NULL;
}
}
return node;
}

void list_free(struct list_node *node, void (*free_data)(void *))
{
struct list_node *next_node;

while (node != NULL) {
next_node = node -> next;
free_data(node -> data);
free(node);
node = next_node;
}
}

int list_fputs(FILE *stream, struct list_node *node)
{
while (node != NULL
&& fputs(node -> data, stream) != EOF
&& putc( '\n', stream) != EOF)
{
node = node -> next;
}
return node == NULL ? '\n' : EOF;
}

/* END line_to_string.c */
 
K

Keith Thompson

Xavoux said:
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...
[...]
gets(buff);

The gets() function cannot be used safely. Well, in theory, it can be
used "safely" if your program is guaranteed to run only in an
environment in which you control what will appear on stdin, but in
practice it's best to avoid it altogether.

See question 12.23 in the comp.lang.c FAQ, <http://www.c-faq.com/>.

[...]
fgets(buff, sizeof(buff), stdin);

As you can see, the fgets() function takes an argument specifying the
size of the buffer. It uses this to avoid writing past the end of the
buffer. It does have some problems of its own: it leaves the trailing
'\n', if any, in the buffer, and if the input line is too long,
fgets() reads only part of it, leaving the rest to be read later.
Both of these problems can be dealt with.

[...]
scanf("%s", buff);

This is as unsafe as gets(), but it doesn't do the same thing. gets()
and fgets() read a line at a time (from the current position to the
next '\n' character). scanf() with a "%s" format skips leading
whitespace (possibly including multiple '\n' characters), then reads a
string of contiguous non-whitespace characters. With more complex
formats, you can limit the number of characters read, avoiding buffer
overflow; see the scanf documentation (man page or whatever) for
details.
 
X

Xavoux

thanks,

fgets dont lead to buffer overflow (my mistake)... but char not read are
leaving... and explode next read such as scanf("%d", &i);

how can i empty stdin buffer?
 
P

Peter Nilsson

Please don't top post.

scanf can be used safely.
thanks,

fgets dont lead to buffer overflow (my mistake)... but char not read are
leaving... and explode next read such as scanf("%d", &i);

In what way?
how can i empty stdin buffer?

Why do you think you need to? [BTW, read the FAQ.]

A specific example of the problem (if you have one) is likely to be
more
useful than asking general questions. Bullet proof input in C is not as
trivial is it might seem. Different situations have different caveats.
 
X

Xavoux

Peter Nilsson said:
Please don't top post.

scanf can be used safely.
thanks,

fgets dont lead to buffer overflow (my mistake)... but char not read are
leaving... and explode next read such as scanf("%d", &i);

In what way?
how can i empty stdin buffer?

Why do you think you need to? [BTW, read the FAQ.]

A specific example of the problem (if you have one) is likely to be
more
useful than asking general questions. Bullet proof input in C is not as
trivial is it might seem. Different situations have different caveats.

i want to manage input of, at most k values (double)
user need to be able to give less than k in a time

k is variable
 
M

Malcolm

Xavoux said:
Hello all...
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...

i compiled that code with gcc version 2.95.2, on windows 2000

char tmp0[10] = "ABCDEFGHI\0";
char buff[5]; /* Input buffer. */
char tmp1[10] = "ABCDEFGHI\0";

/* Get data from the keyboard. */
printf("\nGETS : please enter text => \n");

gets(buff);

printf("\nGETS : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

/* Get data from the keyboard. */
printf("\nFGETS : please enter text => \n");

fgets(buff, sizeof(buff), stdin);

printf("\nFGETS : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

printf("\nSCANF : please enter text => \n");

scanf("%s", buff);

printf("\nSCANF : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);
gets() doesn't take the buffer size as an argument, so it is impossible to
prevent a long line from overflowing the buffer.
fgets() does. However there is very little point in simply truncating the
line and treating it as valid input - that is extremely likely to lead to a
serious bug as a number is half-read or something similar.

If you know how long valid lines can be, write

char buff[MAXLINELEN + 1];

fgets(buff, sizeof buff, fp);
if(!strchr(buff, '\n'))
{
fprintf(stderr, "line too long\n");
exit(EXIT_FAILURE);
}

if you don't, fgets() is virtually useless. Use a function that read
arbitrarily long lines instead. Chuck falconer has one avialable called
ggets().
 
J

jaysome

Look at their prototypes, which one has an input size limit?

The function gets() does not have an input size. But that didn't stop
the authors of the C standard from defining gets() to return a
pointer. If the return value is NULL, a read error occurred, and the
array contents are indeterminate. This implies that you must always
check the return value of gets(), if you want to avoid accessing an
array contents that is indeterminate.

You can and should avoid the pitfalls of gets() (accessing an array
contents that is indeterminate, and undefined behavior), by not using
gets().
 
S

santosh

Xavoux said:
Hello all...
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...

None of the above will lead to buffer overflow if used correctly.
However, among them, gets() is the toughest to use safely, since you've
to basically be assured that input from stdin will be of the required
length. For most programs, under most conditions, this is not possible.
Hence, you should rule out gets() for all purposes.

fgets() can do anything that gets() can and in a safer manner. scanf()
with the %s specifier unqualified with a field width behaves just like
gets().

Also ggets(), though non-standard, is a good alternative to fgets().
Search the group's archive for it's download location.

Finally, none of the above functions, except possibly ggets(), are
immune from programmer mistakes. You'll have to ensure that the buffer
used with fgets() and scanf() is sufficient. And if you can't do that,
then C really isn't your language.
 
S

santosh

Xavoux said:
A specific example of the problem (if you have one) is likely to be
more
useful than asking general questions. Bullet proof input in C is not as
trivial is it might seem. Different situations have different caveats.
[snip]
i want to manage input of, at most k values (double)
user need to be able to give less than k in a time

k is variable

Download the following:
<http://cbfalconer.home.att.net/download/ggets.zip>

Use it to read in lines of arbitrary size. Then use strtod() to attempt
to convert each line to a double. This is a quite robust solution.

You won't need to worry about discarding unread input from stdin if you
use the above method.
 
R

Richard Heathfield

Xavoux said:
thanks,

fgets dont lead to buffer overflow (my mistake)... but char not read are
leaving... and explode next read such as scanf("%d", &i);

If you use fgets exclusively, you never get this problem in practice.
how can i empty stdin buffer?

If you must do this (and generally it is only students who need to do it,
because it is only students who don't know to capture input on a
line-by-line basis and do the parsing on the string themselves), you can do
it as follows:

#include <stdio.h>

int fdiscardline(FILE *fp)
{
int ch;
while((ch = getc(fp)) != '\n' && ch != EOF)
{
continue;
}
return ch == EOF;
}

This routine will return 1 if it runs out of stuff to read (i.e. the end of
the stream was reached).

To use it on stdin, pass stdin to it.
 
R

Richard Heathfield

santosh said:

Also ggets(), though non-standard, is a good alternative to fgets().

No, it isn't. It's a good function, and it has an important place, but it is
not a good alternative to fgets. It is certainly not a drop-in replacement.
fgets does not require you to jump through memory management hoops in order
to use it.

ggets is well-suited for situations where you want to store every line you
read. Many times, however, you want to read a line, process it, read the
next line, process it, etc, using the same buffer every time. ggets is not
well-suited to this usage.
 
C

CBFalconer

Richard said:
santosh said:



No, it isn't. It's a good function, and it has an important place,
but it is not a good alternative to fgets. It is certainly not a
drop-in replacement. fgets does not require you to jump through
memory management hoops in order to use it.

ggets is well-suited for situations where you want to store every
line you read. Many times, however, you want to read a line, process
it, read the next line, process it, etc, using the same buffer every
time. ggets is not well-suited to this usage.

I certainly agree. And no function is immune to misuse. However,
for interactive input, the overhead of allocating and possibly
freeing storage is totally trivial, and will be masked by the
innate interactive delays. I designed it to maximally simplify the
call, i.e. you only have to specify the address of a pointer to
char, and the file for fggets. In addition it handles the problem
of deciding whether or not to remove a terminal '\n' by always
removing it.

Your fgetline at http://users.powernet.co.uk/eton/c/fgetdata.html
is always available to handle the "constant buffer" condition you
mention. I believe that URL is now obsolete, and if you specify
the correct one and I remember I will try to update the reference
that appears on my download page.

As far as buffering philosophy is concerned, with proper design
most i/o can be done by using nothing more than getc [1] with
judicious use of ungetc [2]. Unfortunately the standard library
routines are not really compatible with this mode (apart from
scanf, which always seems to lead to confusion). The basic idea is
to always leave the field terminating char in the input stream.
Very often that will be a '\n', so that a flushln() [3] function
can be used consistently. At the same time the user has the option
of identifying that field terminator, and objecting when it does
not meet the required syntax.

[1] When getc is implemented as a macro (system dependent), there
is usually no cost in efficiency to doing this char by char input
scanning. It simply replaces a local buffer with the systems
buffer.

[2] Multiple level ungetc may be available on more systems than is
apparent at first glance, provided that ungets do not cross line
boundaries. I recently published here a test program for the level
available, and was surprised to find that it worked under DJGPP.

[3] flushln is:

int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc(f))) && (EOF != ch)) continue;
return ch;
}
 
P

pete

Xavoux said:
Hello all...
I can't remind which function to use for safe inputs...
gets, fgets, scanf leads to buffer overflow...

i compiled that code with gcc version 2.95.2, on windows 2000

char tmp0[10] = "ABCDEFGHI\0";
char buff[5]; /* Input buffer. */
char tmp1[10] = "ABCDEFGHI\0";

/* Get data from the keyboard. */
printf("\nGETS : please enter text => \n");

gets(buff);

printf("\nGETS : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

/* Get data from the keyboard. */
printf("\nFGETS : please enter text => \n");

fgets(buff, sizeof(buff), stdin);

printf("\nFGETS : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

printf("\nSCANF : please enter text => \n");

scanf("%s", buff);

printf("\nSCANF : length is %d size is %d #%s#\n", strlen(buff),
sizeof(buff), buff);
printf("%s\n%s\n", tmp0, tmp1);

/* BEGIN new.c */

#include <stdio.h>
#include <string.h>

#define LENGTH 4
#define BUFFER_SIZE (LENGTH + 1)
#define str(x) # x
#define xstr(x) str(x)

int main(void)
{
char buff[BUFFER_SIZE];
int rc;

printf("\nSCANF : please enter text => \n");

rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", buff);
if (!feof(stdin)) {
getc(stdin);
}
if (rc == 0 || rc == EOF) {
*buff = '\0';
}

printf("\nSCANF : length is %u\n", (unsigned)strlen(buff));
printf("size is %u\n", (unsigned)sizeof(buff));
printf("string is #%s#\n", buff);
return 0;
}

/* END new.c */
 
P

pete

Richard said:
Many times, however, you want to read a line, process it, read the
next line, process it, etc, using the same buffer every time.
ggets is not well-suited to this usage.

line_to_string (posted elsewhere in this thread)
is good for that.
 
R

Richard Heathfield

CBFalconer said:


http://www.cpax.org.uk/prg/writings/fgetdata.php has a discussion of the
line capture problem, and provides fgetline() and fgetword() as solutions
to that problem.
is always available to handle the "constant buffer" condition you
mention. I believe that URL is now obsolete, and if you specify
the correct one and I remember I will try to update the reference
that appears on my download page.

Thanks - I'd appreciate it. :)
 
X

Xavoux

Richard Heathfield said:
CBFalconer said:



http://www.cpax.org.uk/prg/writings/fgetdata.php has a discussion of the
line capture problem, and provides fgetline() and fgetword() as solutions
to that problem.


Thanks - I'd appreciate it. :)

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: rjh at the above domain, - www.

thanks all !!

I had a major problem : buffer overflow and a second one : make a really
interactive input (the user has to be able to correct his input...)

Solution which solve buffer overflow in one input function and line discard
in another function may block the programme when no line discarding needed

With all your stuff i propose

int safegets(char * buf)
{
char bf[10] = "---------\0"; //not very nice to fix size like
this...
int len;
fgets(buf, sizeof(buf)-1, stdin);
len = strlen(buf)-1;
if (buf[len] == '\n')
{
buf[len] = '\0';
return 1;
}
else
do
fgets(bf, 9, stdin);
while (bf[strlen(bf)-1] != '\n');
return 0;
}

Is there something wrong?
 
R

Richard Heathfield

pete said:
I'm also not getting this link to work:

Strmsrfr's fgetanyline function.

The web site address you entered could not be found.
Please try the related content suggestions and paid advertisements
below, or try another search.
You entered "storm.freeshell.org ".

Thanks for letting me know. I've replaced it with a link to Eric Sosman's
getline().
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Scanf is being prioritized over printf ? 1
Using fgets 1
Fibonacci 0
A process take input from /proc/<pid>/fd/0, but won't process it 0
fgets 1
Problem with scanf 7
C language. work with text 3
scanf internals 11

Members online

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top