diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp index a79e3e13a..eb647047c 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp @@ -599,8 +599,13 @@ bool UserSnapshotServer::WaitForSocket() { return false; } - // We must re-initialize property service access, since we launched before - // second-stage init. + // This initialization of system property is important. When daemon is + // launched post selinux transition (before init second stage), + // bionic libc initializes system property as part of __libc_init_common(); + // however that initialization fails silently given that fact that we don't + // have /dev/__properties__ setup which is created at init second stage. + // + // At this point, we have the handlers setup and is safe to setup property. __system_properties_init(); if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) { diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp index e11510ee8..5deaf3156 100644 --- a/init/snapuserd_transition.cpp +++ b/init/snapuserd_transition.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -247,6 +248,56 @@ void SnapuserdSelinuxHelper::FinishTransition() { } } +/* + * Before starting init second stage, we will wait + * for snapuserd daemon to be up and running; bionic libc + * may read /system/etc/selinux/plat_property_contexts file + * before invoking main() function. This will happen if + * init initializes property during second stage. Any access + * to /system without snapuserd daemon will lead to a deadlock. + * + * Thus, we do a simple probe by reading system partition. This + * read will eventually be serviced by daemon confirming that + * daemon is up and running. Furthermore, we are still in the kernel + * domain and sepolicy has not been enforced yet. Thus, access + * to these device mapper block devices are ok even though + * we may see audit logs. + */ +bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() { + std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix(); + android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT)); + if (fd < 0) { + PLOG(ERROR) << "open " << dev << " failed"; + return false; + } + + void* addr; + ssize_t page_size = getpagesize(); + if (posix_memalign(&addr, page_size, page_size) < 0) { + PLOG(ERROR) << "posix_memalign with page size " << page_size; + return false; + } + + std::unique_ptr buffer(addr, ::free); + + int iter = 0; + while (iter < 10) { + ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0)); + if (n < 0) { + // Wait for sometime before retry + std::this_thread::sleep_for(100ms); + } else if (n == page_size) { + return true; + } else { + LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size; + } + + iter += 1; + } + + return false; +} + void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { auto fd = GetRamdiskSnapuserdFd(); if (!fd) { @@ -268,6 +319,13 @@ void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() { setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); LOG(INFO) << "Relaunched snapuserd with pid: " << pid; + + if (!TestSnapuserdIsReady()) { + PLOG(FATAL) << "snapuserd daemon failed to launch"; + } else { + LOG(INFO) << "snapuserd daemon is up and running"; + } + return; } diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h index be22afd20..557d10587 100644 --- a/init/snapuserd_transition.h +++ b/init/snapuserd_transition.h @@ -56,6 +56,7 @@ class SnapuserdSelinuxHelper final { private: void RelaunchFirstStageSnapuserd(); void ExecSnapuserd(); + bool TestSnapuserdIsReady(); std::unique_ptr sm_; BlockDevInitializer block_dev_init_;