From b1f2be98fa72137f9a69ff390049e25bb42f1718 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Mon, 16 Jan 2023 01:06:43 +0200 Subject: [PATCH] [FS] Add app data folder, add path safety checks --- include/emulator.hpp | 7 ++++ include/fs/archive_base.hpp | 47 +++++++++++++++++++++++++++ include/io_file.hpp | 8 +++++ src/core/fs/archive_ext_save_data.cpp | 16 +++++---- src/core/services/fs.cpp | 9 ++--- src/main.cpp | 2 +- 6 files changed, 77 insertions(+), 12 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index 3ef0ef68..fe812e5c 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -5,6 +5,7 @@ #include #include "cpu.hpp" +#include "io_file.hpp" #include "memory.hpp" #include "opengl.hpp" #include "PICA/gpu.hpp" @@ -43,6 +44,12 @@ public: SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL); glContext = SDL_GL_CreateContext(window); + + // Get path for saving files (AppData on Windows, /home/user/.local/share/ApplcationName on Linux, etc) + char* appData = SDL_GetPrefPath(nullptr, "Alber"); + IOFile::setAppDataDir(appData); + SDL_free(appData); + reset(); } diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 5fcc37f9..9a08b4de 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include #include +#include #include #include "helpers.hpp" #include "memory.hpp" @@ -93,6 +95,51 @@ protected: using Handle = u32; Memory& mem; + // Returns if a specified 3DS path in UTF16 or ASCII format is safe or not + // A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs + // And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree + // And access files outside of the emulator's app data folder + template + bool isPathSafe(const FSPath& path) { + static_assert(format == PathType::ASCII || format == PathType::UTF16); + using String = std::conditional::type; // String type for the path + using Char = String::value_type; // Char type for the path + + String pathString, dots; + if constexpr (std::is_same()) { + pathString = path.utf16_string; + dots = u".."; + } else { + pathString = path.string; + dots = ".."; + } + + // If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe + if (pathString[0] != Char('/')) return false; + + // Counts how many folders sans the root our file is nested under. + // If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox. + // If it's 0 then this is the FS root. + // If it's > 0 then we're in a subdirectory of the root. + int level = 0; + + // Split the string on / characters and see how many of the substrings are ".." + size_t pos = 0; + while ((pos = pathString.find(Char('/'))) != String::npos) { + String token = pathString.substr(0, pos); + pathString.erase(0, pos + 1); + + if (token == dots) { + level--; + if (level < 0) return false; + } else { + level++; + } + } + + return true; + } + public: virtual std::string name() = 0; virtual u64 getFreeBytes() = 0; diff --git a/include/io_file.hpp b/include/io_file.hpp index 1deb2ec2..9a26a240 100644 --- a/include/io_file.hpp +++ b/include/io_file.hpp @@ -19,6 +19,7 @@ class IOFile { FILE* handle = nullptr; + static inline std::filesystem::path appData = ""; // Directory for holding app data. AppData on Windows public: bool isOpen() { @@ -81,4 +82,11 @@ public: bool rewind() { return seek(0, SEEK_SET); } + + static void setAppDataDir(const char* dir) { + if (!dir) Helpers::panic("Failed to set app data directory"); + appData = std::filesystem::path(dir); + } + + static std::filesystem::path getAppData() { return IOFile::appData; } }; \ No newline at end of file diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index ad68ef38..e125951c 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -1,9 +1,16 @@ #include "fs/archive_ext_save_data.hpp" #include +namespace fs = std::filesystem; + bool ExtSaveDataArchive::openFile(const FSPath& path) { - if (path.type != PathType::Binary) { - Helpers::panic("ExtSaveData accessed with a non-binary path in OpenFile. Type: %d", path.type); + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) + Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); + + fs::path p = IOFile::getAppData() / "NAND"; + p += fs::path(path.utf16_string).make_preferred(); + return false; } Helpers::panic("ExtSaveDataArchive::OpenFile: Failed"); @@ -15,11 +22,6 @@ ArchiveBase* ExtSaveDataArchive::openArchive(const FSPath& path) { Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } - u32 mediaType = *(u32*)&path.binary[0]; - u64 saveID = *(u64*)&path.binary[4]; // TODO: Get rid of UB here. - - Helpers::panic("ExtSaveData: media type = %d\n", mediaType); - return this; } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 2a0df4e5..1d4b2ca1 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -18,7 +18,8 @@ namespace FSCommands { namespace Result { enum : u32 { Success = 0, - Failure = 0xFFFFFFFF + FileNotFound = 0xC8804464, // TODO: Verify this + Failure = 0xFFFFFFFF, }; } @@ -166,9 +167,9 @@ void FSService::openFile(u32 messagePointer) { std::optional handle = openFileHandle(archive, filePath); if (!handle.has_value()) { - Helpers::panic("OpenFile: Failed to open file with given path"); - } - else { + printf("OpenFile failed\n"); + mem.write32(messagePointer + 4, Result::FileNotFound); + } else { mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor" mem.write32(messagePointer + 12, handle.value()); diff --git a/src/main.cpp b/src/main.cpp index 5a9aafa0..2dc17683 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ int main (int argc, char *argv[]) { emu.initGraphicsContext(); - auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "Pokemon Mystery Dungeon - Gates to Infinity (USA).3ds"); + auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "Pokemon Rumble Blast (USA).3ds"); if (!emu.loadROM(romPath)) { // For some reason just .c_str() doesn't show the proper path Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str());