POSIX wordexp(3) is dangerous

71 points by yossarian


andyc

I have seen this in libc, and I wonder why it is there at all. No shells use it to implement anything. Tilde expansion can be implemented with a much more straightforward algorithm.

And if it just forks a shell, yes that is incredibly dangerous !! (Not to mention making it impossible for shells to use it :-) )

I think the problem is that the specification of such a function involves the entire shell, because the entire shell language can appear inside command subs:

$ echo $( f() { echo 'defined function'; }; f; f && f; f | f )
defined function defined function defined function defined function

Nobody is going to implement an entire shell inside libc ... that would be crazy.

It seems like it should be removed from libc. Not sure who put it there, and why.

bentley

If I continue to have say in this, wordexp() will never be in OpenBSD's libc. Perhaps if there is one open source project which refuses to have it, coders will continue avoiding use of wordexp().

—Theo de Raadt (2010)

fanf

A few other things that make wordexp(3) dangerous:

You can’t use it in a multithreaded program, because it usually forks a shell.

On glibc wordexp is built in, but shell expansions that set variables call setenv() which is not thread safe, and which also modifies the caller’s environment which traditional forky wordexp() does not. (libc basically never calls setenv which is why this is a notable oddity.)

It really is a lunatic function that should be documented with a big “do not use this” health warning.

seb

A while back I came to the same conclusion when researching wordexp, because we were having discussions about adding a wordexp:: module to the Hare standard library. I was extremely against it, both because it's impossible to use safely on untrusted input (as you noted), and also because pretty much no one fully implements it correctly: implementations like musl's are hacks where it's trivial to find stuff like shell bypasses, and implementations like glibc which don't use the system shell (except for command substitution) can't handle edge cases like $(case 0 in *) echo hi; esac)

The compromise we ended up on was to ignore POSIX altogether. This means that Hare's wordexp always uses the system shell (even if it isn't a POSIX shell), and, more importantly, we don't implement WRDE_NOCMD at all; we instead document that the interface is only suitable for trusted inputs:

Note that, by design, this module runs arbitrary shell commands from user-supplied inputs. It must only be used in a trusted environment.

IMO providing a flag like WRDE_NOCMD which doesn't actually make the interface safe to use is worse than providing nothing at all, since it gives a false illusion of security.

The main motivator for us to implement wordexp was for the upcoming hare.ini configuration format, to declare dependencies and linker flags and stuff (that way the user can use pkg-config, for example). Although wordexp wouldn't have been my first choice, I'm happy with the compromise we ended up with.