-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Description
Its code is:
let path = CString::new(p.as_os_str().as_bytes())?;
let buf;
unsafe {
let r = libc::realpath(path.as_ptr(), ptr::null_mut());
if r.is_null() {
return Err(io::Error::last_os_error())
}
buf = CStr::from_ptr(r).to_bytes().to_vec();
libc::free(r as *mut _);
}
Ok(PathBuf::from(OsString::from_vec(buf)))
}
The problem comes from the pair of calls libc::realpath
, libc::free
. Under normal conditions, libc::realpath
calls malloc
, libc::free
corresponds to free
and everyone lives happily every after.
Now, funky things are funky. Say you have a binary that makes malloc
and free
be jemalloc. From rust, libc::malloc
and libc::free
then point to jemalloc. But here's the catch: libc's malloc
is not necessarily jemalloc's. That is, while on e.g. Linux, when you have such a setup, libc will happily use jemalloc's malloc
/free
for its allocations (as long as it's not built with -Bsymbolic or -Bsymbolic-functions), that's not the case on Android: IIRC, libc is linked in such a way that all its "internal" function calls stay internal (that is, it's built with -Bsymbolic). So when realpath
calls malloc
, it calls libc's not jemalloc's. And then canonicalize
calls libc::free
with the resulting pointer, which is jemalloc's. Kaboom.
Yes, there is __malloc_hook
and friends, which would solve the problem if they existed on Android. (Well, they do now, but they don't on Android versions < 9 (and apparently 7 for IoT if I'm to believe tags in the bionic repository).
https://bugzilla.mozilla.org/show_bug.cgi?id=1531887 is a real issue that results from this, although it doesn't involve -Bsymbolic
, but some subtle dynamic linking setup where rust code is in libxul.so, which depends on libmozglue.so, which provides malloc
/free
as jemalloc's, for use by libxul, but all native Android code still ends up using libc's malloc
/free
.
For this particular case, it's possible to pass an appropriately-sized buffer to realpath instead of null
, which makes it allocate on its own (https://pubs.opengroup.org/onlinepubs/009696799/functions/realpath.html says PATH_MAX ; the only system I know that doesn't use those limits is Hurd). For the more general case, it almost feels like there should be an overridable GlobalAlloc
for the non-rust system allocator.
Cc: @SimonSapin