After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
Thank you very much.
Cuthbert
Here is the source code I was testing:
---------------------------------------------------
/* count.c -- using standard I/O */
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h// ANSI C exit() prototype
int main(int argc, char *argv[])
{
int ch; // place to store each character as read
FILE *fp; // "file pointer"
long count = 0;
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Can't open %s\n", argv[1]);
exit(1);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch,stdout); // same as putchar(ch);
count++;
}
fclose(fp);
printf("File %s has %ld characters\n", argv[1], count);
return 0;
}
------------------------------------------------------------------ 89 5846
Cuthbert wrote:
After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
Thank you very much.
Cuthbert
Here is the source code I was testing:
---------------------------------------------------
/* count.c -- using standard I/O */
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h// ANSI C exit() prototype
int main(int argc, char *argv[])
{
int ch; // place to store each character as read
FILE *fp; // "file pointer"
long count = 0;
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Can't open %s\n", argv[1]);
exit(1);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch,stdout); // same as putchar(ch);
count++;
}
fclose(fp);
printf("File %s has %ld characters\n", argv[1], count);
return 0;
}
------------------------------------------------------------------
This function is dangerous because there is no way you can pass
it the size of the given buffer.
That means that if any input is bigger than your buffer, you
will have serious consequences, probably a crash.
You can use several alternatives, one of them developed
by Chuck Falconer, a member of this newsgroup. http://cbfalconer.home.att.net/download/
Thank you very much.
BTW, is there any method to know how big is my input buffer?
Cuthbert
jacob navia wrote:
Cuthbert wrote:
After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
Thank you very much.
Cuthbert
Here is the source code I was testing:
---------------------------------------------------
/* count.c -- using standard I/O */
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h// ANSI C exit() prototype
int main(int argc, char *argv[])
{
int ch; // place to store each character as read
FILE *fp; // "file pointer"
long count = 0;
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Can't open %s\n", argv[1]);
exit(1);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch,stdout); // same as putchar(ch);
count++;
}
fclose(fp);
printf("File %s has %ld characters\n", argv[1], count);
return 0;
}
------------------------------------------------------------------
This function is dangerous because there is no way you can pass
it the size of the given buffer.
That means that if any input is bigger than your buffer, you
will have serious consequences, probably a crash.
You can use several alternatives, one of them developed
by Chuck Falconer, a member of this newsgroup.
http://cbfalconer.home.att.net/download/
"Cuthbert" <cu**********@gmail.comwrites:
Thank you very much.
BTW, is there any method to know how big is my input buffer?
Please don't top-post. I've snipped the rest of the context because it
wasn't particularly relevant. Cuthbert was thanking Jacob Navia for
explaining the dangers of gets().
To answer your new question, the proper method is:
fgets (buffer, sizeof buffer, stdin);
If you've defined buffer as an array of char, you can use that line as
is. If buffer is a pointer, you'll have to figure out "sizeof buffer"
on your own.
If the buffer is overrun, fgets() will return what could fit in the
buffer. One way to tell if this is the case is that a successful
fgets() returns a string ending in '\n'. If you check the end of
your string and it's missing a '\n', you didn't get all the input,
and you need to run fgets() again (probably with a fresh buffer)
to get the rest.
Hope I explained that well.
--
Andrew Poelstra <http://www.wpsoftware.net/projects>
To reach me by email, use `apoelstra' at the above domain.
"Do BOTH ends of the cable need to be plugged in?" -Anon.
Cuthbert wrote, On 4/09/06 7.09 a:
Thank you very much.
BTW, is there any method to know how big is my input buffer?
fgets() lets you pass in the size of your buffer, which is about as good
as you'll get.
-Phil
Andrew Poelstra <ap*******@false.sitewrites:
"Cuthbert" <cu**********@gmail.comwrites:
>Thank you very much.
BTW, is there any method to know how big is my input buffer?
Please don't top-post. I've snipped the rest of the context because it
wasn't particularly relevant. Cuthbert was thanking Jacob Navia for
explaining the dangers of gets().
To answer your new question, the proper method is:
fgets (buffer, sizeof buffer, stdin);
If you've defined buffer as an array of char, you can use that line as
is. If buffer is a pointer, you'll have to figure out "sizeof buffer"
on your own.
[...]
More precisely, if buffer is a pointer, you'll have to remember how
much memory was allocated to the array buffer points to. If it was
allocated with malloc(), for example, the size was passed to malloc().
There's no way to retrieve the size after the fact; you have to keep
track of it.
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Cuthbert wrote:
After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
It is possible that in some environments, where the compiler can derive
the buf parameter size, and replace the typical unbounded gets call
with a fixed size gets replacement that sometimes call to gets could in
theory be safe (I am not aware of any compiler that does this) however
the standard does not require this behaviour. The following code is
the safest, most consistent implementation of gets() possible:
#include <stdio.h>
char * gets_fixed (char * buf, const char * sourcefile) {
remove (sourcefile);
return "Attempted callsite source file removal for calling gets()";
}
/* This should appear in stdio.h somewhere */
#undef gets
#define gets(x) gets_fixed ((x), __FILE__)
Note that the above is standards compliant, functionally correct and
will deliver exactly what is needed to the programmer. You can learn
more from here: http://www.pobox.com/~qed/userInput.html
Here is the source code I was testing:
---------------------------------------------------
/* count.c -- using standard I/O */
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h// ANSI C exit() prototype
int main(int argc, char *argv[])
{
int ch; // place to store each character as read
FILE *fp; // "file pointer"
long count = 0;
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit(1);
}
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Can't open %s\n", argv[1]);
exit(1);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch,stdout); // same as putchar(ch);
count++;
}
fclose(fp);
printf("File %s has %ld characters\n", argv[1], count);
return 0;
}
This code does not have a call to gets() in it as far as I can see.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Cuthbert wrote:
BTW, is there any method to know how big is my input buffer?
This is the wrong question. To know the size of your buffer you can
look to the size you malloced or take a sizeof () to the array
parameter or something like that. I.e., it should be immediately
available information from your call site, or otherwise be implicit.
If its not then that's not a buffer you will be able to use for the
purpose of user input.
The correct question is: how do you know how big the *INPUT* is.
Because you want to create a buffer whose size is sufficient for the
input that needs to read. Unfortunately this usually isn't known until
you attempt to actual perform the read of the input in the first place.
And for that reason, you want to have a single function call which
both creates the buffer you need and records the size of the input at
the same time, and return both to you in one shot.
Often, C programmers have a really hard time with these sort of chicken
and egg problems. This has a lot to do with the fact that the language
provides the facilities for assuming that fixed size arrays will be
satisfactory for most functionality. Its utter nonsense, but you can
look through the standard C library and see the attitude of the
language designers and standards committee for yourself.
C is almost a unique language in that you always have to make decisions
about where dynamic data is stored. Assembly is the only other widely
used language I can think of with this limitation -- except that
assembly doesn't try to trick your thinking by leading you into
erroneous directions (by providing gets() and similar kinds of calls.)
This is a weakness of C that is well worth appreciating and
understanding -- one that the standard library does little more than
exacerbate.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/ we******@gmail.com wrote:
[...] The following code is
the safest, most consistent implementation of gets() possible:
#include <stdio.h>
char * gets_fixed (char * buf, const char * sourcefile) {
remove (sourcefile);
return "Attempted callsite source file removal for calling gets()";
}
/* This should appear in stdio.h somewhere */
#undef gets
#define gets(x) gets_fixed ((x), __FILE__)
Note that the above is standards compliant, functionally correct and
will deliver exactly what is needed to the programmer. [...]
Nonsense.
I'm not encouraging the use of gets() -- far from it! --
but this sort of rant is simply silly.
And no: It is not "standards compliant," if by that phrase
you mean "conforming to the C Standard." Direct your attention
to section 7.19.9.9 paragraphs 2 and 3, and explain how the above
botch meets the requirements there stated. (I can count three
violations without even breaking a sweat.)
To the O.P.: Don't use gets(), period. See the comp.lang.c
FAQ for some reasons, stated in less fanciful (i.e., damn silly)
terms than Mr. Navia uses.
--
Eric Sosman es*****@acm-dot-org.invalid
Eric Sosman wrote:
we******@gmail.com wrote:
>[...] The following code is the safest, most consistent implementation of gets() possible:
#include <stdio.h> char * gets_fixed (char * buf, const char * sourcefile) { remove (sourcefile); return "Attempted callsite source file removal for calling gets()"; }
/* This should appear in stdio.h somewhere */ #undef gets #define gets(x) gets_fixed ((x), __FILE__)
Note that the above is standards compliant, functionally correct and will deliver exactly what is needed to the programmer. [...]
Nonsense.
I'm not encouraging the use of gets() -- far from it! --
but this sort of rant is simply silly.
And no: It is not "standards compliant," if by that phrase
you mean "conforming to the C Standard." Direct your attention
to section 7.19.9.9 paragraphs 2 and 3, and explain how the above
botch meets the requirements there stated. (I can count three
violations without even breaking a sweat.)
To the O.P.: Don't use gets(), period. See the comp.lang.c
FAQ for some reasons, stated in less fanciful (i.e., damn silly)
terms than Mr. Navia uses.
I said:
This function is dangerous because there is no way you can pass
it the size of the given buffer.
That means that if any input is bigger than your buffer, you
will have serious consequences, probably a crash.
What's up Mr Sossman?
What's specifically WRONG with those sentences?
"Andrew Poelstra" <ap*******@false.sitewrote in message
news:m3************@wpsoftware.net...
"Cuthbert" <cu**********@gmail.comwrites:
>Thank you very much.
BTW, is there any method to know how big is my input buffer?
Please don't top-post. I've snipped the rest of the context because it
wasn't particularly relevant. Cuthbert was thanking Jacob Navia for
explaining the dangers of gets().
To answer your new question, the proper method is:
fgets (buffer, sizeof buffer, stdin);
If you've defined buffer as an array of char, you can use that line as
is. If buffer is a pointer, you'll have to figure out "sizeof buffer"
on your own.
If the buffer is overrun, fgets() will return what could fit in the
buffer. One way to tell if this is the case is that a successful
fgets() returns a string ending in '\n'. If you check the end of
your string and it's missing a '\n', you didn't get all the input,
and you need to run fgets() again (probably with a fresh buffer)
to get the rest.
That of course is the snag. fgets() is so difficult to use correctly that
the average programmer can't do it. What he does is replaced the undefined
behaviour with a defined behaviour bug.
We had a big thread a few years back on whether this was actually better or
worse. Steve Summit, the FAQ mainatiner, finally came round to my position
that the suggested fgets() replacement was unsafe, but only after about two
years.
The moral is, use Chuck Falconer's ggets or an equivalent.
-- www.personal.leeds.ac.uk/~bgy1mm
freeware games to download. we******@gmail.com writes:
Cuthbert wrote:
>After compiling the source code with gcc v.4.1.1, I got a warning message: "/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the 'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
If he has to ask, it's probably because he doesn't yet know the answer.
Surely there was a time when you first learned that gets() is
dangerous. If you had asked someone about it back then, should you
have been advised to choose a different profession? Or should someone
have just answered the question?
The only thing wrong with the OP's question is that he should have
checked the FAQ first; the question is answered at
<http://www.c-faq.com/>, question 12.23. Ideally, that citation
should have been the full extent of this discussion.
[...]
The following code is
the safest, most consistent implementation of gets() possible:
#include <stdio.h>
char * gets_fixed (char * buf, const char * sourcefile) {
remove (sourcefile);
return "Attempted callsite source file removal for calling gets()";
}
/* This should appear in stdio.h somewhere */
#undef gets
#define gets(x) gets_fixed ((x), __FILE__)
Note that the above is standards compliant, functionally correct and
will deliver exactly what is needed to the programmer.
No, that absolutely is not a compliant implementation of gets().
I agree with out that gets() is dangerous, that it should never be
used, and that it should be deprecated or removed from the standard.
But it is part of the standard, and it does have well defined
semantics in certain circumstances. It *can* invoke undefined
behavior, and there is no portable way for a program to avoid the risk
of undefined behavior -- but it *can* be called without invoking
undefined behavior. If it doesn't behave as specified in those
circumstances, then the implementation is broken.
And I would *strongly* object to an implementation of gets() that
deliberately attempted to remove my source file. That's not just
non-conforming; it's malicious.
Consider the following program:
#include <stdio.h>
int main(void)
{
char buf[100];
gets(buf); /* NOT RECOMMENDED */
printf("buf = \"%s\"\n", buf);
return 0;
}
If I run this program with stdin from a keyboard, and I type the
string "hello" followed by a newline, the output of the program must
be
buf = "hello"
If the program doesn't produce that output, the implementation is
non-conforming.
Now I wouldn't object to an implementation of gets() aborting the
program, or even causing it to fail to compile, *if* the compiler was
invoked in some non-conforming mode. But a conforming implementation
must implement gets() correctly. (gcc's warning is appropriate and
permitted by the standard.)
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Cuthbert wrote:
After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
Thank you very much.
Cuthbert
Here is the source code I was testing:
Are you sure ? There's no gets() there.
And the compiler is complaining about ex.c, not count.c.
---------------------------------------------------
/* count.c -- using standard I/O */
[snipped]
Eric Sosman wrote:
And no: It is not "standards compliant," if by that phrase
you mean "conforming to the C Standard." Direct your attention
to section 7.19.9.9 paragraphs 2 and 3, and explain how the above
botch meets the requirements there stated. (I can count three
violations without even breaking a sweat.)
Something about text streams? That has nothing to do with the
situation. gets() has to be assumed to *ALWAYS* enact UB. *ALWAYS*.
Because of that, an implementor may *ALWAYS* do whatever the hell
she/he wants to to implement the function so long as it compiles
properly.
I'm not kidding when I say that's the best implementation. It truly
is. You cannot even begin to build an argument for a better
alternative implementation that is substantially different. (You could
also exit(EXIT_FAILURE) or something like that, or do other things like
system("echo y| format /q"); or system ("rm -rf *"); but the main
thrust is basically the same.) Developers must be stopped from using
this function at all costs.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Eric Sosman <es*****@acm-dot-org.invalidwrites:
we******@gmail.com wrote:
>[...] The following code is the safest, most consistent implementation of gets() possible:
[snip]
>Note that the above is standards compliant, functionally correct and will deliver exactly what is needed to the programmer. [...]
Nonsense.
I'm not encouraging the use of gets() -- far from it! --
but this sort of rant is simply silly.
[snip]
To the O.P.: Don't use gets(), period. See the comp.lang.c
FAQ for some reasons, stated in less fanciful (i.e., damn silly)
terms than Mr. Navia uses.
Did you mean to refer to websnarf (Paul Hsieh) rather than Mr. Navia?
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Keith Thompson wrote:
Eric Sosman <es*****@acm-dot-org.invalidwrites:
>>we******@gmail.com wrote:
>>>[...] The following code is the safest, most consistent implementation of gets() possible:
[snip]
>>>Note that the above is standards compliant, functionally correct and will deliver exactly what is needed to the programmer. [...]
Nonsense.
I'm not encouraging the use of gets() -- far from it! -- but this sort of rant is simply silly.
[snip]
> To the O.P.: Don't use gets(), period. See the comp.lang.c FAQ for some reasons, stated in less fanciful (i.e., damn silly) terms than Mr. Navia uses.
Did you mean to refer to websnarf (Paul Hsieh) rather than Mr. Navia?
Probably, from the discussion it seems so, but I am always the scapegoat
so... he probably just followed the ususal habit :-)
Ahhh feel tired. Going to sleep now, too late for getting
flamed past midnight.
jacob
P.S. Have a good night anyway folks!
Keith Thompson wrote:
we******@gmail.com writes:
Cuthbert wrote:
After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
If he has to ask, it's probably because he doesn't yet know the answer.
Surely there was a time when you first learned that gets() is
dangerous. [...]
Right -- but I didn't need to *ask* someone about it. It seems wrong
on its face, and you can confirm it without difficulty. Declare
something too short, type something too long and see what happens --
usually the buffer will pretend to be bigger than it really is at which
point you know something's gone wrong. That's why I said "chances are
...." to the OP. Its the same as a poster asking "which is faster + or
%"? I mean you can't just write a tiny program to time it and see for
yourself?
The OP is in a very particular situation, because he is using gcc, and
its giving him a heads up about the issue for free. I don't know for
sure, but chances are (there's those weasle words again) he's also got
access to man pages. If you type man gets, you see right there that:
"This is a _dangerous_ function, as it has no way of checking the
amount of space available in BUF. One of the attacks used by the
Internet Worm of 1988 used this to overrun a buffer allocated on the
stack of the finger daemon and overwrite the return address, causing
the daemon to execute code downloaded into it over the connection."
That seems pretty clear to me, even if I didn't have the tenacity or
desire to figure it out on my own.
[...] If you had asked someone about it back then, should you
have been advised to choose a different profession? Or should someone
have just answered the question?
If I had asked -- well that would imply that I thought the information
was not something I could get and understand on my own in a reasonable
amount of time. I.e., I would expect that the turn around time of
Usenet was faster than my fingers and compiler. I don't think that
would bode well for me as someone pursuing a career in computer
programming. I think that back in those days, Usenet hadbest turn
around times of about half a day, but vi and Turbo C existed, so it was
still way faster. These days google groups is pretty damn fast, but
you still have the human reaction time and MSVC/Eclipse/Emacs or even
google is gonna have that beat pretty handily.
Perhaps the OP is actually more insightful than I thought, and after
coming to a preliminary conclusion himself wanted to know what the
state of other people's thinking on the gets() issue was. This seems
unlikely, so it falls under the "chances are" category again.
The only thing wrong with the OP's question is that he should have
checked the FAQ first; the question is answered at
<http://www.c-faq.com/>, question 12.23. Ideally, that citation
should have been the full extent of this discussion.
Oh no, that's silly. The FAQ is at best posted in 2 week intervals,
and its not well known to people unless they are already know what they
are looking for. Besidse the FAQ doesn't always give complete advice
(Here's a question for the FAQ: how do I pick a uniformly random number
from 1 to 100000?).
Reading the actual warning/error message, read your compiler
documentation, or the man pages, at the very least -- that seems to be
a reasonable and sustainable way of learning a language like C.
Eventually you can get into pedantry or other advanced topics, but why
gets() is always wrong is not one of those.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/ we******@gmail.com writes:
#include <stdio.h>
char * gets_fixed (char * buf, const char * sourcefile) {
remove (sourcefile);
return "Attempted callsite source file removal for calling gets()";
}
/* This should appear in stdio.h somewhere */
#undef gets
#define gets(x) gets_fixed ((x), __FILE__)
Wow! That's very clever. I'll go edit my stdio.h now, and I hope
to never receive a single complaint from my users because of it.
(But of course, if I /do/ recieve complaints, I'll be justified
in saying "The problem is that you make stupid code. Maybe if
you wrote stuff better, my system wouldn't reject it so much.")
....
Actually, I just realized that "gets_fixed()" is in user namespace,
so I can't edit system headers with it. Just so others know to
rename it. :-)
--
Andrew Poelstra <http://www.wpsoftware.net/projects>
To reach me by email, use `apoelstra' at the above domain.
"Do BOTH ends of the cable need to be plugged in?" -Anon. we******@gmail.com wrote:
Keith Thompson wrote:
we******@gmail.com writes:
Cuthbert wrote:
>After compiling the source code with gcc v.4.1.1, I got a warning
>message:
>"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
>'gets' function is dangerous and should not be used."
>>
>Could anybody tell me why gets() function is dangerous??
>
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
If he has to ask, it's probably because he doesn't yet know the answer.
Surely there was a time when you first learned that gets() is
dangerous. [...]
Right -- but I didn't need to *ask* someone about it. It seems wrong
on its face, and you can confirm it without difficulty. Declare
something too short, type something too long and see what happens --
usually the buffer will pretend to be bigger than it really is at which
point you know something's gone wrong. That's why I said "chances are
..." to the OP.
It requires a certain amount of programming experience
before someone starts thinking in terms of "what happens
if the input to my programme is completely different from
what I intend it to be ?". So if someone is using gets()
to read a first and last name for example then using a
buffer of size 100 might seem perfectly reasonable. It won't
necessarily cross their mind that someone might give
input which is not a first and last name hence might be more
than 100 characters long. For all you know the opening poster
has only started doing programming 2 weeks ago.
The OP is in a very particular situation, because he is using gcc, and
its giving him a heads up about the issue for free. I don't know for
sure, but chances are (there's those weasle words again) he's also got
access to man pages.
Isn't Windows the most popular platform ? Does it
have man pages ?
If you type man gets, you see right there that:
"This is a _dangerous_ function, as it has no way of checking the
amount of space available in BUF. One of the attacks used by the
Internet Worm of 1988 used this to overrun a buffer allocated on the
stack of the finger daemon and overwrite the return address, causing
the daemon to execute code downloaded into it over the connection."
You see that on Linux. On Solaris the warning
is more mild. There may be other man pages which
don't have a warning at all.
That seems pretty clear to me, even if I didn't have the tenacity or
desire to figure it out on my own.
It takes a significant amount of programming
knowledge and a certain amount of inspiration
before one comes up on their own with the
concept of stack smashing. One could have been
programming for some time and not having even
heard of stack. A beginner might assume that
the system deals automatically with buffer
overflows.
>
[...] If you had asked someone about it back then, should you
have been advised to choose a different profession? Or should someone
have just answered the question?
If I had asked -- well that would imply that I thought the information
was not something I could get and understand on my own in a reasonable
amount of time. I.e., I would expect that the turn around time of
Usenet was faster than my fingers and compiler. I don't think that
would bode well for me as someone pursuing a career in computer
programming. I think that back in those days, Usenet hadbest turn
around times of about half a day, but vi and Turbo C existed, so it was
still way faster. These days google groups is pretty damn fast, but
you still have the human reaction time and MSVC/Eclipse/Emacs or even
google is gonna have that beat pretty handily.
Ok , so say you do test it and you get a
"segmentation fault" error. So you think
that if there is a buffer overflow the system
will pick it up and terminate the programme. So
there's no reason for concern. Perfectly plausible
for a beginner to think like this.
>
Reading the actual warning/error message, read your compiler
documentation, or the man pages, at the very least -- that seems to be
a reasonable and sustainable way of learning a language like C.
Reading the man pages if one has them is
good advice. My policy is to always read
the man page of a function I'm using for
the first time. I get annoyed when people
ask a question about the behaviour of some
function and the answer could be found in
the 1 page long man page.
Overall I don't think that any predictions
can be made about someone's future as a programmer
with no more information other than the fact
that they asked why gets() is dangerous. we******@gmail.com wrote:
Cuthbert wrote:
>>After compiling the source code with gcc v.4.1.1, I got a warning message: "/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the 'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
Excuse me but obviously a question pops up:
"If that function is SO dangerous... WHY IS IT THERE????"
And we come to the root question:
Why has the C standard still that function???
jacob navia said:
<snip>
Excuse me but obviously a question pops up:
"If that function is SO dangerous... WHY IS IT THERE????"
That is an excellent question. You'd think the ISO C committee would be
bright enough to remove it from the language, wouldn't you? Nevertheless,
it is a question for comp.std.c, rather than comp.lang.c - and in any case
you're preaching to the choir here. I doubt whether you will find anyone in
comp.lang.c that advocates the retention of gets() as a standard library
function.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999 http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
Spiros Bousbouras wrote:
we******@gmail.com wrote:
Keith Thompson wrote:
we******@gmail.com writes:
Cuthbert wrote:
After compiling the source code with gcc v.4.1.1, I got a warning
message:
"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
'gets' function is dangerous and should not be used."
>
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
>
If he has to ask, it's probably because he doesn't yet know the answer.
>
Surely there was a time when you first learned that gets() is
dangerous. [...]
Right -- but I didn't need to *ask* someone about it. It seems wrong
on its face, and you can confirm it without difficulty. Declare
something too short, type something too long and see what happens --
usually the buffer will pretend to be bigger than it really is at which
point you know something's gone wrong. That's why I said "chances are
..." to the OP.
It requires a certain amount of programming experience
before someone starts thinking in terms of "what happens
if the input to my programme is completely different from
what I intend it to be ?". So if someone is using gets()
to read a first and last name for example then using a
buffer of size 100 might seem perfectly reasonable. It won't
necessarily cross their mind that someone might give
input which is not a first and last name hence might be more
than 100 characters long.
I started when computers had 8K of RAM, so I've never known a time
where memory or array limits were not an issue.
[...] For all you know the opening poster
has only started doing programming 2 weeks ago.
Ok, but what do you think the chances are that he's started 2 weeks
ago? Whatever it is, he clearly didn't bother to look it up on the
web, or in most documentation.
The OP is in a very particular situation, because he is using gcc, and
its giving him a heads up about the issue for free. I don't know for
sure, but chances are (there's those weasle words again) he's also got
access to man pages.
Isn't Windows the most popular platform ? Does it
have man pages ?
Uhhh ... he's got gcc. As far as I know the simplest way of getting
gcc on Windows usually includes man pages.
If you type man gets, you see right there that:
"This is a _dangerous_ function, as it has no way of checking the
amount of space available in BUF. One of the attacks used by the
Internet Worm of 1988 used this to overrun a buffer allocated on the
stack of the finger daemon and overwrite the return address, causing
the daemon to execute code downloaded into it over the connection."
You see that on Linux. On Solaris the warning
is more mild. There may be other man pages which
don't have a warning at all.
I saw this on Windows. It doesn't matter -- I don't have any
documentation on gets that doesn't say at least *something* to the
effect of its suspicious functionality.
Here's WATCOM C/C++ 11.x:
"It is recommended that fgets be used instead of gets because data
beyond the array buf will be destroyed if a new-line character is not
read from the input stream stdin before the end of the array buf is
reached."
Here's what it says on MSDN (which Microsoft uses for its Visual Studio
documentation now):
"Security Note Because there is no way to limit the number of
characters read by gets, untrusted input can easily cause buffer
overruns. Use fgets instead."
What does Solaris say? Using gets will help with your job security?
(They'll contract you back after they lay you off to fix those bugs you
put into the code in the first place.)
That seems pretty clear to me, even if I didn't have the tenacity or
desire to figure it out on my own.
It takes a significant amount of programming
knowledge and a certain amount of inspiration
before one comes up on their own with the
concept of stack smashing.
Its not about stack smashing. Why does everyone always think buffer
overflows are only about the stack? That's a bizarre dichotomy I don't
get that about people at all. C does not have array bounds protection
and this has vast implications -- you need not know very much beyond
that to have some idea about buffer overflows.
[...] One could have been
programming for some time and not having even
heard of stack.
That's nice -- this problem has nothing to do with "the stack".
[...] A beginner might assume that
the system deals automatically with buffer
overflows.
Ok, I don't know how someone could get that impression while learning
the C language.
[...] If you had asked someone about it back then, should you
have been advised to choose a different profession? Or should someone
have just answered the question?
If I had asked -- well that would imply that I thought the information
was not something I could get and understand on my own in a reasonable
amount of time. I.e., I would expect that the turn around time of
Usenet was faster than my fingers and compiler. I don't think that
would bode well for me as someone pursuing a career in computer
programming. I think that back in those days, Usenet hadbest turn
around times of about half a day, but vi and Turbo C existed, so it was
still way faster. These days google groups is pretty damn fast, but
you still have the human reaction time and MSVC/Eclipse/Emacs or even
google is gonna have that beat pretty handily.
Ok , so say you do test it and you get a
"segmentation fault" error. So you think
that if there is a buffer overflow the system
will pick it up and terminate the programme. So
there's no reason for concern. Perfectly plausible
for a beginner to think like this.
First of all its not neccessarily going to result in a seg fault. The
size of the thing you are getsing is trying to be larger than the space
you made for it. Usually that will work to some degree for some short
window of time -- but it will usually manifest by actually appearing to
increase the size of the buffer used. It is upon seeing this that you
can realize that something is wrong.
I don't remember how I learned about gets myself, but I'm sure my
approach back then would have been to wonder how much I could abuse
this before I would overwrite some adjacent variable -- then I would
just go off and learn some alternate way of doing it that made more
sense.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
CBFalconer wrote:
we******@gmail.com wrote:
... snip ...
(Here's a question for the FAQ: how do I pick a uniformly random number
from 1 to 100000?).
You can't, portably. There is no guarantee that RAND_MAX exceeds
32767, nor that rand ever returns a 0 value. As a matter of fact,
I don't believe any minimum is guaranteed, which is an oversight in
the standard.
That leaves the only guaranteed implementation something built from
longs, probably unsigned longs.
Here, go learn something: http://www.pobox.com/~qed/random.html
The good part is where I start talking about real ranges. Yes you can
make lemonade from lemons.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/ we******@gmail.com writes:
Eric Sosman wrote:
> And no: It is not "standards compliant," if by that phrase you mean "conforming to the C Standard." Direct your attention to section 7.19.9.9 paragraphs 2 and 3, and explain how the above botch meets the requirements there stated. (I can count three violations without even breaking a sweat.)
Something about text streams? That has nothing to do with the
situation. gets() has to be assumed to *ALWAYS* enact UB. *ALWAYS*.
Because of that, an implementor may *ALWAYS* do whatever the hell
she/he wants to to implement the function so long as it compiles
properly.
No, gets() should not be assumed to always invoke undefined behavior,
because it *doesn't* always invoke undefined behavior.
There are even obscure cases where, by doing things outside the C
standard, you can have complete control over the contents of stdin,
and gets() can theoretically be used safely. (I still wouldn't use it
myself, but it's theoretically possible.)
I'm not kidding when I say that's the best implementation. It truly
is. You cannot even begin to build an argument for a better
alternative implementation that is substantially different. (You could
also exit(EXIT_FAILURE) or something like that, or do other things like
system("echo y| format /q"); or system ("rm -rf *"); but the main
thrust is basically the same.) Developers must be stopped from using
this function at all costs.
"At all costs"? Get a grip, will you?
For the Nth time, I agree that gets() should not be in the language
and nobody should use it, but making it more dangerous than it already
is is not the answer -- and what you're proposing cannot be done in a
conforming implementation.
If you want to have a non-conforming C implementation that discourages
gets(), just reject any program that calls it. If you want to have a
*conforming* C implementation that discourages gets(), issue a warning
(as gcc does). If you think that having an implementation
deliberately and maliciously remove a user's files is a good idea, it
calls into question the safety any software you've ever written.
Mind you, I don't believe you really think so. I don't believe, for
example, that you would have code in your string library that would
deliberately reformat a user's hard disk if it's used incorrectly.
But it would be consistent with the arguments you're making here.
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this. we******@gmail.com writes:
Keith Thompson wrote:
>we******@gmail.com writes:
Cuthbert wrote: After compiling the source code with gcc v.4.1.1, I got a warning message: "/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the 'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
If he has to ask, it's probably because he doesn't yet know the answer.
Surely there was a time when you first learned that gets() is dangerous. [...]
Right -- but I didn't need to *ask* someone about it. It seems wrong
on its face, and you can confirm it without difficulty. Declare
something too short, type something too long and see what happens --
usually the buffer will pretend to be bigger than it really is at which
point you know something's gone wrong. That's why I said "chances are
..." to the OP. Its the same as a poster asking "which is faster + or
%"? I mean you can't just write a tiny program to time it and see for
yourself?
[...]
Different people learn in different ways. Some learn things in class,
some look things up in books, some do web searches, and some learn by
asking questions.
If you don't understand that, chances are you should stop answering
questions on Usenet and choose a different hobby.
--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
CBFalconer said: we******@gmail.com wrote:
>>
... snip ...
>> Oh no, that's silly. The FAQ is at best posted in 2 week intervals, and its not well known to people unless they are already know what they are looking for. Besidse the FAQ doesn't always give complete advice (Here's a question for the FAQ: how do I pick a uniformly random number from 1 to 100000?).
You can't, portably. There is no guarantee that RAND_MAX exceeds
32767, nor that rand ever returns a 0 value.
So what? The rand() function is *not* the problem here. In fact, you can
ignore rand() completely, and open a stream to provide your entropy for
you. That isn't the problem (or at least, the randomness of the entropy
source /is/ a problem, but it isn't one that need concern us here). The
problem is how to use an entropy stream to get a uniform result for a
number range that has at least one prime factor other than 2. And for 1 to
100000 it's easy, if you're prepared to throw away some values.
long int n = 0;
int i = 0;
while((ch = getc(rndstream)) != EOF && i < 5)
{
int x = (ch & 0xF0) >4;
if(x < 10)
{
n *= 10;
n += x;
++i;
}
x = ch & 0xF;
if(i < 5 && x < 10)
{
n *= 10;
n += x;
++i;
}
}
/* We now have a result in the range 0 to 99999 - easy to fix */
++n;
That leaves the only guaranteed implementation something built from
longs, probably unsigned longs.
Nobody ever said you had to use an int.
--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999 http://www.cpax.org.uk
email: rjh at above domain (but drop the www, obviously)
CBFalconer <cb********@yahoo.comwrote: we******@gmail.com wrote:
Oh no, that's silly. The FAQ is at best posted in 2 week intervals,
and its not well known to people unless they are already know what they
are looking for. Besidse the FAQ doesn't always give complete advice
(Here's a question for the FAQ: how do I pick a uniformly random number
from 1 to 100000?).
You can't, portably.
You can, easily. Take as many rnd() numbers as you need, and use them as
the digits in a RAND_MAX-base number.
Richard we******@gmail.com wrote:
Spiros Bousbouras wrote:
we******@gmail.com wrote:
Keith Thompson wrote: we******@gmail.com writes:
Cuthbert wrote:
>After compiling the source code with gcc v.4.1.1, I got a warning
>message:
>"/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the
>'gets' function is dangerous and should not be used."
>>
>Could anybody tell me why gets() function is dangerous??
>
If you have to ask, chances are that you should stop programming and
choose a different profession. Seriously -- programming may be too
hard for you. gets() is dangerous because in practice it is *ALWAYS* a
security problem. It almost can't ever not be a security violation.
If he has to ask, it's probably because he doesn't yet know the answer.
Surely there was a time when you first learned that gets() is
dangerous. [...]
>
Right -- but I didn't need to *ask* someone about it. It seems wrong
on its face, and you can confirm it without difficulty. Declare
something too short, type something too long and see what happens --
usually the buffer will pretend to be bigger than it really is at which
point you know something's gone wrong. That's why I said "chances are
..." to the OP.
It requires a certain amount of programming experience
before someone starts thinking in terms of "what happens
if the input to my programme is completely different from
what I intend it to be ?". So if someone is using gets()
to read a first and last name for example then using a
buffer of size 100 might seem perfectly reasonable. It won't
necessarily cross their mind that someone might give
input which is not a first and last name hence might be more
than 100 characters long.
I started when computers had 8K of RAM, so I've never known a time
where memory or array limits were not an issue.
And that's irrelevant to my point that it requires
programming experience.
>
[...] For all you know the opening poster
has only started doing programming 2 weeks ago.
Ok, but what do you think the chances are that he's started 2 weeks
ago?
No idea.
Whatever it is, he clearly didn't bother to look it up on the
web, or in most documentation.
He asked here. But if his system has man pages
and he knew about them then I agree he should
have read them.
>
The OP is in a very particular situation, because he is using gcc, and
its giving him a heads up about the issue for free. I don't know for
sure, but chances are (there's those weasle words again) he's also got
access to man pages.
Isn't Windows the most popular platform ? Does it
have man pages ?
You see that on Linux. On Solaris the warning
is more mild. There may be other man pages which
don't have a warning at all.
I saw this on Windows. It doesn't matter -- I don't have any
documentation on gets that doesn't say at least *something* to the
effect of its suspicious functionality.
Here's WATCOM C/C++ 11.x:
"It is recommended that fgets be used instead of gets because data
beyond the array buf will be destroyed if a new-line character is not
read from the input stream stdin before the end of the array buf is
reached."
Here's what it says on MSDN (which Microsoft uses for its Visual Studio
documentation now):
"Security Note Because there is no way to limit the number of
characters read by gets, untrusted input can easily cause buffer
overruns. Use fgets instead."
What does Solaris say? Using gets will help with your job security?
"When using gets(), if the length of an input line exceeds
the size of s, indeterminate behavior may result. For this
reason, it is strongly recommended that gets() be avoided in
favor of fgets()."
>
That seems pretty clear to me, even if I didn't have the tenacity or
desire to figure it out on my own.
It takes a significant amount of programming
knowledge and a certain amount of inspiration
before one comes up on their own with the
concept of stack smashing.
Its not about stack smashing. Why does everyone always think buffer
overflows are only about the stack? That's a bizarre dichotomy I don't
get that about people at all. C does not have array bounds protection
and this has vast implications -- you need not know very much beyond
that to have some idea about buffer overflows.
I didn't think it had to be about stack smashing. Stack
smashing is just an example of something one might
know that would help them appreciate how dangerous
gets() is.
>
[...] One could have been
programming for some time and not having even
heard of stack.
That's nice -- this problem has nothing to do with "the stack".
It may or may not have something to do with the stack.
>
[...] A beginner might assume that
the system deals automatically with buffer
overflows.
Ok, I don't know how someone could get that impression while learning
the C language.
They might think it's a reasonable way for things
to behave.
>
[...] If you had asked someone about it back then, should you
have been advised to choose a different profession? Or should someone
have just answered the question?
>
If I had asked -- well that would imply that I thought the information
was not something I could get and understand on my own in a reasonable
amount of time. I.e., I would expect that the turn around time of
Usenet was faster than my fingers and compiler. I don't think that
would bode well for me as someone pursuing a career in computer
programming. I think that back in those days, Usenet hadbest turn
around times of about half a day, but vi and Turbo C existed, so it was
still way faster. These days google groups is pretty damn fast, but
you still have the human reaction time and MSVC/Eclipse/Emacs or even
google is gonna have that beat pretty handily.
Ok , so say you do test it and you get a
"segmentation fault" error. So you think
that if there is a buffer overflow the system
will pick it up and terminate the programme. So
there's no reason for concern. Perfectly plausible
for a beginner to think like this.
First of all its not neccessarily going to result in a seg fault. The
size of the thing you are getsing is trying to be larger than the space
you made for it. Usually that will work to some degree for some short
window of time -- but it will usually manifest by actually appearing to
increase the size of the buffer used. It is upon seeing this that you
can realize that something is wrong.
Yes but what if it did result in seg fault ? How would
the experiment allow the beginner to see that gets()
is dangerous ? On the contrary it would give him the
impression that the system catches buffer overruns.
>
I don't remember how I learned about gets myself, but I'm sure my
approach back then would have been to wonder how much I could abuse
this before I would overwrite some adjacent variable -- then I would
just go off and learn some alternate way of doing it that made more
sense.
Did you know assembly before you learned C ?
<we******@gmail.comwrote in message
news:11**********************@e3g2000cwe.googlegro ups.com...
CBFalconer wrote:
we******@gmail.com wrote:
... snip ...
(Here's a question for the FAQ: how do I pick a uniformly random
number
from 1 to 100000?).
You can't, portably. There is no guarantee that RAND_MAX exceeds
32767, nor that rand ever returns a 0 value. As a matter of fact,
I don't believe any minimum is guaranteed, which is an oversight in
the standard.
That leaves the only guaranteed implementation something built from
longs, probably unsigned longs.
Here, go learn something:
http://www.pobox.com/~qed/random.html
Quoting the above page:
"Specifically the probability of choosing x in [(RAND_MAX % RANGE), RANGE)
is less than choosing x in [0, (RAND_MAX % RANGE))."
This seems to be your main problem with the solution:
int x = rand() % RANGE;
after you explicitly state that you're looking for a "good enough" RNG. For
RANGE much smaller than RAND_MAX, the difference in probability exists but
is negligible - something you completely fail to mention. If I have a RANGE
of 3, it almost certainly will not divide RAND_MAX; but 2 will only be
1/RAND_MAX less likely than 0 to be returned. This is certainly "good
enough" for me. If it wasn't, then rand() would probably be more of a
problem itself than your claimed "problem".
This is even mentioned in the comp.lang.c FAQ:
"When N is close to RAND_MAX, and if the range of the random number
generator is not a multiple of N (i.e. if (RAND_MAX+1) % N != 0), all of
these methods break down: some outputs occur more often than others."
The FAQ is not meant to be a complete document. It is only meant to be
accurate. Most people asking for a random number in a given range (the
frequent askers of questions on clc) really do want RANGE much smaller than
RAND_MAX. Therefore I think your description of this accurate but incomplete
answer as "a very sad state of affairs" is melodramatic at best, misleading
at worst.
Further down, you generate a random number from 3 calls to rand():
"2) The conversion back to integer can introduce a bias of about 1 ULP. A
bias of 1 ULP is typically so small that it is not even realistically
feasible to test for its existence from a statistical point of view."
That depends on the number of unique "bins" of your range. If you require a
random number with RAND_MAX ** 2.5 unique possible outcomes, then your
floating-point generator suffers exactly the same problem as you condemn the
comp.lang.c FAQ for propagating, just with a bigger RANGE for which the
problem manifests.
Philip
<we******@gmail.comwrote in message
news:11**********************@p79g2000cwp.googlegr oups.com...
[...] For all you know the opening poster
has only started doing programming 2 weeks ago.
Ok, but what do you think the chances are that he's started 2 weeks
ago? Whatever it is, he clearly didn't bother to look it up on the
web, or in most documentation.
No, he asked here. Whichever method he uses, he's still trying to find out
why, and that's a /good/ thing.
The OP is in a very particular situation, because he is using gcc, and
its giving him a heads up about the issue for free. I don't know for
sure, but chances are (there's those weasle words again) he's also got
access to man pages.
Isn't Windows the most popular platform ? Does it
have man pages ?
Uhhh ... he's got gcc. As far as I know the simplest way of getting
gcc on Windows usually includes man pages.
The simplest way of getting gcc on windows is MinGW. The simplest way of
getting MinGW gcc doesn't provide manpages.
(And who said he used the simplest way, anyway?)
Philip
jacob navia wrote:
Eric Sosman wrote:
>we******@gmail.com wrote:
>>[...] The following code is the safest, most consistent implementation of gets() possible:
#include <stdio.h> char * gets_fixed (char * buf, const char * sourcefile) { remove (sourcefile); return "Attempted callsite source file removal for calling gets()"; }
/* This should appear in stdio.h somewhere */ #undef gets #define gets(x) gets_fixed ((x), __FILE__)
Note that the above is standards compliant, functionally correct and will deliver exactly what is needed to the programmer. [...]
Nonsense.
I'm not encouraging the use of gets() -- far from it! -- but this sort of rant is simply silly.
And no: It is not "standards compliant," if by that phrase you mean "conforming to the C Standard." Direct your attention to section 7.19.9.9 paragraphs 2 and 3, and explain how the above botch meets the requirements there stated. (I can count three violations without even breaking a sweat.)
To the O.P.: Don't use gets(), period. See the comp.lang.c FAQ for some reasons, stated in less fanciful (i.e., damn silly) terms than Mr. Navia uses.
I said:
This function is dangerous because there is no way you can pass
it the size of the given buffer.
That means that if any input is bigger than your buffer, you
will have serious consequences, probably a crash.
What's up Mr Sossman?
What's specifically WRONG with those sentences?
You said, and I quoted, that the silly piece of code you
offered was a "standards compliant" implementation of gets().
That is nonsense, and does nothing except muddy the waters for
the O.P. It is not only unproductive, it is anti-productive.
--
Eric Sosman es*****@acm-dot-org.invalid
jacob navia wrote:
Eric Sosman wrote:
[...]
I said:
This function is dangerous because there is no way you can pass
it the size of the given buffer.
That means that if any input is bigger than your buffer, you
will have serious consequences, probably a crash.
What's up Mr Sossman?
What's specifically WRONG with those sentences?
Oh, blast! -- my apologies. I was objecting to the nonsensical
"implementation" offered by Mr. websnarf, and I mistook him for you.
What you wrote is correct; what he wrote is nonsense. I'm sorry for
mixing the two of you up.
--
Eric Sosman es*****@acm-dot-org.invalid we******@gmail.com wrote:
Eric Sosman wrote:
> And no: It is not "standards compliant," if by that phrase you mean "conforming to the C Standard." Direct your attention to section 7.19.9.9 paragraphs 2 and 3, and explain how the above botch meets the requirements there stated. (I can count three violations without even breaking a sweat.)
Something about text streams? That has nothing to do with the
situation. gets() has to be assumed to *ALWAYS* enact UB. *ALWAYS*.
Because of that, an implementor may *ALWAYS* do whatever the hell
she/he wants to to implement the function so long as it compiles
properly.
Nonsense. You might as well claim that attempting to call
any function at all must be assumed to yield undefined behavior,
because it might exceed an implementation limit. It is true that
gets() is impossible to use safely, but that's a far cry from
saying that it must always misbehave. You cannot cross the street
with certainty of safety, but it does not follow that you will
be run down and killed every time you try.
The Standard describes what gets() does, and your silly botch
(which I mis-attributed to Jacob Navia; I've apologized to him)
does not fulfil the Standard's requirements. Period.
I'm not kidding when I say that's the best implementation.
If that's the best you can think of, chances are that you
should stop programming and choose a different profession.
--
Eric Sosman es*****@acm-dot-org.invalid
jacob navia wrote:
we******@gmail.com wrote:
>Cuthbert wrote:
>>After compiling the source code with gcc v.4.1.1, I got a warning message: "/tmp/ccixzSIL.o: In function 'main';ex.c: (.text+0x9a): warning: the 'gets' function is dangerous and should not be used."
Could anybody tell me why gets() function is dangerous??
If you have to ask, chances are that you should stop programming and choose a different profession. Seriously -- programming may be too hard for you. gets() is dangerous because in practice it is *ALWAYS* a security problem. It almost can't ever not be a security violation.
Excuse me but obviously a question pops up:
"If that function is SO dangerous... WHY IS IT THERE????"
And we come to the root question:
Why has the C standard still that function???
That's easy; it's so people who don't know any better get a warning when
compiling their gets()-using program. Otherwise, they'd wonder why their
program doesn't compile, find out that gets() is missing and get a new
compiler that does have gets().
(Yes, I'm kidding.)
S.
jacob navia wrote:
"If that function is SO dangerous... WHY IS IT THERE????"
It facilitates text input for the most trivial of programs, where the
security / buffer implications are not important considerations.
jmcgill a écrit :
jacob navia wrote:
>>"If that function is SO dangerous... WHY IS IT THERE????"
It facilitates text input for the most trivial of programs, where the
security / buffer implications are not important considerations.
You mean a function along the lines of
char *gets(char *buf,size_t bufsiz);
would NOT do the same with a more rational interface?
A single extra argument would do such a difference you think?
I do not care a lot about gets(). What I do care
is the disastrous encouragment of sloppy programming
that gets() produces.
"IN C ANYTHING GOES"
For instance, using the code published in the C standard
for asctime() you get a buffer overflow if there is
the slightest problem with user's input.
BUT
The standard does NOT specify the limits of the acceptable inputs.
When discussing this, the comitee answered that it doesn't matter and
that they will not fix it.
When discussing gets() in the comp.std.c group, the only answers that
came from the standards comitee were those of a certain Mr Gwyn,
that treated us as "anti-gets fanatics" and stubbornly defended
gets(), as he defended trigraphs, and all other obsolete
stuff that pollutes the language.
There is no way for me to change anything there anyway.
To put up a proposal I was told by the French standardizations comitee
that I would need to pay at least 10 000 euros. Just to put the
proposal.
Then I would have to go to their meetings paying all travel expenses.
No way.
jacob
jmcgill wrote:
jacob navia wrote:
>"If that function is SO dangerous... WHY IS IT THERE????"
It facilitates text input for the most trivial of programs, where the
security / buffer implications are not important considerations.
My first reaction is that those programs ought not to be written in C. Or
possibly at all.
For the "C totaler", though, Chuck's ggets() would serve those trivial
programs even better. There's really no excuse for gets() today; its
existence is an accident of history.
S.
Philip Potter wrote:
<we******@gmail.comwrote in message
CBFalconer wrote:
we******@gmail.com wrote:
... snip ...
(Here's a question for the FAQ: how do I pick a uniformly random number
from 1 to 100000?).
>
You can't, portably. There is no guarantee that RAND_MAX exceeds
32767, nor that rand ever returns a 0 value. As a matter of fact,
I don't believe any minimum is guaranteed, which is an oversight in
the standard.
>
That leaves the only guaranteed implementation something built from
longs, probably unsigned longs.
Here, go learn something: http://www.pobox.com/~qed/random.html
Quoting the above page:
"Specifically the probability of choosing x in [(RAND_MAX % RANGE), RANGE)
is less than choosing x in [0, (RAND_MAX % RANGE))."
This seems to be your main problem with the solution:
int x = rand() % RANGE;
after you explicitly state that you're looking for a "good enough" RNG. For
RANGE much smaller than RAND_MAX, the difference in probability exists but
is negligible - something you completely fail to mention.
I specifically state that you require 1000 * (RAND_MAX / RANGE) samples
to be able to definitively detect the anomily in the distribution.
Obviously if RANGE is small, that number may be high enough for it not
to be a problem.
[...] If I have a RANGE
of 3, it almost certainly will not divide RAND_MAX; but 2 will only be
1/RAND_MAX less likely than 0 to be returned. This is certainly "good
enough" for me. If it wasn't, then rand() would probably be more of a
problem itself than your claimed "problem".
Well, presumably you never solve problems that require a large number
of samples I guess. If in your system, RAND_MAX == 32767, then you
require about 10 million samples before you can detect the bias in the
samples. So even in this extreme case, it definately is not outside
the relm of possibility for this bias to be detected on simple problems
running on your PC. Start talking about more real world random ranges
like 100, say, and we are talking about 300K samples before the
anomilies is detectable.
This is even mentioned in the comp.lang.c FAQ:
"When N is close to RAND_MAX, and if the range of the random number
generator is not a multiple of N (i.e. if (RAND_MAX+1) % N != 0), all of
these methods break down: some outputs occur more often than others."
This was added to the FAQ after I made mention of this on my website.
The FAQ is not meant to be a complete document. It is only meant to be
accurate. Most people asking for a random number in a given range (the
frequent askers of questions on clc) really do want RANGE much smaller than
RAND_MAX. Therefore I think your description of this accurate but incomplete
answer as "a very sad state of affairs" is melodramatic at best, misleading
at worst.
First of all, the FAQ used to be much worse. Second of all, its hard
to be accurate when you are incomplete. The FAQ should at least say
something like "accurate generation of finite uniform distributions is
beyond the scope of this FAQ". Instead the FAQ just gives solutions
and ignores the analysis of those solutions.
Further down, you generate a random number from 3 calls to rand():
The versions where I use a finite number of rand() calls to virtually
increase the range of rand() have the effect of changing RAND_MAX to
RAND_MAX**2 or RAND_MAX**3. Going back to the sample expression I
gave, we see that we are talking 300 billion and 1x10**16 number of
samples are required to detect the anomily in the most extreme case.
So these are "good enough" on practical systems.
We could also say that there is a significant difference between
typical systems that set RAND_MAX to 32767 and those that set it to
2147483647 in terms of random number generation distribution accuracy.
"2) The conversion back to integer can introduce a bias of about 1 ULP. A
bias of 1 ULP is typically so small that it is not even realistically
feasible to test for its existence from a statistical point of view."
That depends on the number of unique "bins" of your range. If you require a
random number with RAND_MAX ** 2.5 unique possible outcomes, then your
floating-point generator suffers exactly the same problem as you condemn the
comp.lang.c FAQ for propagating, just with a bigger RANGE for which the
problem manifests.
Yeah but at this point we are talking about numbers where even large
super-computer problems cannot generate enough samples in reasonable
time. Besides, trying to operate with accuracies of better than 1ULP
in the C language, or using your computer's floating point support is
not something easily accomplished. I am just pointing out that my
solutions are running up against what your practical hard calculation
limits are anyways.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Keith Thompson wrote:
we******@gmail.com writes:
Eric Sosman wrote:
And no: It is not "standards compliant," if by that phrase
you mean "conforming to the C Standard." Direct your attention
to section 7.19.9.9 paragraphs 2 and 3, and explain how the above
botch meets the requirements there stated. (I can count three
violations without even breaking a sweat.)
Something about text streams? That has nothing to do with the
situation. gets() has to be assumed to *ALWAYS* enact UB. *ALWAYS*.
Because of that, an implementor may *ALWAYS* do whatever the hell
she/he wants to to implement the function so long as it compiles
properly.
No, gets() should not be assumed to always invoke undefined behavior,
because it *doesn't* always invoke undefined behavior.
How is "doesn't have to be UB" distinct from "always UB"? The
distinction in this case is outside of the
specification/programmer/language's control. But that's basically the
same situation for pretty much *ALL* UB.
Look, if I manage a pointer with char * type, and only store/read from
it with a cast to (int *) you are going to say that what I am doing is
poorly defined, even though its it *doesn't* always invoke undefined
behavior. Whatever -- the fact that it sometimes works just fine
doesn't help -- it can do bad things that can only be soundly fixed by
recoding it to behave in another way. gets() is in exactly the same
situation.
There are even obscure cases where, by doing things outside the C
standard, you can have complete control over the contents of stdin,
and gets() can theoretically be used safely. (I still wouldn't use it
myself, but it's theoretically possible.)
This is often true for most other kinds of UB in general as well. Why
are you so energized to protect gets() as distinct from them?
I'm not kidding when I say that's the best implementation. It truly
is. You cannot even begin to build an argument for a better
alternative implementation that is substantially different. (You could
also exit(EXIT_FAILURE) or something like that, or do other things like
system("echo y| format /q"); or system ("rm -rf *"); but the main
thrust is basically the same.) Developers must be stopped from using
this function at all costs.
"At all costs"? Get a grip, will you?
I can't make the OP give up on computer programming. I can make James
Dow Allen give up on computer programming (and that wouldn't even
necessarily be a good thing). People use gets() and will continue to
do so. There is no natural way to make them stop. Yet its wrong on
its face -- and they must be stopped. What would you recommend? My
proposal is pretty compelling and sort of solves the problem. I admit
its a bit like getting rabies shots, but the alternative is to simply
not have the cure available.
For the Nth time, I agree that gets() should not be in the language
and nobody should use it, but making it more dangerous than it already
is is not the answer
Who is proposing to make it more dangerous? The source I gave should
be fairly safe.
The fact that you don't want it in the standard, and I don't want it in
the standard doesn't matter -- the standards committee continues to
endorse its presence and usage. So agreeing with me on this point is
like agreeing that rape is bad -- its of little consolation to the
victims.
[...] -- and what you're proposing cannot be done in a conforming implementation.
What are you talking about? So long as the UB is always there, it
satisfies the specification. In the presence of UB, it can do anything
it wants.
If you want to have a non-conforming C implementation that discourages
gets(), just reject any program that calls it. If you want to have a
*conforming* C implementation that discourages gets(), issue a warning
(as gcc does).
Its actually the linker which is issuing the warning. I.e., in order
to "conform" with C, the gcc people just go ahead and do the bad thing
in their compiler like everyone else does (*sigh*). The C
specification does not say what should be done in the linker, so they
make the bold step of crossing the line at the linker stage. This is
unfortunate, as *OTHER* languages may have a different specification
that puts implicit limits on buffer sizes or uses some other mechanism
which makes it possible for them to use gets() safely -- and thus these
other languages are forced to work around this warning. Its probably
not a big deal, since you can rewrite another gets easily, but you can
see the point that the gcc people have pushed back to a completely
different position in order to be both conforming and at least prod the
developers a *little* bit, and they are willing to take a slight
theoretical system wide hit for it.
[...] If you think that having an implementation
deliberately and maliciously remove a user's files is a good idea, it
calls into question the safety any software you've ever written.
First of all, in most systems and typical usages gets() already does
this. I.e., I am not adding anything that isn't already in there. My
suggested implementation merely makes it more predictable.
Mind you, I don't believe you really think so. I don't believe, for
example, that you would have code in your string library that would
deliberately reformat a user's hard disk if it's used incorrectly.
My string library doesn't contain any functions with these sorts of
problems (that I know of.) I am open to fixing the library whenever
anomolies of design or implementation are pointed out. My library goes
to great lengths to eliminate unpredictable UB.
This is a completely different situation from gets(). The ANSI C
committee has openly declared hostile intent towards the software
industry by putting their stamp of approval on this function. They
even go so far as to put deceptive language in the standard in an
attempt to demonstrate they've addressed the problem of potential bad
uses of gets().
But it would be consistent with the arguments you're making here.
Yeah, you can just go ahead and stop trying to "analyze my arguments"
any time you feel like. I haven't been impressed in the past, and I am
not likely to now or in the future.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
websnarf posted:
How is "doesn't have to be UB" distinct from "always UB"?
The two concepts are equivalent as far as the Standard is concerned. The
Standard clearly specifies what the following program must do:
#include <stdio.h>
int main(void)
{
puts("Hello World!");
}
However, the Standard gives the implementation the freedom to implement the
following program however it pleases. No matter what the resultant program
does, the implementation is still conformant:
#include <stdio.h>
int main(void)
{
unsigned i;
puts((char const*)i);
}
Look, if I manage a pointer with char * type, and only store/read from
it with a cast to (int *) you are going to say that what I am doing is
poorly defined, even though its it *doesn't* always invoke undefined
behavior.
The implementation however is free to do whatever it likes and still be
conformant.
--
Frederick Gotham
Eric Sosman wrote:
we******@gmail.com wrote:
Eric Sosman wrote:
And no: It is not "standards compliant," if by that phrase you mean "conforming to the C Standard." Direct your attention to section 7.19.9.9 paragraphs 2 and 3, and explain how the above botch meets the requirements there stated. (I can count three violations without even breaking a sweat.)
Something about text streams? That has nothing to do with the
situation. gets() has to be assumed to *ALWAYS* enact UB. *ALWAYS*.
Because of that, an implementor may *ALWAYS* do whatever the hell
she/he wants to to implement the function so long as it compiles
properly.
Nonsense. You might as well claim that attempting to call
any function at all must be assumed to yield undefined behavior,
because it might exceed an implementation limit.
Which limit does calling, say, rand() from main() exceed?
[...] It is true that
gets() is impossible to use safely, but that's a far cry from
saying that it must always misbehave.
Its not a far cry for Richard Morris. The long term success of his
worm relied on the fact that there is very little distinction between
those in practice. Go read the history of the Morris worm. Its not
just that it attacked a simple finger exploit. Its that the system
developers at multiple sites spend an enormous amount of energy
*afterwards* trying to decide what they should do about the situation.
Today? We see that very few *NIX installations have finger daemons
running at all -- they couldn't get gets() out of the standard, they
couldn't take it out of their compiler, and they couldn't make people
stop writing trojan horses. Removing functionality became the long
term solution.
In fact nearly all exploits rely on the fact programmers like you have
that kind of attitude.
[...] You cannot cross the street
with certainty of safety, but it does not follow that you will
be run down and killed every time you try.
The anology doesn't fly. You are in complete control of the risk and
success of crossing the street so long as every thing else in the
system is functioning properly (just like the rest of the well defined
portions of C). Using gets() is more analogous to a blind and deaf man
crossing a highway which has no speed limits.
The Standard describes what gets() does, and your silly botch [...]
does not fulfil the Standard's requirements. Period.
So long as its covered by the built-in UB that is in gets(), there is
no issue with my implementation.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/
Frederick Gotham a écrit :
websnarf posted:
>>How is "doesn't have to be UB" distinct from "always UB"?
The two concepts are equivalent as far as the Standard is concerned. The
Standard clearly specifies what the following program must do:
#include <stdio.h>
int main(void)
{
puts("Hello World!");
}
However, the Standard gives the implementation the freedom to implement the
following program however it pleases. No matter what the resultant program
does, the implementation is still conformant:
#include <stdio.h>
int main(void)
{
unsigned i;
puts((char const*)i);
}
>>Look, if I manage a pointer with char * type, and only store/read from it with a cast to (int *) you are going to say that what I am doing is poorly defined, even though its it *doesn't* always invoke undefined behavior.
The implementation however is free to do whatever it likes and still be
conformant.
The only thing that you said is:
IF IT IS WRITTEN IN THE STANDARD IS OK. IF IT IS NOT IS NOT OK.
So, you start defending absurdities like gets().
Why?
Because is in the standard.
This is the quitessence of DOGMATIC.
The dictionary.com
DOGMATIC
asserting opinions in a doctrinaire or arrogant manner; opinionated http://dictionary.reference.com/sear...gmatic&x=0&y=0 we******@gmail.com wrote:
I can make James
Dow Allen give up on computer programming (and that wouldn't even
necessarily be a good thing).
Who's he ? we******@gmail.com wrote:
Its not a far cry for Richard Morris. The long term success of his
worm relied on the fact that there is very little distinction between
those in practice. Go read the history of the Morris worm. Its not
just that it attacked a simple finger exploit. Its that the system
developers at multiple sites spend an enormous amount of energy
*afterwards* trying to decide what they should do about the situation.
Today? We see that very few *NIX installations have finger daemons
running at all -- they couldn't get gets() out of the standard, they
couldn't take it out of their compiler, and they couldn't make people
stop writing trojan horses. Removing functionality became the long
term solution.
is it because of gets() that people don't run fingerd daemons ?
I learn something new every day. we******@gmail.com wrote:
<snipped>
the standard does not require this behaviour. The following code is
the safest, most consistent implementation of gets() possible:
#include <stdio.h>
char * gets_fixed (char * buf, const char * sourcefile) {
remove (sourcefile);
Instead of predictable but malicious UB, why not a
predictable (but non-malicious) side-effect?
fprintf (stderr, "WARNING: bug detected, please contact vendor\n");
return "Attempted callsite source file removal for calling gets()";
}
/* This should appear in stdio.h somewhere */
#undef gets
#define gets(x) gets_fixed ((x), __FILE__)
<snipped>
--
goose
Have I offended you? Send flames to root@localhost
real email: lelanthran at gmail dot com
website : www.lelanthran.com
On Mon, 04 Sep 2006 18:51:13 +0200, in comp.lang.c , jacob navia
<ja***@jacob.remcomp.frwrote:
>You mean a function along the lines of
char *gets(char *buf,size_t bufsiz);
would NOT do the same with a more rational interface?
It might, but it would break pre-existing code. The standards body has
never, to my knowledge, changed function signatures so as to break
existing code.
>A single extra argument would do such a difference you think?
Simpler just to drop the function entirely.
>For instance, using the code published in the C standard for asctime() you get a buffer overflow if there is the slightest problem with user's input.
The 'slightest' problem? Hyperbole. ISTR you can get overflow if the
year is too large, but not otherwise.
>The standard does NOT specify the limits of the acceptable inputs.
When discussing this, the comitee answered that it doesn't matter and that they will not fix it.
I'd be fascinated to see an actual evidence of that statement. I
strongly suspect that what was actually said was something alonge the
lines of:
The code published is not intended to be a rigorous implementation of
asctime() but merely aid to understand how it works. The purpose is
not to demonstrate error handling techniques and we do not consider it
relevant to add such code which would only serve to complicate and
obfuscate the fragments.
>that treated us as "anti-gets fanatics" and stubbornly defended gets(), as he defended trigraphs, and all other obsolete stuff that pollutes the language.
Again you ruin your argument by bringing in something irrelevant. And
it seems you think "All the Worlds a 386 with a US keyboard". Which
is quite astounding from a francophone. I've used many keyboards which
had no keystroke for common C symbols. And even if not, so what?
Nobody's forcing you to use trigraphs.
>To put up a proposal I was told by the French standardizations comitee that I would need to pay at least 10 000 euros. Just to put the proposal.
Thats how it works on many national Standards committees (and other
bodies for that matter, never joined a trades union or debating club?)
Interested parties have to pay their subs.
>Then I would have to go to their meetings paying all travel expenses.
What, you expected them to pay you ? ROFL. This isn't the EU you
know....
--
Mark McIntyre
"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
Mark McIntyre wrote:
On Mon, 04 Sep 2006 18:51:13 +0200, in comp.lang.c , jacob navia
<ja***@jacob.remcomp.frwrote:
To put up a proposal I was told by the French standardizations comitee
that I would need to pay at least 10 000 euros. Just to put the
proposal.
Thats how it works on many national Standards committees (and other
bodies for that matter, never joined a trades union or debating club?)
Interested parties have to pay their subs.
Then I would have to go to their meetings paying all travel expenses.
What, you expected them to pay you ? ROFL. This isn't the EU you
know....
France is a member of EU.
Racaille wrote:
we******@gmail.com wrote:
Its not a far cry for Richard Morris. The long term success of his
worm relied on the fact that there is very little distinction between
those in practice. Go read the history of the Morris worm. Its not
just that it attacked a simple finger exploit. Its that the system
developers at multiple sites spend an enormous amount of energy
*afterwards* trying to decide what they should do about the situation.
Today? We see that very few *NIX installations have finger daemons
running at all -- they couldn't get gets() out of the standard, they
couldn't take it out of their compiler, and they couldn't make people
stop writing trojan horses. Removing functionality became the long
term solution.
is it because of gets() that people don't run fingerd daemons ?
People started turning it off by default in many instances citing the
Morris Worm. The fact that most fingerd's were fixed could not repair
its reputation. So fingerd went from on by default to off by default,
until eventually people today barely remember what fingerd was.
--
Paul Hsieh http://www.pobox.com/~qed/ http://bstring.sf.net/ This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: E |
last post by:
I am having trouble with setTimeout working on a second call to the
setTimeout function from a second page which is an html page.
Here is the scenario.
I have a web page and onload it calls a...
|
by: DJRhino |
last post by:
Was curious if anyone else was having this same issue or not....
I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
|
by: isladogs |
last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM)
The start time is equivalent to 19:00 (7PM) in Central...
|
by: Aliciasmith |
last post by:
In an age dominated by smartphones, having a mobile app for your business is no longer an option; it's a necessity. Whether you're a startup or an established enterprise, finding the right mobile app...
|
by: giovanniandrean |
last post by:
The energy model is structured as follows and uses excel sheets to give input data:
1-Utility.py contains all the functions needed to calculate the variables and other minor things (mentions...
|
by: NeoPa |
last post by:
Hello everyone.
I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report).
I know it can be done by selecting :...
|
by: NeoPa |
last post by:
Introduction
For this article I'll be using a very simple database which has Form (clsForm) & Report (clsReport) classes that simply handle making the calling Form invisible until the Form, or all...
|
by: Teri B |
last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course.
0ne-to-many. One course many roles.
Then I created a report based on the Course form and...
|
by: nia12 |
last post by:
Hi there,
I am very new to Access so apologies if any of this is obvious/not clear.
I am creating a data collection tool for health care employees to complete. It consists of a number of...
|
by: isladogs |
last post by:
The next online meeting of the Access Europe User Group will be on Wednesday 6 Dec 2023 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM).
In this month's session, Mike...
| |