Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ object VectorDaemon {
*/
fun softReboot() {
Log.w(TAG, "Soft reboot: restarting the primary zygote")
Dex2OatServer.onSoftReboot()
forgetDeliberateBoot()
SystemProperties.set("ctl.restart", "zygote")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package org.matrix.vector.daemon.env

import android.net.LocalServerSocket
import android.net.LocalSocket
import android.net.LocalSocketAddress
import android.os.Build
import android.os.FileObserver
import android.os.SELinux
import android.os.SystemClock
import android.os.SystemProperties
import android.system.ErrnoException
import android.system.Os
import android.system.OsConstants
Expand All @@ -25,6 +29,7 @@ val DEX2OAT_MOUNT_FAILED = IManagerService.DEX2OAT_MOUNT_FAILED
val DEX2OAT_SEPOLICY_INCORRECT = IManagerService.DEX2OAT_SEPOLICY_INCORRECT
val DEX2OAT_SELINUX_PERMISSIVE = IManagerService.DEX2OAT_SELINUX_PERMISSIVE
val DEX2OAT_CRASHED = IManagerService.DEX2OAT_CRASHED
val DEX2OAT_INJECTED = IManagerService.DEX2OAT_INJECTED

object Dex2OatServer {
private const val WRAPPER32 = "bin/dex2oat32"
Expand Down Expand Up @@ -85,6 +90,7 @@ object Dex2OatServer {

private fun onSelinuxEvent() {
synchronized(this) {
if (compatibility == DEX2OAT_INJECTED) return
if (compatibility == DEX2OAT_CRASHED) {
SelinuxObserver.stopWatching()
return
Expand Down Expand Up @@ -209,7 +215,88 @@ object Dex2OatServer {
doMountNative(enabled, dex2oatArray[0], dex2oatArray[1], dex2oatArray[2], dex2oatArray[3])
}

private fun injectionTargetName(): String {
val useArtService = SystemProperties.get("dalvik.vm.useartservice")
return if (Build.VERSION.SDK_INT >= 35 ||
(Build.VERSION.SDK_INT == 34 && (useArtService == "true" || useArtService == "1")))
"artd"
else "installd"
}

private fun processExists(name: String): Boolean {
val entries = File("/proc").list() ?: return false
for (entry in entries) {
if (entry.isEmpty() || !entry[0].isDigit()) continue
val command =
runCatching {
File("/proc/$entry/cmdline").readBytes().toString(Charsets.UTF_8)
}
.getOrNull() ?: continue
val executable = command.substringBefore('\u0000')
if (executable.isNotEmpty() && File(executable).name == name) return true
}
return false
}

private fun probeMonitor(name: String): Boolean {
val path = "/data/adb/lspd/dex2oat_monitor_$name"
val deadline = SystemClock.uptimeMillis() + 1000L
do {
var socket: LocalSocket? = null
try {
socket = LocalSocket()
socket.connect(LocalSocketAddress(path, LocalSocketAddress.Namespace.FILESYSTEM))
runCatching { socket.setSoTimeout(250) }
val status = socket.inputStream.read()
if (status == 1) return true
if (status >= 0) return false
} catch (_: Throwable) {
} finally {
runCatching { socket?.close() }
}
try {
Thread.sleep(25L)
} catch (_: InterruptedException) {
return false
}
} while (SystemClock.uptimeMillis() < deadline)
return false
}

private fun probeInjection(): Boolean {
val preferred = injectionTargetName()
if (processExists(preferred)) return probeMonitor(preferred)
val fallback = if (preferred == "artd") "installd" else "artd"
if (processExists(fallback)) return probeMonitor(fallback)
return probeMonitor(preferred) || probeMonitor(fallback)
}

fun onSoftReboot() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return
if (probeInjection()) {
if (!notMounted()) doMount(false)
if (notMounted()) {
compatibility = DEX2OAT_INJECTED
return
}
}
if (compatibility == DEX2OAT_INJECTED) return
doMount(false)
doMount(true)
compatibility = if (notMounted()) DEX2OAT_MOUNT_FAILED else DEX2OAT_OK
}

fun start() {
if (probeInjection()) {
if (!notMounted()) doMount(false)
if (notMounted()) {
compatibility = DEX2OAT_INJECTED
Log.i(TAG, "dex2oat injection is active; wrapper is not mounted")
VectorDaemon.scope.launch { runSocketLoop() }
return
}
}

if (notMounted()) {
doMount(true)
if (notMounted()) {
Expand Down
7 changes: 2 additions & 5 deletions dex2oat/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ cmake_minimum_required(VERSION 3.10)
project(dex2oat)

add_executable(dex2oat dex2oat.cpp)
add_library(oat_hook SHARED oat_hook.cpp)

OPTION(LSPLT_BUILD_SHARED OFF)
add_subdirectory(${VECTOR_ROOT}/external/lsplt/lsplt/src/main/jni external)
add_library(oat_hook SHARED compiler_options.cpp)

target_include_directories(oat_hook PUBLIC include)
target_include_directories(dex2oat PUBLIC include)
target_link_libraries(dex2oat log)
target_link_libraries(oat_hook log lsplt_static)
target_link_libraries(oat_hook log)

if (DEFINED DEBUG_SYMBOLS_PATH)
message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}")
Expand Down
26 changes: 26 additions & 0 deletions dex2oat/src/main/cpp/compiler_options.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <dlfcn.h>
#include <unistd.h>

#include <cstddef>
#include <cstdint>
#include <cstdlib>

extern "C" __attribute__((visibility("default"))) void _ZN3art15CompilerOptionsC1Ev(void *self) {
using Constructor = void (*)(void *);
static Constructor original =
reinterpret_cast<Constructor>(dlsym(RTLD_NEXT, "_ZN3art15CompilerOptionsC1Ev"));
if (original == nullptr) return;

original(self);

constexpr size_t kMaxSlots = 10;
for (size_t i = 0; i < kMaxSlots; i++) {
auto *slot = reinterpret_cast<uintptr_t *>(static_cast<char *>(self) + i * sizeof(uintptr_t));
if (*slot == static_cast<uintptr_t>(-1)) {
*slot = 0;
break;
}
}

unsetenv("LD_PRELOAD");
}
Loading