
Spawn your shell like it's the 90s again - mulander
http://akat1.pl/?id=2
======
nothrabannosir
The posix file system API invites these race conditions. but when I want to
access the fs from my app, it often feels like the only choice I have is to
just accept it.

What can I do to change it? Is there a reliable, widespread and usable
successor to the posix API that offers transactions, or "compare and
swap"-type operations? Or are we forever caught in this catch 22 between OS
writers and application programmers ?

I wouldn't mind an API that can be (unsafely) simulated on regular posix for
compatibility, while being forward compatible with the next gen of file IO on
systems that do support it, for example.

~~~
qwertyuiop924
the problem is, you can't really avoid this kind of race condition. Not in a
pre-emptive system. You can't stat a file and open it at the same time: the
whole point of the stat is to see if you SHOULD open it. The best you could do
is maybe stat the file after you open it. But I'm not sure what other problems
that would cause.

~~~
jandrese
What's so strange about stat() on the file after you open it? It's a security
check against a known and abused exploit and it's not even hard to do. This
seems like a simple fix to the problem.

Just make sure you fstat() the file descriptor and not stat() the file to
avoid a second race condition where a malicious actor undoes their attack
immediately after you open() the file to hide the evidence.

~~~
gpvos
fstat is a more recent system call than stat, so old code bases do not use it,
and mail.local is quite ancient.

Although a quick lookup tells me that it appeared in 4.3BSD-Tahoe (June 1988)
and SysVR4 (October 1988), so one would have expected all reasonable
distributions to have gotten with the program by now.

~~~
jandrese
A little digging suggests that mail.local appeared in Version 7 Unix from
1979, so it is not a surprise that it doesn't include a syscall invented 9
years later.

Still, that syscall is 28 years old now. It's kind of embarrassing that nobody
has gone though and checked for ancient and obvious privilege escalation
issues like this. Or I guess they have, but on different OSes. This is one big
downside to fragmentation, getting fixes distributed to all of the fragments.

~~~
caf
fstat() isn't the complete fix here - it doesn't protect you against
opening/creating an unintended file through a symlink, for which you need
O_NOFOLLOW (which is a bit more recent).

------
1_2__3
Author is correct, this is an old bug. I can confirm it was at least present
in SunOS 4.1.3, because I worked for an ISP back in the 90s that offered shell
accounts and we found evidence of attempts all over the place (when you "lose"
the race a root-owned file is created, so unless you ever "win" and clean up
after yourself there's evidence of your attempt).

------
a1k0n
Why Korn shell? Is there something special about the way it handles being run
w/ a setuid bit?

~~~
mmarx
It's one of the shells in the NetBSD base system. bash, for example, is not
guaranteed to be installed.

------
asveikau
Waiting for someone to say this wouldn't be possible in rust.

Spoiler alert: it would be.

~~~
sdegutis
I don't think anyone's claiming you can't use your system's API in Rust, to do
things like gain root privileges. Just that Rust makes memory bugs and race
conditions near impossible.

~~~
asveikau
The vulnerability described in the C program (race condition in filesystem
which leads an attacker to write to a file they should have no access to) is
absolutely possible to write in rust.

I was attempting to parody the line that you see around here every time a
vulernable C program is shown, that rust is magic pixie dust that removes all
security issues. (This is exactly the type of bug that memory safe languages
are still vulnerable to.)

(One could argue setuid is the real problem though.)

~~~
pjmlp
Yes safe systems programming languages are still open to logic errors, but
getting rid of the memory corruption bugs that C spread into the world thanks
to the adoption of UNIX is already an improvement over the daily CVE entries.

Had AT&T charged UNIX at the same prices as the other OSes, instead of giving
it for free to universities and I hardly believe C would matter.

~~~
qwertyuiop924
It's likely C, or something like it, would still matter. C, despite, and
because, of its faults, fills an important niche: It's at a higher level of
abstraction than assembler, but still provides the control you need for that
very low-level code that you need to write things like operating systems. Yes,
it isn't as safe as the Wirthian languages that you yourself prefer, and I
will say that it certainly isn't ideal for most of the purposes it's put to,
but if you want a high-level assembly language, than it works well, because
that's pretty much what it is.

~~~
pjmlp
C has stopped being a high level Assembly for quite a while now.

Most of the features people associate with C are compiler specific language
extensions that any compiler writer can bother to add to his favourite
language and not part of ANSI C.

Inline Assembly, CPU intrisics, SIMD, calling conventions, out of order
execution, hardware transaction memory, cache line control, GPU execution,
attaching functions to interrupt handlers and so on.

Also the pile of UB patterns that C compilers have accumulated throughout the
years, and the differences between compilers, makes it actually easier to
reason about code at the Assembly level than looking at C code.

The only thing has going for it, us that thanks to the economics of free, we
now have UNIX flavours everywhere.

So we can only get rid of C when UNIX is not an option, and that is almost
impossible.

Only now almost 30 years later has C++ surpassed C in many use cases, but it
still carries C with it.

C could have been made so much safer with a few tiny changes, like having
optional bounds checking and not decaying arrays into pointers.

~~~
qwertyuiop924
>C has stopped being a high level Assembly for quite a while now.

...No, not especially. It may not expose CPU-specific functionality in the
spec, which is why it is somewhat portable, but it still is effectively good
at the same things asm is good at. That's why C is sometimes dubbed "portable
assembly"

>Also the pile of UB patterns that C compilers have accumulated throughout the
years, and the differences between compilers, makes it actually easier to
reason about code at the Assembly level than looking at C code.

Well, yeah. Asm is rigorously defined. It's perhaps the only language that has
absolutely zero UB in practice (many languages CLAIM to have no UB, but if
you're on multiple architectures, it's almost unavoidable, although few have
as much as C): because it's totally unportable, it doesn't have to lean on UB
the way C does for optimization.

>Only now almost 30 years later has C++ surpassed C in many use cases, but it
still carries C with it.

Yeah, but C++ has... other problems.

>C could have been made so much safer with a few tiny changes, like having
optional bounds checking and not decaying arrays into pointers.

Now that's something I wish happened.

