Files
RetroArch/frontend/drivers/platform_ps2.c
T
LibretroAdmin b9be38fd55 strl: copy string literals without scanning them at runtime
strlcpy() begins with strlen() of its source. It lives in another
translation unit, so with no LTO that scan happens at runtime even when
the source is a literal whose length the compiler already measured, and
the call itself cannot be folded away either.

strlcpy_lit() passes the length over as sizeof(). When the destination
size is also a constant -- sizeof(buf), the usual case -- nothing is
left but the stores memcpy() compiles to:

  strlcpy      "core_options"  sizeof(buf)   7.01 ns
  strlcpy_lit  "core_options"  sizeof(buf)   0.42 ns
  strlcpy      41-char literal sizeof(buf)   7.74 ns
  strlcpy_lit  41-char literal sizeof(buf)   0.71 ns
  strlcpy      "core_options"  runtime len   7.11 ns
  strlcpy_lit  "core_options"  runtime len   0.70 ns

The destination bound is kept. When the literal does not fit it defers
to strlcpy(), so the copy truncates as before rather than overrunning;
a form that took only the literal would have turned every one of these
sites into an overflow waiting for a small buffer. The literal is
concatenated with "" so a non-literal source is a compile error rather
than a sizeof() that silently measures a pointer.

818 sites across 104 files were converted by parsing arguments rather
than by pattern-matching text: only a three-argument call whose second
argument is a bare string literal is rewritten, so a macro that happens
to expand to a literal is left alone. Line endings are preserved, so
the CRLF files carry only their own changed lines.

test/compat/test_strl.c compares the two against each other over every
destination size from 0 past the literal's length, for literals that
are empty, single-character, path-shaped, adjacently concatenated and
escape-bearing, checking the return, the terminator and the whole
buffer so an overrun past the terminator is caught. ASan+UBSan+LSan and
TSan both 4/4. The full menu_displaylist_build_list walk over every
displaylist type is byte-identical to before the change
(md5 a2a081bcac09cdc8d72d67efd14ac40e).
2026-08-30 02:10:20 +02:00

