cryptfs: kill processes more quickly in wait_and_unmount()

In wait_and_unmount(), kill the processes with open files after umount()
has been failing for 2 seconds rather than 17 seconds.  This avoids a
long boot delay on devices that use FDE.

Detailed explanation:

On FDE devices, vold needs to unmount the tmpfs /data in order to mount
the real, decrypted /data.  On first boot, it also needs to unmount the
unencrypted /data in order to encrypt it in-place.

/data can't be unmounted if files are open inside it.  In theory, init
is responsible for killing all processes with open files in /data, via
the property trigger "vold.decrypt=trigger_shutdown_framework".

However, years ago, commit 6e8440fd50 ("cryptfs: kill processes with
open files on tmpfs /data") added a fallback where vold kills the
processes itself.  Since then, in practice people have increasingly been
relying on this fallback, as services keep being added that use /data
but don't get stopped by trigger_shutdown_framework.

This is slowing down boot, as vold sleeps for 17 seconds before it
actually kills the processes.

The problematic services include services that are now started
explicitly in the post-fs-data trigger rather than implicitly as part of
a class (e.g., tombstoned), as well as services that now need to be
started as part of one of the early-boot classes like core or early_hal
but can still open files in /data later (e.g. keystore2 and credstore).

Another complication is that on default-encrypted devices (devices with
no PIN/pattern/password), trigger_shutdown_framework isn't run at all,
but rather it's expected that the relevant services simply weren't
started yet.  This means that we can't fix the problem just by fixing
trigger_shutdown_framework to kill all the needed processes.

Therefore, given that the vold fallback is being relied on in practice,
and FDE won't be supported much longer anyway (so simple fixes are very
much preferable here), let's just change wait_and_unmount() in vold to
use more appropriate timeouts.  Instead of waiting for 17 seconds before
killing processes, just wait for 2 seconds.  Keep the total timeout of
20 seconds, but spend most of it retrying killing the processes, and
only if the unmount is still failing.

This avoids the long boot delays in practice.

Bug: 187231646
Bug: 186165644
Test: Tested FDE on Cuttlefish, and checked logcat to verify that the
      boot delay is gone.
Change-Id: Id06a9615a87988c8336396c49ee914b35f8d585b
This commit is contained in:
Eric Biggers 2021-05-10 17:44:34 -07:00
parent ef439c5367
commit b4faeb8d44

View file

@ -256,8 +256,6 @@ struct crypt_persist_data {
struct crypt_persist_entry persist_entry[0];
};
static int wait_and_unmount(const char* mountpoint, bool kill);
typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey,
void* params);
@ -1515,7 +1513,7 @@ static void ensure_subdirectory_unmounted(const char *prefix) {
}
}
static int wait_and_unmount(const char* mountpoint, bool kill) {
static int wait_and_unmount(const char* mountpoint) {
int i, err, rc;
// Subdirectory mount will cause a failure of umount.
@ -1537,15 +1535,19 @@ static int wait_and_unmount(const char* mountpoint, bool kill) {
err = errno;
/* If allowed, be increasingly aggressive before the last two retries */
if (kill) {
if (i == (WAIT_UNMOUNT_COUNT - 3)) {
SLOGW("sending SIGHUP to processes with open files\n");
android::vold::KillProcessesWithOpenFiles(mountpoint, SIGTERM);
} else if (i == (WAIT_UNMOUNT_COUNT - 2)) {
SLOGW("sending SIGKILL to processes with open files\n");
android::vold::KillProcessesWithOpenFiles(mountpoint, SIGKILL);
}
// If it's taking too long, kill the processes with open files.
//
// Originally this logic was only a fail-safe, but now it's relied on to
// kill certain processes that aren't stopped by init because they
// aren't in the main or late_start classes. So to avoid waiting for
// too long, we now are fairly aggressive in starting to kill processes.
static_assert(WAIT_UNMOUNT_COUNT >= 4);
if (i == 2) {
SLOGW("sending SIGTERM to processes with open files\n");
android::vold::KillProcessesWithOpenFiles(mountpoint, SIGTERM);
} else if (i >= 3) {
SLOGW("sending SIGKILL to processes with open files\n");
android::vold::KillProcessesWithOpenFiles(mountpoint, SIGKILL);
}
sleep(1);
@ -1679,7 +1681,7 @@ static int cryptfs_restart_internal(int restart_main) {
return -1;
}
if (!(rc = wait_and_unmount(DATA_MNT_POINT, true))) {
if (!(rc = wait_and_unmount(DATA_MNT_POINT))) {
/* If ro.crypto.readonly is set to 1, mount the decrypted
* filesystem readonly. This is used when /data is mounted by
* recovery mode.
@ -2238,7 +2240,7 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) {
* /data, set a property saying we're doing inplace encryption,
* and restart the framework.
*/
wait_and_unmount(DATA_MNT_POINT, true);
wait_and_unmount(DATA_MNT_POINT);
if (fs_mgr_do_tmpfs_mount(DATA_MNT_POINT)) {
goto error_shutting_down;
}