android: only load Vulkan drivers picked in the menu

video_vulkan_driver_path is an ordinary config key, and the config is
chosen by whoever launches RetroArch (the CONFIGFILE extra), so a config
could name a driver zip that RetroArch would extract and run.

A pick in the menu now records the zip's path and version in a trust
file in app-private storage. Context init uses the system driver for
any other driver and asks the user to pick it again. When no trust file
exists yet, drivers already extracted are trusted, so current users do
not have to pick them again.
This commit is contained in:
davey authored and libretroadmin committed 2026-11-07 03:06:17 +01:00
1 parent d7de9cc7b6
commit 2fc159a4e9
6 files changed
+279 -12

No files matched your search

+32 -1
View File
@@ -398,6 +398,31 @@ size_t vulkan_custom_driver_crash_clear(char *state, size_t len,
return len;
}
static void trust_hex(char *out, uint32_t key)
{
static const char digits[] = "0123456789abcdef";
int i;
for (i = 7; i >= 0; i--, key >>= 4)
out[i] = digits[key & 15];
out[8] = '\0';
}
bool vulkan_custom_driver_trust_find(const char *trust, size_t len,
const char *path, uint32_t key)
{
char hex[9];
trust_hex(hex, key);
return crash_has(trust, len, path, strlen(path), hex, 8);
}
size_t vulkan_custom_driver_trust_add(char *trust, size_t len, size_t cap,
const char *path, uint32_t key)
{
char hex[9];
trust_hex(hex, key);
return crash_put(trust, len, cap, path, strlen(path), hex, 8);
}
bool vulkan_custom_driver_session_init(vulkan_custom_driver_session_t *s,
vulkan_custom_driver_load_t load, void *load_data,
const char *state, size_t state_len,
@@ -481,6 +506,11 @@ static bool session_wants(vulkan_custom_driver_session_t *s,
*msg = session_once(p, VULKAN_CUSTOM_DRIVER_MSG_INVALID);
return false;
}
if (!req->trusted)
{
*msg = session_once(p, VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED);
return false;
}
return true;
}
@@ -600,6 +630,7 @@ enum vulkan_custom_driver_msg vulkan_custom_driver_session_picked(
if (p->failed)
return VULKAN_CUSTOM_DRIVER_MSG_RESTART;
p->invalid = false;
p->shown &= ~(1u << VULKAN_CUSTOM_DRIVER_MSG_INVALID);
p->shown &= ~((1u << VULKAN_CUSTOM_DRIVER_MSG_INVALID)
| (1u << VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED));
return VULKAN_CUSTOM_DRIVER_MSG_NONE;
}
+12 -2
View File
@@ -56,7 +56,8 @@ enum vulkan_custom_driver_msg
VULKAN_CUSTOM_DRIVER_MSG_CRASHED,
VULKAN_CUSTOM_DRIVER_MSG_INVALID,
VULKAN_CUSTOM_DRIVER_MSG_UNSUPPORTED,
VULKAN_CUSTOM_DRIVER_MSG_RESTART
VULKAN_CUSTOM_DRIVER_MSG_RESTART,
VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED
};
/* Crash state: one "<core>\t<zip path>\n" line per driver that took the
@@ -71,6 +72,14 @@ size_t vulkan_custom_driver_crash_add(char *state, size_t len, size_t cap,
size_t vulkan_custom_driver_crash_clear(char *state, size_t len,
const char *path);
/* Trust list: one "<key>\t<zip path>\n" line per driver version picked
* in the menu, the key as "%08x". add returns the new length; the oldest
* lines go when cap is reached. */
bool vulkan_custom_driver_trust_find(const char *trust, size_t len,
const char *path, uint32_t key);
size_t vulkan_custom_driver_trust_add(char *trust, size_t len, size_t cap,
const char *path, uint32_t key);
/* Loads the driver at path into the extraction dir named by key. Returns
* its loader with *meta filled in, or NULL with *msg set to
* VULKAN_CUSTOM_DRIVER_MSG_INVALID (bad package) or _FAILED. */
@@ -86,6 +95,7 @@ struct vulkan_custom_driver_request
const char *core; /* the running core; "" or NULL: the menu */
bool supported; /* Adreno and API >= 28 */
bool safe_mode; /* started with the system driver forced */
bool trusted; /* this version was picked in the menu */
};
struct vulkan_custom_driver_slot
@@ -170,7 +180,7 @@ bool vulkan_custom_driver_current(void); /* this init opened a cu
bool vulkan_custom_driver_mesa(void); /* the driver open() returned is Mesa */
bool vulkan_custom_driver_supported(void); /* Adreno and API >= 28 */
bool vulkan_custom_driver_crashed(const char *path); /* off for the running core after a crash */
void vulkan_custom_driver_picked(const char *path); /* clears path's crash state and INVALID mark */
void vulkan_custom_driver_picked(const char *path); /* trusts path; clears its crash state and INVALID mark */
void vulkan_custom_driver_set_safe_mode(bool safe_mode); /* true: system driver until the process ends */
bool vulkan_custom_driver_safe_mode(void); /* Safe start: system driver for this process */
/* vulkan_custom_driver_linker.c: a private copy of the system Vulkan
+127 -9
View File
@@ -44,6 +44,7 @@ static struct
char base[PATH_MAX_LENGTH]; /* <internalDataPath>/vulkan_drivers/ */
char marker[PATH_MAX_LENGTH];
char state[PATH_MAX_LENGTH];
char trust[PATH_MAX_LENGTH];
char prep[PATH_MAX_LENGTH]; /* the zip a pick's task was pushed for */
const char *core; /* of the open() in progress */
bool mesa; /* of the last driver open() returned */
@@ -78,6 +79,9 @@ static void custom_driver_msg(enum vulkan_custom_driver_msg msg)
case VULKAN_CUSTOM_DRIVER_MSG_RESTART:
id = MSG_VULKAN_CUSTOM_DRIVER_RESTART;
break;
case VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED:
id = MSG_VULKAN_CUSTOM_DRIVER_UNTRUSTED;
break;
default:
return;
}
@@ -115,6 +119,34 @@ static bool custom_driver_save_state(void)
(int64_t)custom_driver_st.session.state_len);
}
/* Adds a picked version to the trust file. Never two writers: the seed
* runs before any pick is queued, and pick tasks run one at a time. */
static void custom_driver_trust(const char *file, const char *path,
uint32_t key)
{
int64_t len = 0;
char *old = custom_driver_read(file, &len);
size_t n = (size_t)len;
size_t added;
char *trust;
/* Rewriting a file it could not read would drop its other picks */
if (!old && path_is_valid(file))
return;
if ((trust = (char*)malloc(VULKAN_CUSTOM_DRIVER_STATE_MAX)))
{
if (n > VULKAN_CUSTOM_DRIVER_STATE_MAX)
n = VULKAN_CUSTOM_DRIVER_STATE_MAX;
if (n)
memcpy(trust, old, n);
added = vulkan_custom_driver_trust_add(trust, n,
VULKAN_CUSTOM_DRIVER_STATE_MAX, path, key);
if (added != n)
filestream_write_file_atomic(file, trust, (int64_t)added);
free(trust);
}
free(old);
}
/* Written before driver code runs, deleted once it is up or failed. */
static void custom_driver_mark(const char *path, const char *core)
{
@@ -223,6 +255,48 @@ static bool custom_driver_extracted(struct custom_driver_work *w,
return ok;
}
/* With no trust file yet, trusts each finished extraction whose zip is
* unchanged: those drivers were in use before picks were recorded. */
static void custom_driver_seed(struct custom_driver_work *w)
{
struct vulkan_custom_driver_meta meta;
size_t len = 0;
char *trust = (char*)malloc(VULKAN_CUSTOM_DRIVER_STATE_MAX);
struct RDIR *rdir = retro_opendir(custom_driver_st.base);
if (trust && rdir)
{
while (retro_readdir(rdir))
{
int64_t n = 0;
const char *name = retro_dirent_get_name(rdir);
char *zip;
if ( !retro_dirent_is_dir(rdir, NULL)
|| strlen(name) != 8
|| strspn(name, "0123456789abcdef") != 8)
continue;
fill_pathname_join_special(w->dir, custom_driver_st.base, name,
sizeof(w->dir));
/* current_dir leaves w->file at the source file */
if ( custom_driver_extracted(w, g_android->activity->sdkVersion,
&meta)
&& custom_driver_current_dir(w, name)
&& (zip = custom_driver_read(w->file, &n)))
{
len = vulkan_custom_driver_trust_add(trust, len,
VULKAN_CUSTOM_DRIVER_STATE_MAX, zip,
(uint32_t)strtoul(name, NULL, 16));
free(zip);
}
}
}
if (trust)
filestream_write_file_atomic(custom_driver_st.trust, trust,
(int64_t)len);
if (rdir)
retro_closedir(rdir);
free(trust);
}
/* Extraction matches the member name ignoring case, so only one member
* may match, or it could write one other than the one checked. */
static bool custom_driver_one_member(const struct string_list *members,
@@ -435,7 +509,19 @@ static bool custom_driver_init(void)
"pending", sizeof(custom_driver_st.marker));
fill_pathname_join_special(custom_driver_st.state, custom_driver_st.base,
"state", sizeof(custom_driver_st.state));
fill_pathname_join_special(custom_driver_st.trust, custom_driver_st.base,
"trusted", sizeof(custom_driver_st.trust));
fill_pathname_slash(custom_driver_st.base, sizeof(custom_driver_st.base));
if (!path_is_valid(custom_driver_st.trust))
{
struct custom_driver_work *w = (struct custom_driver_work*)
calloc(1, sizeof(*w));
if (w)
{
custom_driver_seed(w);
free(w);
}
}
state = custom_driver_read(custom_driver_st.state, &state_len);
marker = custom_driver_read(custom_driver_st.marker, &marker_len);
@@ -451,6 +537,27 @@ static bool custom_driver_init(void)
return true;
}
/* Picked in the menu: in the trust file, or its pick's task has not
* recorded it yet. */
static bool custom_driver_trusted(const char *path, int64_t size,
int64_t mtime)
{
int64_t len = 0;
char *trust;
bool trusted;
/* No newline: the trust file could not record it */
if ( retro_atomic_load_acquire_int(&custom_driver_preps) > 0
&& string_is_equal(custom_driver_st.prep, path)
&& !strchr(path, '\n'))
return true;
if (!(trust = custom_driver_read(custom_driver_st.trust, &len)))
return false;
trusted = vulkan_custom_driver_trust_find(trust, (size_t)len, path,
vulkan_custom_driver_key(path, size, mtime));
free(trust);
return trusted;
}
dylib_t vulkan_custom_driver_open(void)
{
struct vulkan_custom_driver_request req;
@@ -478,12 +585,14 @@ dylib_t vulkan_custom_driver_open(void)
req.supported = false;
req.size = -1;
req.mtime = 0;
req.trusted = false;
if (!string_is_empty(req.path) && !req.safe_mode)
{
req.supported = vulkan_custom_driver_supported();
req.size = path_get_size(req.path);
if (path_get_mtime(req.path, &mtime))
req.mtime = mtime;
req.trusted = custom_driver_trusted(req.path, req.size, req.mtime);
}
custom_driver_st.core = req.core;
handle = vulkan_custom_driver_session_open(&custom_driver_st.session,
@@ -494,6 +603,9 @@ dylib_t vulkan_custom_driver_open(void)
if (msg == VULKAN_CUSTOM_DRIVER_MSG_INVALID && req.size < 0)
RARCH_WARN("[Vulkan] Custom driver \"%s\" not used: zip missing or not readable.\n",
req.path);
if (msg == VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED)
RARCH_WARN("[Vulkan] Custom driver \"%s\" not used: not picked in the menu, or changed since.\n",
req.path);
if (custom_driver_st.session.full)
RARCH_LOG("[Vulkan] Custom driver \"%s\" not used: too many drivers tried this session.\n",
req.path);
@@ -574,9 +686,9 @@ bool vulkan_custom_driver_crashed(const char *path)
custom_driver_st.session.state_len, path, custom_driver_core());
}
/* A pick's task: the package checked and extracted ahead of the
* context init that loads it, which then finds it done. Its state is
* all it reads. */
/* A pick's task: trusts the pick, then checks and extracts the package
* ahead of the context init that loads it. Its state and the trust file
* are all it reads. */
struct custom_driver_prep
{
struct custom_driver_work w;
@@ -584,6 +696,7 @@ struct custom_driver_prep
char path[PATH_MAX_LENGTH];
char base[PATH_MAX_LENGTH];
int api_level;
bool extract; /* not in safe mode */
};
static void custom_driver_prep_handler(retro_task_t *task)
@@ -597,13 +710,17 @@ static void custom_driver_prep_handler(retro_task_t *task)
&& path_get_mtime(p->path, &mtime))
{
const char *reason;
snprintf(p->w.other, sizeof(p->w.other), "%08x",
(unsigned)vulkan_custom_driver_key(p->path, size, mtime));
uint32_t key = vulkan_custom_driver_key(p->path, size, mtime);
fill_pathname_join_special(p->w.file, p->base, "trusted",
sizeof(p->w.file));
custom_driver_trust(p->w.file, p->path, key);
snprintf(p->w.other, sizeof(p->w.other), "%08x", (unsigned)key);
fill_pathname_join_special(p->w.dir, p->base, p->w.other,
sizeof(p->w.dir));
strlcpy(p->w.tmp, p->w.dir, sizeof(p->w.tmp));
strlcat(p->w.tmp, ".prep", sizeof(p->w.tmp));
if ( !custom_driver_extracted(&p->w, p->api_level, &p->meta)
if ( p->extract
&& !custom_driver_extracted(&p->w, p->api_level, &p->meta)
&& (reason = custom_driver_extract(&p->w, p->path,
p->api_level, &p->meta)))
RARCH_WARN("[Vulkan] Custom driver \"%s\" not usable: %s.\n",
@@ -618,15 +735,15 @@ static void custom_driver_prep_free(retro_task_t *task)
retro_atomic_fetch_add_int(&custom_driver_preps, -1);
}
/* Main thread, at a pick: extracts path ahead of its first start. */
/* Main thread, at a pick: trusts path and extracts it ahead of its
* first start. */
static void custom_driver_prepare(const char *path)
{
struct custom_driver_prep *p;
retro_task_t *task;
/* One task per zip at a time: two write the same .prep */
if ( custom_driver_st.safe_mode
|| !vulkan_custom_driver_supported()
if ( !vulkan_custom_driver_supported()
|| ( retro_atomic_load_acquire_int(&custom_driver_preps) > 0
&& string_is_equal(custom_driver_st.prep, path))
|| !(p = (struct custom_driver_prep*)calloc(1, sizeof(*p))))
@@ -640,6 +757,7 @@ static void custom_driver_prepare(const char *path)
strlcpy(p->path, path, sizeof(p->path));
strlcpy(p->base, custom_driver_st.base, sizeof(p->base));
p->api_level = g_android->activity->sdkVersion;
p->extract = !custom_driver_st.safe_mode;
task->handler = custom_driver_prep_handler;
task->state = p;
task->cleanup = custom_driver_prep_free;
+4
View File
@@ -34305,6 +34305,10 @@ MSG_HASH(
MSG_VULKAN_CUSTOM_DRIVER_SAFE_MODE,
"Started with the system Vulkan driver."
)
MSG_HASH(
MSG_VULKAN_CUSTOM_DRIVER_UNTRUSTED,
"Custom Vulkan driver was not picked in the menu or has changed. Pick it again to use it."
)
MSG_HASH(
MSG_RESAMPLER_QUALITY_LOWEST,
"Lowest"
+1
View File
@@ -348,6 +348,7 @@ enum msg_hash_enums
MSG_VULKAN_CUSTOM_DRIVER_UNSUPPORTED,
MSG_VULKAN_CUSTOM_DRIVER_RESTART,
MSG_VULKAN_CUSTOM_DRIVER_SAFE_MODE,
MSG_VULKAN_CUSTOM_DRIVER_UNTRUSTED,
MSG_RESAMPLER_QUALITY_LOWEST,
MSG_RESAMPLER_QUALITY_LOWER,
MSG_RESAMPLER_QUALITY_NORMAL,
@@ -1,6 +1,7 @@
/* Copyright (C) 2026 The RetroArch team. SPDX-License-Identifier:
* GPL-3.0-or-later */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lists/string_list.h>
@@ -443,6 +444,7 @@ static struct vulkan_custom_driver_request request(const char *path,
req.core = core;
req.supported = true;
req.safe_mode = false;
req.trusted = true;
return req;
}
@@ -582,6 +584,106 @@ static void test_session(void)
&& msg == VULKAN_CUSTOM_DRIVER_MSG_NONE && loader.loads == 2);
}
static void test_trust(void)
{
static char full[VULKAN_CUSTOM_DRIVER_STATE_MAX];
enum vulkan_custom_driver_msg msg;
struct vulkan_custom_driver_request req;
char trust[64];
char path[32];
char dir[9];
unsigned i;
size_t len = 0;
uint32_t a = vulkan_custom_driver_key("/d/A.zip", 18870258, 1000);
uint32_t b = vulkan_custom_driver_key("/d/B.zip", 4096, 2000);
/* a pick trusts that path in that version */
len = vulkan_custom_driver_trust_add(trust, len, sizeof(trust),
"/d/A.zip", a);
sprintf(dir, "%08x", (unsigned)a);
CHECK(len == 18 && !memcmp(trust, dir, 8)
&& !memcmp(trust + 8, "\t/d/A.zip\n", 10));
CHECK(vulkan_custom_driver_trust_find(trust, len, "/d/A.zip", a));
CHECK(vulkan_custom_driver_trust_add(trust, len, sizeof(trust),
"/d/A.zip", a) == len);
/* a changed zip, or the key under another path, is not trusted */
CHECK(!vulkan_custom_driver_trust_find(trust, len, "/d/A.zip",
vulkan_custom_driver_key("/d/A.zip", 18870259, 1000)));
CHECK(!vulkan_custom_driver_trust_find(trust, len, "/d/A.zip",
vulkan_custom_driver_key("/d/A.zip", 18870258, 1001)));
CHECK(!vulkan_custom_driver_trust_find(trust, len, "/d/B.zip", a));
/* seeded from an extraction: its dir is named by the key */
sprintf(dir, "%08x", (unsigned)b);
len = vulkan_custom_driver_trust_add(trust, len, sizeof(trust),
"/d/B.zip", (uint32_t)strtoul(dir, NULL, 16));
CHECK(len == 36);
CHECK(vulkan_custom_driver_trust_find(trust, len, "/d/B.zip", b));
CHECK(vulkan_custom_driver_trust_find(trust, len, "/d/A.zip", a));
/* a torn last line does not count, and is dropped by the next add */
sprintf(dir, "%08x", (unsigned)b);
memcpy(trust + len, dir, 8);
memcpy(trust + len + 8, "\t/d/C.zip", 9);
CHECK(!vulkan_custom_driver_trust_find(trust, len + 17, "/d/C.zip", b));
len = vulkan_custom_driver_trust_add(trust, len + 17, sizeof(trust),
"/d/D.zip", a);
CHECK(len == 54 && !memcmp(trust + 44, "\t/d/D.zip\n", 10));
CHECK(!vulkan_custom_driver_trust_find(trust, len, "/d/C.zip", b));
CHECK(vulkan_custom_driver_trust_find(trust, len, "/d/D.zip", a));
/* a tab in the path is fine, a newline cannot be read back */
len = vulkan_custom_driver_trust_add(trust, 0, sizeof(trust),
"/d/a\tb.zip", a);
CHECK(len == 20);
CHECK(vulkan_custom_driver_trust_find(trust, len, "/d/a\tb.zip", a));
CHECK(!vulkan_custom_driver_trust_find(trust, len, "/d/a", a));
CHECK(!vulkan_custom_driver_trust_find(trust, len, "b.zip", a));
CHECK(vulkan_custom_driver_trust_add(trust, len, sizeof(trust),
"/d/\n.zip", a) == len);
CHECK(vulkan_custom_driver_trust_add(trust, len, sizeof(trust),
"/d/C.zip\n", a) == len);
/* full at the file's cap: the oldest picks go */
len = 0;
for (i = 0; i < VULKAN_CUSTOM_DRIVER_STATE_MAX / 32; i++)
{
sprintf(path, "/d/drivers/v%06u.zip", i); /* 32-byte lines */
len = vulkan_custom_driver_trust_add(full, len, sizeof(full), path, i);
}
CHECK(len == VULKAN_CUSTOM_DRIVER_STATE_MAX);
CHECK(vulkan_custom_driver_trust_find(full, len,
"/d/drivers/v000000.zip", 0));
sprintf(path, "/d/drivers/v%06u.zip", i);
len = vulkan_custom_driver_trust_add(full, len, sizeof(full), path, i);
CHECK(len == VULKAN_CUSTOM_DRIVER_STATE_MAX);
CHECK(!vulkan_custom_driver_trust_find(full, len,
"/d/drivers/v000000.zip", 0));
CHECK(vulkan_custom_driver_trust_find(full, len,
"/d/drivers/v000001.zip", 1));
CHECK(vulkan_custom_driver_trust_find(full, len, path, i));
/* never picked: the system driver, UNTRUSTED once, nothing loaded */
begin(NULL, NULL);
req = request("/d/A.zip", "Core");
req.trusted = false;
CHECK(!open_req(&req, &msg) && msg == VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED);
CHECK(!open_req(&req, &msg) && msg == VULKAN_CUSTOM_DRIVER_MSG_NONE);
CHECK(loader.loads == 0 && !vulkan_custom_driver_session_current(&session));
/* picked again: said again while not yet trusted; trusted, it loads */
CHECK(vulkan_custom_driver_session_picked(&session, "/d/A.zip")
== VULKAN_CUSTOM_DRIVER_MSG_NONE);
CHECK(!open_req(&req, &msg) && msg == VULKAN_CUSTOM_DRIVER_MSG_UNTRUSTED);
req.trusted = true;
CHECK(open_req(&req, &msg) && msg == VULKAN_CUSTOM_DRIVER_MSG_NONE
&& loader.loads == 1);
/* a missing zip is INVALID, trusted or not */
req = request("/d/gone.zip", "Core");
req.size = -1;
req.trusted = false;
CHECK(!open_req(&req, &msg) && msg == VULKAN_CUSTOM_DRIVER_MSG_INVALID);
}
static bool opened(void)
{
return vulkan_custom_driver_session_current(&session);
@@ -922,6 +1024,7 @@ int main(void)
test_key();
test_crash_state();
test_session();
test_trust();
test_session_table();
test_crash_marker();
test_patch_soname();