Using gets() safely

A

ais523

I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string that
will be read in will have to fit in readin (whose length has been
calculated by an earlier printf). The program itself demonstrates one
possible (inefficient) way of writing an asprintf() function: write to
a temporary file, and then read back the data from that file. (Of
course, in real code fgets() would be used, rather than gets(), and
stdin wouldn't be redirected!). I thought something like fclose(stdin)
might cause UB or at best be implementation-defined, but from reading
the standard it seems to be valid.
 
V

Vladimir Oka

ais523 opined:
I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string
that will be read in will have to fit in readin (whose length has
been calculated by an earlier printf). The program itself
demonstrates one possible (inefficient) way of writing an asprintf()
function: write to a temporary file, and then read back the data from
that file. (Of course, in real code fgets() would be used, rather
than gets(), and stdin wouldn't be redirected!). I thought something
like fclose(stdin) might cause UB or at best be
implementation-defined, but from reading the standard it seems to be
valid.

Have you considered non-hosted implementations (e.g. embedded systems)
with no file system at all (and where `stdin`, `stdout`, and `stderr`
are connected to, say, a serial port)?

--
"We all know Linux is great...it does infinite loops in 5 seconds."
(Linus Torvalds about the superiority of Linux on the Amsterdam
Linux Symposium)

<http://clc-wiki.net/wiki/Introduction_to_comp.lang.c>
 
A

ais523

Vladimir said:
ais523 opined:
I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string
that will be read in will have to fit in readin (whose length has
been calculated by an earlier printf). The program itself
demonstrates one possible (inefficient) way of writing an asprintf()
function: write to a temporary file, and then read back the data from
that file. (Of course, in real code fgets() would be used, rather
than gets(), and stdin wouldn't be redirected!). I thought something
like fclose(stdin) might cause UB or at best be
implementation-defined, but from reading the standard it seems to be
valid.

Have you considered non-hosted implementations (e.g. embedded systems)
with no file system at all (and where `stdin`, `stdout`, and `stderr`
are connected to, say, a serial port)?
In that case, at least the freopen, and probably the tmpnam or fopen,
will fail, causing abort() to be called. Anyway, a non-hosted
implementation need not even be able to printf(). Most C programs
wouldn't run on a freestanding implementation without modification, as
such implementations have virtually no standard library. (They nearly
all have nonstandard libraries of their own, but it would be OT to
discuss them).
 
W

websnarf

ais523 said:
I was just wondering whether there was a portable way to use gets()
safely, [...]
No.

[...] and came up with this: [... whatever deleted unread]

That's nice, the answer is still no. Your best bet is something like
the following:

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

#undef gets
int (gets) (char * p) {
p = p;
remove (__FILE__);
puts ("Don't ever use gets() for any reason");
exit (EXIT_FAILURE);
return 0;
}

The problem is that __FILE__ might be right protected, and gets might
be redefined as a macro in some header file, so this isn't really
guaranteed to function correctly.
 
V

Vladimir Oka

ais523 opined:

said:
In that case, at least the freopen, and probably the tmpnam or fopen,
will fail, causing abort() to be called.

(Which is probably not what you'd want to do in a freestanding
implementation.)
Anyway, a non-hosted implementation need not even be able to
printf().

They need not, but many do. As I said, `stdio` and friends are often
connected to serial ports, if not something more comfortable.
Most C programs wouldn't run on a freestanding implementation without
modification,

True, but you proposed portable, safe way of using `gets()`. I may want
to write some new, embedded code now, that will be portable later (at
least for argument's sake).
as such implementations have virtually no standard library.

Well, you'd be surprised. Whether it makes sense to use (parts of) it
(in terms of size and execution time costs mostly) is another matter.
For that reason, yes:
> (They nearly all have nonstandard libraries of their own,
> but it would be OT to discuss them).

--
"I'd crawl over an acre of 'Visual This++' and 'Integrated Development
That' to get to gcc, Emacs, and gdb. Thank you."
(By Vance Petree, Virginia Power)

<http://clc-wiki.net/wiki/Introduction_to_comp.lang.c>
 
F

Flash Gordon

ais523 said:
I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()

Better to be explicit about not taking parameters.
int main(void)
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);

At this point another process could modify the file. It might take work,
but it is theoretically possible.
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string that
will be read in will have to fit in readin (whose length has been
calculated by an earlier printf). The program itself demonstrates one
possible (inefficient) way of writing an asprintf() function: write to
a temporary file, and then read back the data from that file. (Of

I would definitely not do it that way.
course, in real code fgets() would be used, rather than gets(), and
stdin wouldn't be redirected!). I thought something like fclose(stdin)
might cause UB or at best be implementation-defined, but from reading
the standard it seems to be valid.

It's a stream, why would you not be allowed to close it?
 
A

ais523

Vladimir said:
ais523 opined:


(Which is probably not what you'd want to do in a freestanding
implementation.)

I have experimented with embedded devices (but not in C), and I realise
that calling abort() on one is likely to be highly unproductive. OTOH,
this code isn't designed to do anything useful; it's just there to try
to find a safe (albeit useless) way of using gets(). gets()'s intended
purpose was probably to read from a keyboard or other input device, not
from a temporary file that the program has just created. (fgets() would
do better in any program of my type).
They need not, but many do. As I said, `stdio` and friends are often
connected to serial ports, if not something more comfortable.


True, but you proposed portable, safe way of using `gets()`. I may want
to write some new, embedded code now, that will be portable later (at
least for argument's sake).


Well, you'd be surprised. Whether it makes sense to use (parts of) it
(in terms of size and execution time costs mostly) is another matter.
For that reason, yes:

The program was only meant to be a proof-of-concept. There are clearly
better ways of writing it (without using gets()).
 
E

Eric Sosman

ais523 wrote On 04/26/06 11:59,:
I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);

Note that the `+1' is unnecessary in this rather
peculiar case.
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string that
will be read in will have to fit in readin (whose length has been
calculated by an earlier printf). The program itself demonstrates one
possible (inefficient) way of writing an asprintf() function: write to
a temporary file, and then read back the data from that file. (Of
course, in real code fgets() would be used, rather than gets(), and
stdin wouldn't be redirected!). I thought something like fclose(stdin)
might cause UB or at best be implementation-defined, but from reading
the standard it seems to be valid.

Essentially, you're asking whether gets() is safe if
you happen to know that the input line will fit in the
buffer. Yes, under those circumstances gets() is safe.
It is, however, unusual to have so much foreknowledge --
if you were that good at peering into the future, you
ought to know what the input would be without going to the
effort of reading it at all!

It is possible to imagine scenarios where the above
code would fail and gets() would wreak its accustomed
havoc. For example, there might be a second program
running, one that just watches for temp files to appear
and immediately writes to them, replacing the short line
your code wrote with a much longer line. (This may seem
far-fetched, but some security exploits have used similar
techniques.) But assuming you have exclusive control of
the temporary file, then yes: I think your use of gets()
is safe. Silly, but safe.
 
K

Keith Thompson

ais523 said:
I was just wondering whether there was a portable way to use gets()
safely, [...]
No.

[...] and came up with this: [... whatever deleted unread]

That's nice, the answer is still no.
[...]

You're wrong. If you had bothered to read the original article, you
would understand why.
 
N

Neil

ais523 said:
Vladimir said:
ais523 opined:
I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string
that will be read in will have to fit in readin (whose length has
been calculated by an earlier printf). The program itself
demonstrates one possible (inefficient) way of writing an asprintf()
function: write to a temporary file, and then read back the data from
that file. (Of course, in real code fgets() would be used, rather
than gets(), and stdin wouldn't be redirected!). I thought something
like fclose(stdin) might cause UB or at best be
implementation-defined, but from reading the standard it seems to be
valid.
Have you considered non-hosted implementations (e.g. embedded systems)
with no file system at all (and where `stdin`, `stdout`, and `stderr`
are connected to, say, a serial port)?
In that case, at least the freopen, and probably the tmpnam or fopen,
will fail, causing abort() to be called. Anyway, a non-hosted
implementation need not even be able to printf(). Most C programs
wouldn't run on a freestanding implementation without modification, as
such implementations have virtually no standard library. (They nearly
all have nonstandard libraries of their own, but it would be OT to
discuss them).
That is a huge leap. To be call "ANSI C" The compiler must include all
required libraries. Even if using them would be "silly". printf() is
required and always included. And often to large to use in small
systems, but not always
 
K

Keith Thompson

Neil said:
ais523 said:
Vladimir Oka wrote: [...]
Have you considered non-hosted implementations (e.g. embedded systems)
with no file system at all (and where `stdin`, `stdout`, and `stderr`
are connected to, say, a serial port)?
In that case, at least the freopen, and probably the tmpnam or fopen,
will fail, causing abort() to be called. Anyway, a non-hosted
implementation need not even be able to printf(). Most C programs
wouldn't run on a freestanding implementation without modification, as
such implementations have virtually no standard library. (They nearly
all have nonstandard libraries of their own, but it would be OT to
discuss them).
That is a huge leap. To be call "ANSI C" The compiler must include
all required libraries. Even if using them would be "silly". printf()
is required and always included. And often to large to use in small
systems, but not always

You are mistaken.

First, it's more precise to refer to ISO C rather than ANSI C.
Historically, ANSI produced a C standard in 1989. ISO adopted it
(with some cosmetic changes) in 1990. In 1999, ISO produced a new
standard, which ANSI adopted without change.

A non-hosted (freestanding) implementation can be conforming without
providing most of the standard library. C99 4p6 says:

The two forms of _conforming implementation_ are hosted and
freestanding. A _conforming hosted implementation_ shall accept
any strictly conforming program. A _conforming freestanding
implementation_ shall accept any strictly conforming program that
does not use complex types and in which the use of the features
specified in the library clause (clause 7) is confined to the
contents of the standard headers <float.h>, <iso646.h>,
<limits.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.

The point of this is to allow conforming C implementations on small
embedded systems that might not be able to support the C library, or
on which things like file I/O doesn't make any sense.
 
P

pete

Neil said:
That is a huge leap.
To be call "ANSI C" The compiler must include all
required libraries. Even if using them would be "silly". printf() is
required and always included. And often to large to use in small
systems, but not always

No. He's talking about freestanding vs. hosted.
Freestanding implementations are only required to have
a very small part of the library.

N869
4. Conformance
[#6]
A conforming
freestanding implementation shall accept any strictly
conforming program that does not use complex types and in
which the use of the features specified in the library
clause (clause 7) is confined to the contents of the
standard headers <float.h>, <iso646.h>, <limits.h>,
<stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.
 
P

Peter Nilsson

ais523 said:
I was just wondering whether there was a portable way to use gets()
safely, and came up with this:

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

int main()
{
FILE* temp;
char buf[L_tmpnam];
char* readin, *tfn;
int len;
tfn = tmpnam(buf);
if(!tfn) abort();
temp = fopen(tfn,"w");
if(!temp) abort();
len = fprintf(temp, "%s %d.\n", "Hello, world!", 42);
fclose(temp);
if(!freopen(tfn, "r", stdin)) {remove(tfn); abort();};
readin = malloc(len+1);
if(readin)
{
gets(readin); /* hopefully safe */
printf("%s contained %s\n", tfn, readin);
free(readin);
}
fclose(stdin);
remove(tfn);
return 0;
}

Here, freopen is used to control the input to stdin, so the string that
will be read in will have to fit in readin (whose length has been
calculated by an earlier printf). The program itself demonstrates one
possible (inefficient) way of writing an asprintf() function: write to
a temporary file, and then read back the data from that file. (Of
course, in real code fgets() would be used, rather than gets(), and
stdin wouldn't be redirected!).

You'd be on better ground if you opened the temp file with w+ and used
rewind() before reading the file. [See Eric's reply.]

Note that in the more general application of your code, text files will
only
compare equal if printable characters are used. If the code prints
characters sourced from outside the program, and if it doesn't check
the printable status of the characters sent to the temp file, then you
cannot be sure that gets() will read exactly what was written.
 
J

Jack Klein

No. He's talking about freestanding vs. hosted.
Freestanding implementations are only required to have
a very small part of the library.

Actually, absolutely none of the library at all.
N869
4. Conformance
[#6]
A conforming
freestanding implementation shall accept any strictly
conforming program that does not use complex types and in
which the use of the features specified in the library
clause (clause 7) is confined to the contents of the
standard headers <float.h>, <iso646.h>, <limits.h>,
<stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.

If you look through the descriptions of these headers in section 7,
you will note that none of them prototypes any functions at all.
 
P

pete

Jack said:
No. He's talking about freestanding vs. hosted.
Freestanding implementations are only required to have
a very small part of the library.

Actually, absolutely none of the library at all.
N869
4. Conformance
[#6]
A conforming
freestanding implementation shall accept any strictly
conforming program that does not use complex types and in
which the use of the features specified in the library
clause (clause 7) is confined to the contents of the
standard headers <float.h>, <iso646.h>, <limits.h>,
<stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.

If you look through the descriptions of these headers in section 7,
you will note that none of them prototypes any functions at all.

The contents of those headers, is still part of the library.
 
R

Richard Bos

Jack Klein said:
No. He's talking about freestanding vs. hosted.
Freestanding implementations are only required to have
a very small part of the library.

Actually, absolutely none of the library at all.
N869
4. Conformance
[#6]
A conforming
freestanding implementation shall accept any strictly
conforming program that does not use complex types and in
which the use of the features specified in the library
clause (clause 7) is confined to the contents of the
standard headers <float.h>, <iso646.h>, <limits.h>,
<stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.

If you look through the descriptions of these headers in section 7,
you will note that none of them prototypes any functions at all.

That's irrevelant. The section is called "Library", not "Library; but
only where it describes function prototypes".

Richard
 

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

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,076
Latest member
OrderKetoBeez

Latest Threads

Top