561 lines
18 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2018 - Francisco Javier Trujillo Mata - fjtrujy
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
* * You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sbv_patches.h>
#include <sifrpc.h>
#include <iopcontrol.h>
#include <elf-loader.h>
#include <ps2_all_drivers.h>
#include <libpwroff.h>
#include <ps2sdkapi.h>
#if defined(SCREEN_DEBUG)
#include <debug.h>
#endif
#ifndef IS_SALAMANDER
#include "../../retroarch.h"
#ifdef HAVE_MENU
#include "../../menu/menu_driver.h"
#endif
#endif
#include <compat/strl.h>
#include <file/file_path.h>
#include <string/stdstring.h>
#include "../frontend_driver.h"
#include "../../defaults.h"
#include "../../file_path_special.h"
#include "../../paths.h"
#include "../../verbosity.h"
#if defined(DEBUG)
#define DEFAULT_PARTITION "hdd0:__common:pfs"
#endif
static enum frontend_fork ps2_fork_mode = FRONTEND_FORK_NONE;
static char cwd[FILENAME_MAX] = {0};
static char mountPoint[10] = {0};
static char partition[50] = {0};
static enum HDD_MOUNT_STATUS hddMountStatus = HDD_MOUNT_INIT_STATUS_NOT_READY;
static enum HDD_INIT_STATUS hddStatus = HDD_INIT_STATUS_UNKNOWN;
static void create_path_names(void)
{
char user_path[FILENAME_MAX];
size_t _len = strlcpy(user_path, cwd, sizeof(user_path));
if (!PATH_CHAR_IS_SLASH(user_path[_len - 1])) {
_len += strlcpy(user_path + _len, PATH_DEFAULT_SLASH(), sizeof(user_path) - _len);
}
strlcpy_lit(user_path + _len, "retroarch", sizeof(user_path) - _len);
fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], cwd, sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
/* Content in the same folder */
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], cwd,
"cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], cwd,
"info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
/* user data */
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], user_path,
"assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], user_path,
"database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CHEATS], user_path,
"cheats", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], user_path,
"config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], user_path,
"downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], user_path,
"playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
"remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], user_path,
"savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], user_path,
"savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], user_path,
"system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CACHE], user_path,
"temp", sizeof(g_defaults.dirs[DEFAULT_DIR_CACHE]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OVERLAY], user_path,
"overlays", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS], user_path,
"thumbnails", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS]));
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_LOGS], user_path,
"logs", sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS]));
/* history and main config */
strlcpy(g_defaults.dirs[DEFAULT_DIR_CONTENT_HISTORY],
user_path, sizeof(g_defaults.dirs[DEFAULT_DIR_CONTENT_HISTORY]));
fill_pathname_join(g_defaults.path_config, user_path,
FILE_PATH_MAIN_CONFIG, sizeof(g_defaults.path_config));
#ifndef IS_SALAMANDER
dir_check_defaults("custom.ini");
#endif
}
static void reset_IOP()
{
SifInitRpc(0);
#if !defined(DEBUG) || defined(BUILD_FOR_PCSX2)
/* Comment this line if you don't wanna debug the output */
while(!SifIopReset(NULL, 0)){};
#endif
while(!SifIopSync()){};
SifInitRpc(0);
sbv_patch_enable_lmb();
sbv_patch_disable_prefix_check();
}
/* This method returns true if it can extract needed info from path, otherwise false.
* In case of true, it also updates mountPoint, partition and newCWD parameters
* It splits path by ":", and requires a minimum of 3 elements
* Example: if path = hdd0:__common:pfs:/retroarch/ then
* mountPoint = "pfs:"
* partition = "hdd0:__common"
* newCWD = pfs:/retroarch/
* return true
*/
bool getMountInfo(char *path, char *mountPoint, char *partition, char *newCWD)
{
struct string_list *str_list = string_split(path, ":");
if (str_list->size < 3)
{
string_list_free(str_list);
return false;
}
/* Build partition string: "device:path" using strlcpy offsets */
size_t len = strlcpy(partition, str_list->elems[0].data, 50);
if (len < 50) {
len += strlcpy_lit(partition + len, ":", 50 - len);
if (len < 50)
strlcpy(partition + len, str_list->elems[1].data, 50 - len);
}
/* Build mountPoint string: "mount:" using strlcpy offset */
len = strlcpy(mountPoint, str_list->elems[2].data, 10);
if (len < 10)
strlcpy_lit(mountPoint + len, ":", 10 - len);
/* Build newCWD string using strlcpy offset */
len = strlcpy(newCWD, mountPoint, FILENAME_MAX);
if (len < FILENAME_MAX)
strlcpy(newCWD + len,
str_list->size >= 4 ? str_list->elems[3].data : "",
FILENAME_MAX - len);
string_list_free(str_list);
return true;
}
static void init_drivers(bool extra_drivers)
{
init_fileXio_driver();
init_memcard_driver(true);
init_usb_driver(true);
init_mx4sio_driver(true);
init_cdfs_driver();
bool only_if_booted_from_hdd = true;
#if defined(DEBUG) && !defined(BUILD_FOR_PCSX2)
only_if_booted_from_hdd = false;
#else
init_poweroff_driver();
#endif
init_dev9_driver();
hddStatus = init_hdd_driver(false, only_if_booted_from_hdd);
#ifndef IS_SALAMANDER
if (extra_drivers)
{
init_audio_driver();
init_joystick_driver(true);
}
#endif
}
static void mount_partition(void)
{
char mount_path[FILENAME_MAX];
char new_cwd[FILENAME_MAX];
int should_mount = 0;
int bootDeviceID = getBootDeviceID(cwd);
if (hddStatus != HDD_INIT_STATUS_IRX_OK)
return;
/* Try to mount HDD partition, either from cwd or default one */
if (bootDeviceID == BOOT_DEVICE_HDD || bootDeviceID == BOOT_DEVICE_HDD0)
{
should_mount = 1;
strlcpy(mount_path, cwd, sizeof(mount_path));
}
#if !defined(IS_SALAMANDER) && defined(DEBUG)
else
{
/* Even if we're booting from USB, try to mount default partition */
strlcpy(mount_path, DEFAULT_PARTITION, sizeof(mount_path));
should_mount = 1;
}
#endif
if (!should_mount)
return;
if (getMountInfo(mount_path, mountPoint, partition, new_cwd) != 1)
{
RARCH_WARN("Partition info not read\n");
return;
}
hddMountStatus = mount_hdd_partition(mountPoint, partition);
if (hddMountStatus != HDD_MOUNT_STATUS_OK)
{
RARCH_WARN("Error mount mounting partition %s, %s\n", mountPoint, partition);
return;
}
/* If we're booting from HDD, we must update the cwd variable
* and add : to the partition */
if (bootDeviceID == BOOT_DEVICE_HDD || bootDeviceID == BOOT_DEVICE_HDD0)
{
strlcpy(cwd, new_cwd, sizeof(cwd));
strlcat(partition, ":", sizeof(partition));
}
else
{
/* We MUST put partition as empty to avoid wrong results
with LoadELFFromFileWithPartition */
strlcpy_lit(partition, "", sizeof(partition));
}
}
static void deinit_drivers(bool deinit_filesystem, bool deinit_powerOff)
{
#ifndef IS_SALAMANDER
deinit_audio_driver();
deinit_joystick_driver(false);
#endif
if (deinit_filesystem)
{
umount_hdd_partition(mountPoint);
deinit_hdd_driver(false);
deinit_dev9_driver();
deinit_cdfs_driver();
deinit_mx4sio_driver(true);
deinit_usb_driver(true);
deinit_memcard_driver(true);
deinit_fileXio_driver();
hddMountStatus = HDD_MOUNT_INIT_STATUS_NOT_READY;
hddStatus = HDD_INIT_STATUS_UNKNOWN;
}
if (deinit_powerOff)
deinit_poweroff_driver();
}
static void poweroffHandler(void *arg)
{
deinit_drivers(true, false);
poweroffShutdown();
}
static void frontend_ps2_get_env(int *argc, char *argv[],
void *args, void *params_data)
{
create_path_names();
#ifndef IS_SALAMANDER
if (argv[1] && *argv[1])
{
static char path[FILENAME_MAX] = {0};
struct rarch_main_wrap *args =
(struct rarch_main_wrap*)params_data;
if (args)
{
strlcpy(path, argv[1], sizeof(path));
args->flags &= ~(RARCH_MAIN_WRAP_FLAG_VERBOSE
| RARCH_MAIN_WRAP_FLAG_NO_CONTENT);
args->flags |= RARCH_MAIN_WRAP_FLAG_TOUCHED;
args->config_path = NULL;
args->sram_path = NULL;
args->state_path = NULL;
args->content_path = path;
args->libretro_path = NULL;
RARCH_LOG("argv[0]: %s\n", argv[0]);
RARCH_LOG("argv[1]: %s\n", argv[1]);
RARCH_LOG("Auto-start game %s.\n", argv[1]);
}
}
#endif
#ifndef IS_SALAMANDER
dir_check_defaults("custom.ini");
#endif
}
static void common_init_drivers(bool extra_drivers)
{
init_drivers(extra_drivers);
poweroffSetCallback(&poweroffHandler, NULL);
getcwd(cwd, sizeof(cwd));
// Workaround for PS2SDK issue: https://github.com/ps2dev/ps2sdk/issues/805
// PS2SDK's initialization routine does not properly set the CWD for HDD
// paths, leading to all slashes being converted to backslashes. Manually
// convert them back to slashes to work around the issue until it is
// properly fixed in PS2SDK.
int bootDeviceID = getBootDeviceID(cwd);
if (bootDeviceID == BOOT_DEVICE_HDD || bootDeviceID == BOOT_DEVICE_HDD0)
pathname_conform_slashes_to_os(cwd);
#if !defined(IS_SALAMANDER) && !defined(DEBUG)
/* If it is not Salamander, we need to go one level
* up for setting the CWD. */
path_parent_dir(cwd, strlen(cwd));
#endif
mount_partition();
waitUntilDeviceIsReady(cwd);
}
static void frontend_ps2_init(void *data)
{
reset_IOP();
#if defined(SCREEN_DEBUG)
init_scr();
scr_printf("\n\nStarting RetroArch...\n");
#endif
common_init_drivers(true);
}
static void frontend_ps2_deinit(void *data)
{
bool deinit_filesystem = false;
#ifndef IS_SALAMANDER
if (ps2_fork_mode == FRONTEND_FORK_NONE)
deinit_filesystem = true;
#endif
deinit_drivers(deinit_filesystem, true);
}
static void frontend_ps2_exec(const char *path, bool should_load_game)
{
int args = 0;
char *argv[1];
RARCH_LOG("Attempt to load executable: [%s], partition [%s].\n", path, partition);
/* Reload IOP drivers for saving IOP ram */
deinit_drivers(true, true);
reset_IOP();
common_init_drivers(false);
waitUntilDeviceIsReady((char *)path);
#ifndef IS_SALAMANDER
char game_path[FILENAME_MAX];
if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT))
{
args++;
const char *content = path_get(RARCH_PATH_CONTENT);
strlcpy(game_path, content, sizeof(game_path));
argv[0] = game_path;
RARCH_LOG("Attempt to load executable: [%s], partition [%s] with game [%s]\n", path, partition, game_path);
}
#endif
LoadELFFromFileWithPartition(path, partition, args, argv);
}
#ifndef IS_SALAMANDER
static bool frontend_ps2_set_fork(enum frontend_fork fork_mode)
{
switch (fork_mode)
{
case FRONTEND_FORK_CORE:
RARCH_LOG("FRONTEND_FORK_CORE\n");
ps2_fork_mode = fork_mode;
break;
case FRONTEND_FORK_CORE_WITH_ARGS:
RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n");
ps2_fork_mode = fork_mode;
break;
case FRONTEND_FORK_RESTART:
RARCH_LOG("FRONTEND_FORK_RESTART\n");
/* NOTE: We don't implement Salamander, so just turn
* this into FRONTEND_FORK_CORE. */
ps2_fork_mode = FRONTEND_FORK_CORE;
break;
case FRONTEND_FORK_NONE:
default:
return false;
}
return true;
}
#endif
static void frontend_ps2_exitspawn(char *s, size_t len, char *args)
{
bool should_load_content = false;
#ifndef IS_SALAMANDER
if (ps2_fork_mode == FRONTEND_FORK_NONE)
return;
switch (ps2_fork_mode)
{
case FRONTEND_FORK_CORE_WITH_ARGS:
should_load_content = true;
break;
case FRONTEND_FORK_NONE:
default:
break;
}
#endif
frontend_ps2_exec(s, should_load_content);
}
enum frontend_architecture frontend_ps2_get_arch(void)
{
return FRONTEND_ARCH_MIPS;
}
static int frontend_ps2_parse_drive_list(void *data, bool load_content)
{
#ifndef IS_SALAMANDER
char hdd[10];
file_list_t *list = (file_list_t*)data;
enum msg_hash_enums enum_idx = load_content
? MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR
: MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
menu_entries_append(list,
rootDevicePath(BOOT_DEVICE_MC0),
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
menu_entries_append(list,
rootDevicePath(BOOT_DEVICE_MC1),
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
menu_entries_append(list,
rootDevicePath(BOOT_DEVICE_CDFS),
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
menu_entries_append(list,
rootDevicePath(BOOT_DEVICE_MASS),
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
if (hddMountStatus == HDD_MOUNT_STATUS_OK)
{
size_t _len = strlcpy(hdd, mountPoint, sizeof(hdd));
hdd[ _len] = '/';
hdd[ ++_len] = '\0';
menu_entries_append(list,
hdd,
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
}
menu_entries_append(list,
rootDevicePath(BOOT_DEVICE_HOST),
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
#if defined(DEBUG) && !defined(BUILD_FOR_PCSX2)
menu_entries_append(list,
"host:",
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
enum_idx,
FILE_TYPE_DIRECTORY, 0, 0, NULL);
#endif
#endif
return 0;
}
static void frontend_ps2_process_args(int *argc, char *argv[])
{
#ifndef IS_SALAMANDER
/* Make sure active core path is set here. */
char path[PATH_MAX_LENGTH] = {0};
strlcpy(path, argv[0], sizeof(path));
if (path_is_valid(path))
path_set(RARCH_PATH_CORE, path);
#endif
}
frontend_ctx_driver_t frontend_ctx_ps2 = {
frontend_ps2_get_env, /* get_env */
frontend_ps2_init, /* init */
frontend_ps2_deinit, /* deinit */
frontend_ps2_exitspawn, /* exitspawn */
frontend_ps2_process_args, /* process_args */
frontend_ps2_exec, /* exec */
#ifdef IS_SALAMANDER
NULL, /* set_fork */
#else
frontend_ps2_set_fork, /* set_fork */
#endif
NULL, /* shutdown */
NULL, /* get_name */
NULL, /* get_os */
NULL, /* load_content */
frontend_ps2_get_arch, /* get_architecture */
NULL, /* get_powerstate */
frontend_ps2_parse_drive_list,/* parse_drive_list */
NULL, /* install_signal_handler */
NULL, /* get_sighandler_state */
NULL, /* set_sighandler_state */
NULL, /* destroy_sighandler_state */
NULL, /* attach_console */
NULL, /* detach_console */
NULL, /* get_lakka_version */
NULL, /* set_screen_brightness */
NULL, /* set_sustained_performance_mode */
NULL, /* get_cpu_model_name */
NULL, /* get_user_language */
NULL, /* is_narrator_running */
NULL, /* accessibility_speak */
NULL, /* set_gamemode */
NULL, /* get_display_type */
"ps2", /* ident */
NULL /* get_video_driver */
};