-
Notifications
You must be signed in to change notification settings - Fork 242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add get_current_dir_name from musl to fix building on Bionic libc #580
Conversation
get_current_dir_name is a GNU function, and is not supported on all C libraries. Bionic is the main one here, as musl provides the get_current_dir_name we're using here. We mostly copy-paste the musl implementation in order to fix building on the Bionic libc. Bubblewrap will now build and run successfully on Android. Note that Android currently prohibits user namespaces and suid programs, so this needs root access to function. Signed-off-by: Dallas Strouse <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a general comment, I'm not a fan of "minimal" "non-bloated" libc implementations that achieve their minimalism by not providing straightforward, useful extension functions like get_current_dir_name()
, which just means the "bloat" gets moved into the implementation and build system of every program that would have called those functions.
Normally I'd be writing C code to depend on GLib, which provides this sort of "make C not awful" glue in case the libc doesn't; but bubblewrap can't depend on GLib, because as a sometimes-setuid-root executable it needs to minimize its attack surface, so we have to deal with this sort of thing.
if (res && *res && !stat(res, &a) && !stat(".", &b) | ||
&& (a.st_dev == b.st_dev) && (a.st_ino == b.st_ino)) | ||
return strdup(res); | ||
return getcwd(0, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This relies on a behaviour of getcwd()
that is documented to be a GNU extension. getcwd(3)
on my (GNU/)Linux system says:
[glibc's implementation of] getcwd() conforms to POSIX.1-2001. Note however that POSIX.1-2001 leaves the behavior of getcwd() unspecified if buf is NULL.
meaning that it's considered perfectly OK under POSIX for getcwd (NULL, 0)
to crash, or even to be a security vulnerability. On GNU systems (and presumably also musl and bionic systems), as an extension, getcwd (NULL, 0)
is documented to allocate a large enough buffer as if via malloc()
, to be freed by the caller - but there is nothing to stop someone from deciding that musl and bionic are "too bloated" and writing a new libc that doesn't implement that GNU extension, in which case bubblewrap's behaviour would become undefined if we used this code on that libc.
Undefined behaviour in bubblewrap is scary, because it's sometimes setuid root, making it security-sensitive.
I see you're using this under #ifdef __BIONIC__
rather than #ifndef HAVE_GET_CURRENT_DIR_NAME
, which means that as long as Bionic documents the GNU-like getcwd(NULL, 0)
extension, it's safe to use. Does it document that behaviour?
I think that's subtle enough to deserve comments. Perhaps above the __BIONIC__
check, something like:
intentionally not using a HAVE_GET_CURRENT_DIR_NAME check, because our implementation relies on an extension that is documented by bionic libc but not guaranteed by POSIX
(except with appropriate line wrapping); and above this call to getcwd(0, 0)
,
Not guaranteed by POSIX, but bionic libc documents that if the first parameter here is null, it will allocate a new buffer as if via malloc()
(assuming that's true of course).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the Bionic implementation: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/getcwd.cpp
I don't know much about the details of these, I'm mainly a Rust gal, and don't touch C often if at all. If needed, I'll add more comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the Bionic implementation: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/getcwd.cpp
I see it implements the GNU-style extension, but does it document the extension (or a general API stability policy) so that it would be considered to be an incompatible change to remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick Google search doesn't turn anything up, I'll search some more and ask some Android devs if they can comment on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading the following files, I think it's safe to assume they won't change getcwd on us, as they have it listed (and i assume supported because of that) on some of their docs. Can't find any specific details on getcwd though. Would the source code be enough to reference?
https://android.googlesource.com/platform/bionic/+/ics-mr1-release/libc/docs/CHANGES.TXT
https://android.googlesource.com/platform/bionic/+/refs/heads/master/docs/status.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't see anything by following those links that documents that their getcwd (NULL, 0)
is intended to have GNU-compatible semantics.
I think this probably means that making bubblewrap build successfully on Android is not actually practically useful? Sorry, I'm not really willing to pay an ongoing maintenance cost for portability to a platform where bubblewrap can't perform its stated purpose. |
The second commit is missing a I would be inclined to close this without merging if the resulting bubblewrap isn't practically useful on Android/Bionic systems, because the cost (we have more code in the tree, which we'll become responsible for, forever) seems like it exceeds the benefit (if you have root, you can ... do things you would already be able to do by just calling C functions). However, perhaps there's a reason why this is practically useful, that I'm not seeing? |
If Android ever decides to enable one or both of the features, it'll work ootb. It's also worth noting that Bionic is not at all Android-only, that's just where it's most commonly used. I want to tinker with Android some, so having this here would be very nice. I'd also like to see Android move away from using different users entirely for each application as it does right now, this could be a step towards that.
I tried 🥹 it'll probably be signed off on in the squashed commit anyways. |
One more add-on: Android does have suid binaries, they're just very limited (iirc only su is suid). Bubblewrap could be shipped on Android alongside su as a suid binary. The current limitation seems to come from the fact that Android disables suid binaries on some filesystem mounts. |
I'd like to note that Bionic is intended for use in systems with memory constraints, so a libc like you mentioned here would be a good fit. Most users would be using it indirectly anyways, since most Android apps are made using Kotlin or Java and don't need to concern themselves with libc. It's mainly just a system library concern. |
@smcv thoughts on the patch applied by Termux? https://github.com/termux/termux-packages/blob/master/root-packages/bubblewrap/swap-getcwd-func.patch |
This has the same problem as what you proposed here: instead of relying on a non-standardized function with known behaviour (GNU If If If there is documentation somewhere that says Bionic (or other C libraries) won't remove this extension in a future release, then the best implementation might be something like this:
Or if the getcwd extension isn't documented as something we can rely on, then the only portable thing to do is to allocate our own buffer of some reasonable size, (In more or less any other codebase I'd just use |
If the Android community wants to have bubblewrap available on Bionic (or, more generally, if users of other non-GNU libc implementations want to have bubblewrap available on their platforms), I think the best route would probably be to have a library that provides useful GNU extensions like Analogous: libbsd provides a compatibility layer allowing software designed for *BSD to be compiled and run on GNU systems, by implementing missing BSD functions. Android could have an analogous library that allows software designed for GNU to be compiled and run on Android systems, if the Android community wants to provide one. That would also set expectations for security support: if there was an exploitable security vulnerability resulting from a function in the GNU-compatibility shim library either containing vulnerable code or not being fully compatible with glibc, then it would be unambiguously a bug in that library, and not a bug in bubblewrap. The other option here would be (as I said in a previous comment) to allocate our own buffer of some reasonable size, I am not happy with making bubblewrap depend on undocumented behaviour of non-GNU C libraries (in this case, the fact that Bionic |
get_current_dir_name is a GNU function, and is not supported on all C libraries. Bionic is the main one here, as musl provides the get_current_dir_name we're using here.
We mostly copy-paste the musl implementation in order to fix building on the Bionic libc. Bubblewrap will now build and run successfully on Android.
Note that Android currently prohibits user namespaces and suid programs, so this needs root access to function.
Closes #579