android: open the core path that passed the folder check

runloop_core_dylib_load resolved the core's path to check that it
lies in the app's own folders, then handed the original, unresolved
path to dlopen.  Any symlink on that path could be repointed between
the check and the open.  android_core_path_allowed becomes
android_core_path_resolve, which hands back the resolved path it
checked, and that path is the one opened.  Which cores are allowed is
unchanged.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-11-07 03:06:17 +01:00
1 parent d94f129883
commit d7de9cc7b6
3 files changed
+27 -9

No files matched your search

+15 -5
View File
@@ -1558,16 +1558,12 @@ static void android_env_derive_application_info(JNIEnv *env, jobject activity)
(*env)->DeleteLocalRef(env, appinfo);
}
bool android_core_path_allowed(const char *path)
static bool android_core_real_path_allowed(const char *real_path)
{
char real_path[PATH_MAX_LENGTH];
const char *s;
size_t _len;
size_t i;
strlcpy(real_path, path, sizeof(real_path));
if (!path_resolve_realpath(real_path, sizeof(real_path), true))
*real_path = '\0';
for (i = 0; i < ARRAY_SIZE(android_core_dirs); i++)
{
_len = strlen(android_core_dirs[i]);
@@ -1595,6 +1591,20 @@ bool android_core_path_allowed(const char *path)
&& strchr(s + _len, '/'));
}
bool android_core_path_resolve(const char *path, char *s, size_t len)
{
if ( !path || !*path
|| strlcpy(s, path, len) >= len
|| !path_resolve_realpath(s, len, true)
|| !android_core_real_path_allowed(s))
{
if (len)
*s = '\0';
return false;
}
return true;
}
/* getPackageManager().getPackageInfo(getPackageName(), 0).versionCode. */
static unsigned android_env_derive_version_code(JNIEnv *env, jobject activity)
{
+4 -1
View File
@@ -583,7 +583,10 @@ void frontend_android_get_version(int32_t *major, int32_t *minor, int32_t *rel);
void frontend_android_get_version_sdk(int32_t *sdk);
bool android_core_path_allowed(const char *path);
/* Resolves @path, symlinks included, into @s; true only when the
* result lies in one of the app's own core folders. Open @s, not
* @path: the checked path is then the opened one. */
bool android_core_path_resolve(const char *path, char *s, size_t len);
bool is_screen_reader_enabled(void);
+8 -3
View File
@@ -842,8 +842,11 @@ void libretro_get_environment_info(
dylib_t runloop_core_dylib_load(const char *path, bool quiet)
{
#if defined(ANDROID)
/* Another app's intent can name the core */
if (!android_core_path_allowed(path))
/* Another app's intent can name the core. The resolved path that
* passed the check is the one opened, so no symlink on the way can
* be swapped between the check and the open. */
char real_path[PATH_MAX_LENGTH];
if (!android_core_path_resolve(path, real_path, sizeof(real_path)))
{
if (!quiet)
RARCH_ERR("[Core] Not loading \"%s\": not found in the app's own folders.\n",
@@ -852,8 +855,10 @@ dylib_t runloop_core_dylib_load(const char *path, bool quiet)
dylib_error();
return NULL;
}
#endif
return dylib_load(real_path);
#else
return dylib_load(path);
#endif
}
/* Whether the core path is resolved through symlinks before it is