LWN.net Logo

What If I Don't Actually Like My Users?

What If I Don't Actually Like My Users?

Posted Apr 4, 2008 17:02 UTC (Fri) by JoeBuck (subscriber, #2330)
Parent article: What If I Don't Actually Like My Users?

The gets() function actually achieves criterion 10: it's impossible to get right. Any use at all means that a very long line in the input will cause a buffer overflow.


(Log in to post comments)

What If I Don't Actually Like My Users?

Posted Apr 4, 2008 20:06 UTC (Fri) by nix (subscriber, #2304) [Link]

Hm, yes. I was going to say that tmpnam() et al are impossible to get 
right, but they're not: you can run them in constrained environments in 
which you know you won't get attacked. You don't *need* to be attacked for 
gets() to shoot you in the head.

(Why oh why were gets(), puts() and the other pre-stdio functions not 
quietly retired when stdio was invented? At least gets() is rarely used in 
free software, although probably not as rarely as seekdir()/telldir(), 
which I've never even heard of anyone using.)

What If I Don't Actually Like My Users?

Posted Apr 4, 2008 20:38 UTC (Fri) by xbobx (subscriber, #51363) [Link]

> Why oh why were gets(), puts() and the other pre-stdio functions not quietly retired when stdio was invented? puts() is still used all the time. In fact, for the almost-simplest of programs:
#include <stdio.h>
int main(void) {
    printf("hi\n");
    return 0;
}
gcc 4.1.2 on my system generates the following:
sub    $0x8,%rsp
mov    $0x4005f8,%edi
callq  400430 <puts@plt>
xor    %eax,%eax
add    $0x8,%rsp
retq   
If you use anything more complicated than a constant static string it will actually call printf().

What If I Don't Actually Like My Users?

Posted Apr 4, 2008 20:49 UTC (Fri) by nix (subscriber, #2304) [Link]

Well, yeah, but as the analogue of gets(), entirely redundant to fputs(), 
both should have gone if either do, and gets() certainly should have gone.

But it didn't.

What If I Don't Actually Like My Users?

Posted Apr 5, 2008 3:33 UTC (Sat) by njs (subscriber, #40338) [Link]

Any use at all of gets() does cause a linker warning, at least.

What If I Don't Actually Like My Users?

Posted May 19, 2008 8:32 UTC (Mon) by TBBle (guest, #52146) [Link]

> At least gets() is rarely used in 
> free software, although probably not as rarely as seekdir()/telldir(), 
> which I've never even heard of anyone using.

Samba uses it... http://www.vnode.ch/fixing_seekdir

Mind you, I wouldn't have known Samba was using it either (and in fact it took me a little
while to wrap my head around why) before I saw that article.

What If I Don't Actually Like My Users?

Posted May 19, 2008 19:55 UTC (Mon) by nix (subscriber, #2304) [Link]

So a really quite substantial bug (affecting perhaps 3% of all calls to 
this function in nontrivial directories) persisted for *a quarter of a 
century* before anyone noticed it.

I suspect that seekdir()/telldir() has exactly one user: Samba. Given how 
horrible it makes filesystem implementations, and the closeness of Samba 
implementors to the kernel, I'm not sure that it's worth preserving this 
function for that one user (which is privileged in any case so the usual 
oops-it-might-use-up-too-much-memory arguments against a naive 
entirely-in-VFS implementation do not apply).

Votes to make seekdir()/telldir() root-only, anyone?

Pedantry

Posted Apr 5, 2008 9:46 UTC (Sat) by tialaramex (subscriber, #21167) [Link]

It's not /impossible/ to get right although I agree that it provides no functionality you
would otherwise want that's not better implemented elsewhere in a modern POSIX API.

It's not impossible for the same reason that the non-thread safe functions aren't impossible
to get right, and the temporary file functions that aren't protected against races aren't
impossible to use safely. So long as you don't need that safety, they're fine. Similarly
gets() is fine so long as you don't need any protection against over-long strings.

e.g. suppose you have a socket, over which you receive instructions from a device driver in
the kernel, in this case gets() can be appropriate because the inverted privilege separation
means that crashing you with a buffer overflow achieves nothing - as a userspace process you
actually have less privileges than the kernel driver calling you. So long as there's an agreed
rule about buffer size (e.g. each instruction is the name of a file, so max pathname plus a
newline) and a mechanism ensuring that you are connected to that kernel driver as intended,
then gets() adds no new danger to your code.

Still, this is such a corner case, and there are so many better ways to approach this problem,
that the compiler/ linker is justified in complaining if you use this function in new code.

I've never found a sensible use for gets() in my own code, but I have used various other
"non-safe" functions, like tmpnam in contexts where the newer "safer" function didn't do what
I needed, and I judged that the safety issue was something I could cope with after reading
about it. I think I had to roll my own mkdtemp() some years ago for example, using "unsafe"
tmpnam and lots of careful reading of a treatise on race conditions in the filesystem.

A good example of an API that's _really_ impossible to use is one that Raymond Chen rants
about regularly, a Windows API call which purports to tell you whether a pointer is "valid" ie
whether you'd take a page fault if you tried to access it. This is simultaneously useless (it
doesn't do anything a userspace programmer should be trying to do) and dangerous (it will
itself inadvertently page in RAM if you call it on the edge of the stack for example) and
unreliable (the page may appear or vanish before control returns to your program with the
result). But having been foolishly offered to programmers in the past, Windows continues to
provide it for compatibility.

Pedantry

Posted Apr 5, 2008 10:05 UTC (Sat) by tialaramex (subscriber, #21167) [Link]

Ah, I got part of that last example wrong. It's dangerous because it will disable the stack
extension, ie it doesn't cause a new page to be mapped at the edge of the stack, but rather
overrides the runtime's automatic mapping of that page, returns to you an result that it isn't
yet mapped, but doesn't restore the stack extension behaviour. So suddenly, and without
explanation, your stack can't grow.

For a POSIX example that's less dangerous but just as unreliable/ useless, try access(2) which
attempts to guess whether you'd be allowed to do something, but can't assure you whether you
will or will not be able to do it if you actually try. It's not even useful for checks like
those in ssh which wants to see if you obeyed the rules for your own safety, since ssh needs
to check access permissions for /other users/.

Copyright © 2012, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds