Skip to content
Closed
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
13 changes: 13 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ val hasUpdateSigning =
updateSigningKeyAlias,
updateSigningKeyPassword,
).all { !it.isNullOrBlank() }
val x11GuestProbeFault =
providers.gradleProperty("udroidX11GuestProbeFault").orNull ?: "none"
require(x11GuestProbeFault in setOf("none", "deny")) {
"udroidX11GuestProbeFault must be none or deny"
}

kotlin {
compilerOptions {
Expand Down Expand Up @@ -48,6 +53,7 @@ android {
"UPDATE_RELEASES_API",
"\"https://api.github.com/repos/RandomCoderOrg/udroid-app/releases?per_page=20\"",
)
buildConfigField("String", "X11_GUEST_PROBE_FAULT", "\"none\"")

ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64")
Expand All @@ -66,6 +72,13 @@ android {
}

buildTypes {
debug {
buildConfigField(
"String",
"X11_GUEST_PROBE_FAULT",
"\"$x11GuestProbeFault\"",
)
}
release {
isMinifyEnabled = true
isShrinkResources = true
Expand Down
Binary file modified app/src/main/assets/runtime/arm64-v8a/runtime_probe
Binary file not shown.
Binary file modified app/src/main/assets/runtime/armeabi-v7a/runtime_probe
Binary file not shown.
Binary file modified app/src/main/assets/runtime/x86_64/runtime_probe
Binary file not shown.
129 changes: 129 additions & 0 deletions app/src/main/cpp/runtime_probe.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

Expand All @@ -23,7 +27,132 @@ static int64_t monotonic_ms(void) {
return ((int64_t)ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
}

static int read_exactly(int fd, uint8_t *buffer, size_t count) {
size_t offset = 0;
while (offset < count) {
ssize_t received = read(fd, buffer + offset, count - offset);
if (received < 0 && errno == EINTR) {
continue;
}
if (received <= 0) {
return -1;
}
offset += (size_t)received;
}
return 0;
}

static int probe_x11_socket(const char *path, int force_denied,
int abstract_socket, const char *socket_namespace) {
static const uint8_t setup_request[] = {
0x6c, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
uint8_t setup_header[8];
struct sockaddr_un address;
socklen_t address_size;
struct timeval timeout = {.tv_sec = 1, .tv_usec = 0};
const int64_t started_ms = monotonic_ms();

if (force_denied) {
printf("{\"event\":\"x11_guest_probe\",\"status\":\"connect_failed\","
"\"socket_namespace\":\"%s\",\"errno\":%d,"
"\"address_bytes\":0,\"elapsed_ms\":0,"
"\"detail\":\"Permission denied (injected)\"}\n",
socket_namespace, EACCES);
return 20;
}
if (strlen(path) >= sizeof(address.sun_path)) {
printf("{\"event\":\"x11_guest_probe\",\"status\":\"invalid_path\","
"\"socket_namespace\":\"%s\",\"errno\":%d,"
"\"address_bytes\":0,\"elapsed_ms\":%lld,"
"\"detail\":\"Socket path is too long\"}\n",
socket_namespace, ENAMETOOLONG,
(long long)(monotonic_ms() - started_ms));
return 21;
}

int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
printf("{\"event\":\"x11_guest_probe\",\"status\":\"socket_failed\","
"\"socket_namespace\":\"%s\",\"errno\":%d,"
"\"address_bytes\":0,\"elapsed_ms\":%lld,\"detail\":\"%s\"}\n",
socket_namespace, errno,
(long long)(monotonic_ms() - started_ms), strerror(errno));
return 22;
}
(void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
(void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));

memset(&address, 0, sizeof(address));
address.sun_family = AF_UNIX;
if (abstract_socket) {
size_t path_length = strlen(path);
address.sun_path[0] = '\0';
memcpy(address.sun_path + 1, path, path_length);
address_size = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + 1 + path_length);
} else {
memcpy(address.sun_path, path, strlen(path) + 1);
/* libxcb 1.14 passes the complete structure for filesystem sockets. */
address_size = sizeof(address);
}
if (connect(fd, (struct sockaddr *)&address, address_size) != 0) {
int saved_errno = errno;
printf("{\"event\":\"x11_guest_probe\",\"status\":\"connect_failed\","
"\"socket_namespace\":\"%s\",\"errno\":%d,"
"\"address_bytes\":%u,\"elapsed_ms\":%lld,\"detail\":\"%s\"}\n",
socket_namespace, saved_errno, (unsigned int)address_size,
(long long)(monotonic_ms() - started_ms), strerror(saved_errno));
close(fd);
return 23;
}
if (write(fd, setup_request, sizeof(setup_request)) != (ssize_t)sizeof(setup_request) ||
read_exactly(fd, setup_header, sizeof(setup_header)) != 0) {
int saved_errno = errno;
printf("{\"event\":\"x11_guest_probe\",\"status\":\"handshake_failed\","
"\"socket_namespace\":\"%s\",\"errno\":%d,"
"\"address_bytes\":%u,\"elapsed_ms\":%lld,\"detail\":\"%s\"}\n",
socket_namespace, saved_errno, (unsigned int)address_size,
(long long)(monotonic_ms() - started_ms),
saved_errno == 0 ? "Connection closed" : strerror(saved_errno));
close(fd);
return 24;
}
close(fd);

unsigned int protocol_major =
(unsigned int)setup_header[2] | ((unsigned int)setup_header[3] << 8U);
unsigned int protocol_minor =
(unsigned int)setup_header[4] | ((unsigned int)setup_header[5] << 8U);
if (setup_header[0] != 1) {
printf("{\"event\":\"x11_guest_probe\",\"status\":\"rejected\","
"\"socket_namespace\":\"%s\",\"setup_status\":%u,"
"\"protocol_major\":%u,\"protocol_minor\":%u,"
"\"address_bytes\":%u,\"elapsed_ms\":%lld}\n",
socket_namespace, (unsigned int)setup_header[0], protocol_major, protocol_minor,
(unsigned int)address_size, (long long)(monotonic_ms() - started_ms));
return 25;
}
printf("{\"event\":\"x11_guest_probe\",\"status\":\"ready\","
"\"socket_namespace\":\"%s\",\"protocol_major\":%u,"
"\"protocol_minor\":%u,\"address_bytes\":%u,\"elapsed_ms\":%lld}\n",
socket_namespace, protocol_major, protocol_minor, (unsigned int)address_size,
(long long)(monotonic_ms() - started_ms));
return 0;
}

int main(int argc, char **argv) {
if (argc >= 3 && strcmp(argv[1], "--x11-abstract") == 0) {
setvbuf(stdout, NULL, _IOLBF, 0);
return probe_x11_socket(argv[2], 0, 1, "abstract");
}
if (argc >= 3 &&
(strcmp(argv[1], "--x11") == 0 || strcmp(argv[1], "--x11-deny") == 0)) {
setvbuf(stdout, NULL, _IOLBF, 0);
return probe_x11_socket(argv[2], strcmp(argv[1], "--x11-deny") == 0,
0, "filesystem");
}

const char *boot_id = argc > 1 ? argv[1] : "unknown";
const pid_t original_parent = getppid();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.io.File
import java.io.FileOutputStream

object NativeProbeInstaller {
private const val PROBE_VERSION = "1"
private const val PROBE_VERSION = "4"
private val supportedAbis = setOf("arm64-v8a", "armeabi-v7a", "x86_64")

fun install(context: Context): File {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import org.randomcoder.udroid.audio.AudioEndpoint
import org.randomcoder.udroid.install.ProotRuntime
import org.randomcoder.udroid.linuxapps.LinuxApplication
import org.randomcoder.udroid.x11.X11DisplayEndpoint
import java.io.File

data class ProotApplicationLaunch(
Expand All @@ -17,12 +18,12 @@ object ProotApplicationLaunchBuilder {
context: Context,
runtime: ProotRuntime,
rootfs: File,
x11SocketDirectory: File,
x11Endpoint: X11DisplayEndpoint,
application: LinuxApplication,
audioEndpoint: AudioEndpoint? = null,
): ProotApplicationLaunch {
require(application.executable.isNotBlank()) { "Application executable is empty" }
require(x11SocketDirectory.isDirectory) { "The X11 socket directory is unavailable" }
require(x11Endpoint.socketDirectory.isDirectory) { "The X11 socket directory is unavailable" }
val guestHome = if (File(rootfs, "root").isDirectory) "/root" else "/"
val guestWorkingDirectory =
application.workingDirectory
Expand All @@ -32,7 +33,8 @@ object ProotApplicationLaunchBuilder {
buildArguments(
prootPath = runtime.executable.absolutePath,
rootfsPath = ProotPathContract.rootfsPath(context, rootfs),
x11SocketDirectory = x11SocketDirectory.absolutePath,
x11SocketDirectory = x11Endpoint.socketDirectory.absolutePath,
bindX11Socket = x11Endpoint.requiresGuestBind,
guestHome = guestHome,
guestWorkingDirectory = guestWorkingDirectory,
applicationArguments =
Expand Down Expand Up @@ -69,6 +71,7 @@ object ProotApplicationLaunchBuilder {
prootPath: String,
rootfsPath: String,
x11SocketDirectory: String,
bindX11Socket: Boolean = true,
guestHome: String,
guestWorkingDirectory: String,
applicationArguments: List<String>,
Expand All @@ -82,8 +85,10 @@ object ProotApplicationLaunchBuilder {
add("--root-id")
add("--rootfs=$rootfsPath")
addAndroidProotBindMounts()
add("-b")
add("$x11SocketDirectory:/tmp/.X11-unix")
if (bindX11Socket) {
add("-b")
add("$x11SocketDirectory:/tmp/.X11-unix")
}
if (audioAuthDirectory != null) {
add("-b")
add("$audioAuthDirectory:${AudioEndpoint.GUEST_AUTH_DIRECTORY}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,30 @@ import android.content.Context
import org.randomcoder.udroid.audio.AudioEndpoint
import org.randomcoder.udroid.install.ProotRuntime
import org.randomcoder.udroid.install.RootfsInstallationPipeline
import org.randomcoder.udroid.x11.X11DisplayEndpoint
import java.io.File

object ProotDesktopLaunchBuilder {
fun create(
context: Context,
runtime: ProotRuntime,
rootfs: File,
x11SocketDirectory: File,
x11Endpoint: X11DisplayEndpoint,
environment: DesktopEnvironment,
configuration: DesktopConfiguration,
audioEndpoint: AudioEndpoint? = null,
): ProotApplicationLaunch {
require(File(rootfs, RootfsInstallationPipeline.READY_MARKER).isFile) {
"The selected Linux image is not ready"
}
require(x11SocketDirectory.isDirectory) { "The X11 socket directory is unavailable" }
require(x11Endpoint.socketDirectory.isDirectory) { "The X11 socket directory is unavailable" }
val guestHome = if (File(rootfs, "root").isDirectory) "/root" else "/"
val arguments =
buildArguments(
prootPath = runtime.executable.absolutePath,
rootfsPath = ProotPathContract.rootfsPath(context, rootfs),
x11SocketDirectory = x11SocketDirectory.absolutePath,
x11SocketDirectory = x11Endpoint.socketDirectory.absolutePath,
bindX11Socket = x11Endpoint.requiresGuestBind,
guestHome = guestHome,
environment = environment,
configuration = configuration,
Expand Down Expand Up @@ -64,6 +66,7 @@ object ProotDesktopLaunchBuilder {
prootPath: String,
rootfsPath: String,
x11SocketDirectory: String,
bindX11Socket: Boolean = true,
guestHome: String,
environment: DesktopEnvironment,
configuration: DesktopConfiguration,
Expand All @@ -77,8 +80,10 @@ object ProotDesktopLaunchBuilder {
add("--root-id")
add("--rootfs=$rootfsPath")
addAndroidProotBindMounts()
add("-b")
add("$x11SocketDirectory:/tmp/.X11-unix")
if (bindX11Socket) {
add("-b")
add("$x11SocketDirectory:/tmp/.X11-unix")
}
if (audioAuthDirectory != null) {
add("-b")
add("$audioAuthDirectory:${AudioEndpoint.GUEST_AUTH_DIRECTORY}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import org.randomcoder.udroid.audio.AudioEndpoint
import org.randomcoder.udroid.install.ProotRuntime
import org.randomcoder.udroid.install.RootfsInstallationPipeline
import org.randomcoder.udroid.x11.X11DisplayEndpoint
import java.io.File
import java.nio.file.Files

Expand All @@ -27,7 +28,7 @@ object ProotTerminalLaunchBuilder {
context: Context,
runtime: ProotRuntime,
rootfs: File = InstalledRootfsResolver.resolve(context),
x11SocketDirectory: File? = null,
x11Endpoint: X11DisplayEndpoint? = null,
audioEndpoint: AudioEndpoint? = null,
): ProotTerminalLaunch {
require(File(rootfs, RootfsInstallationPipeline.READY_MARKER).isFile) {
Expand Down Expand Up @@ -57,7 +58,8 @@ object ProotTerminalLaunchBuilder {
rootfsPath = ProotPathContract.rootfsPath(context, rootfs),
guestHome = guestHome,
guestShell = guestShell,
x11SocketDirectory = x11SocketDirectory?.absolutePath,
x11SocketDirectory = x11Endpoint?.socketDirectory?.absolutePath,
bindX11Socket = x11Endpoint?.requiresGuestBind == true,
audioAuthDirectory = audioEndpoint?.hostAuthDirectory?.absolutePath,
)
val environment =
Expand All @@ -83,6 +85,7 @@ object ProotTerminalLaunchBuilder {
guestHome: String,
guestShell: String,
x11SocketDirectory: String? = null,
bindX11Socket: Boolean = x11SocketDirectory != null,
audioAuthDirectory: String? = null,
): Array<String> =
buildList {
Expand All @@ -94,7 +97,7 @@ object ProotTerminalLaunchBuilder {
add("--root-id")
add("--rootfs=$rootfsPath")
addAndroidProotBindMounts()
if (x11SocketDirectory != null) {
if (x11SocketDirectory != null && bindX11Socket) {
add("-b")
add("$x11SocketDirectory:/tmp/.X11-unix")
}
Expand Down
Loading
Loading