4 ms·
This is a pure glibcism. FreeBSD libc does similar with getenv, in that the returned environment is a snapshot of that point in time.
by Sanddancer 10y ago
This is a pure glibcism. FreeBSD libc does similar with getenv, in that the returned environment is a snapshot of that point in time.
- simias 10y agoIMO getenv's API itself is to blame since it won't let you provide a buffer. As such if the libc wants to provide a local buffer it has to figure out a way to allocate it for the caller. As far as I can tell glibc's implementation is probably what the people who designed getenv in the first place had in mind, just return a pointer to a global buffer. At best FreeBSD managed to create a workaround for modern environments. There are unfortunately many such oddities in the standard libc APIs, remnants from an other time. Let us remember that "gets" was standardized as part of the C standard at some point, a function that's by design literally impossible to use safely.
- cesarb 10y ago> Let us remember that "gets" was standardized as part of the C standard at some point, a function that's by design literally impossible to use safely. It _is_ possible in theory to use gets safely, as long as your standard input is trusted. For instance, if a process forks and connects the standard input in the child to a pipe from the parent, which always writes a fixed amount of data to the pipe, you can use gets() without risk of overflow. (This is a really contrived scenario, but I don't know of any simpler one where using gets() is safe.)
- MaulingMonkey 10y ago> It _is_ possible in theory to use gets safely, as long as your standard input is trusted. Only if we define "trusted" to include "known to be bug free" in e.g. it's truncation or bounding of output over the pipe to the child. I argue that, in theory, this is impossible to know, and thus that it this level of trust is impossible, and thus that this is not an example of a potential safe use of gets. Even a mathematical proof of safety, after all, could contain errors - or could prove the wrong thing - or could apply to the code as written and not the code as messed with by your optimizer - or another thread - or an injected dll - or ... > For instance, if a process forks and connects the standard input in the child to a pipe from the parent, which always writes a fixed amount of data to the pipe, you can use gets() without risk of overflow. This is also insufficient - one must also prevent nonstandard invocations of the child process. Even if your normal parent process gives the child input that is 100% safe, that's no guarantee that an attacker won't launch your child process in an unusual manner. If the child process is suid, for example, this would be a potential avenue for privilege escalation.
- cesarb 10y ago> Only if we define "trusted" to include "known to be bug free" in e.g. it's truncation or bounding of output over the pipe to the child. I argue that, in theory, this is impossible to know, and thus that it this level of trust is impossible, and thus that this is not an example of a potential safe use of gets. "In the security engineering subspecialty of computer science, a trusted system is a system that is relied upon to a specified extent to enforce a specified security policy. As such, a trusted system is one whose failure may break a specified security policy." -- https://en.wikipedia.org/wiki/Trusted_system https://en.wikipedia.org/wiki/Trusted_system That's the definition of "trusted" I'm using. > This is also insufficient - one must also prevent nonstandard invocations of the child process. I'm thinking of pure fork(), not fork+exec. That is, the child process is the same executable image, so the only way to invoke the child process in a nonstandard way would be through a debugger. And that is why the child process can trust the parent process in my example: they're the same process until the fork(). (As I said, it's a really contrived scenario. In more realistic scenarios, gets() is unsafe.)
- comex 10y agoWell, on Linux, there are various hardening mechanisms that lock down ptrace, as well as things like /proc/PID/mem/, but I don't think the same restrictions apply to grabbing fds from other processes via /proc/PID/fd/. So in theory a process running as the same user (but which can't just debug your process due to hardening) could steal your pipe and exploit your program... probably not a terribly realistic threat, but a contrived objection to a contrived scenario :)
- pjmlp 10y ago> Let us remember that "gets" was standardized as part of the C standard at some point, a function that's by design literally impossible to use safely. Which thankfully was removed by recent standard revisions of C and C++, meaning a compliant compiler isn't required to provide it any longer.
- exprA 10y agogets is impossible to use safely with unformatted input, which need not be the case.
- JdeBP 10y agoActually, what we should remember is that setenv, clearenv, unsetenv, and putenv came along a lot later than getenv. There was a fair while when the general mental model was that the environment was immutable.
- bonzini 10y agoThat is irrelevant. FreeBSD getenv doesn't protect accesses to the event with a lock, meaning that getenv will access a freed copy of the environment if a concurrent call to putenv resizes the underlying array. Musl has the same issue.
- Sanddancer 10y agoIt uses a lock-free method of ensuring that there aren't segfaults. When it needs to move a variable because it's gotten too big, it copies it to the new location and sets the pointer accordingly with the old location flagged as old. The old variable isn't freed, so there are no dangling pointers, with the downside of memory usage being increased some.
- bonzini 10y agoDoesn't it just use realloc? See __rebuild_environ in https://github.com/freebsd/freebsd/blob/master/lib/libc/stdlib/getenv.c https://github.com/freebsd/freebsd/blob/master/lib/libc/stdl...
- deathanatos 10y ago> FreeBSD libc does similar with getenv, in that the returned environment is a snapshot of that point in time. How does FreeBSD's libc know when to free that snapshot? (Since there is no "free the env snapshot you gave me" call.) If thread B calls getenv, how does that not invalidate the snapshot that thread A has?
- MaulingMonkey 10y agoThread local storage would let you store a snapshot for thread A until thread A itself re-invokes getenv. Other comments seem to imply this isn't what FreeBSD is doing however.
- quotemstr 10y agoSo what if you call getenv twice? const char* foo = getenv("FOO"); const char* bar = getenv("BAR"); foo and bar should both be valid here. Sure, you could allocate an arbitrary amount of thread-local storage, but that's still a leak.
- MaulingMonkey 10y agoGuess there's a reason FreeBSD isn't doing it :D.