fix(filesystem): reject invalid parent traversal on Windows
This commit is contained in:
parent
ad14190c13
commit
17827dd9ab
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "prx/libSceAvPlayer/include/AvPlayer.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
|
@ -243,7 +244,9 @@ public:
|
|||
format->pb = replacement->Context();
|
||||
format->flags |= AVFMT_FLAG_CUSTOM_IO;
|
||||
} else {
|
||||
const auto resolved = ResolvePath_nid_no_patch(path.c_str()).u8string();
|
||||
std::filesystem::path hostPath;
|
||||
if (ResolvePathError_nid_no_patch(path.c_str(), hostPath) != 0) return false;
|
||||
const auto resolved = hostPath.u8string();
|
||||
url = "file:" + std::string(resolved.begin(), resolved.end());
|
||||
}
|
||||
if (const int error = avformat_open_input(&format, replacement ? nullptr : url.c_str(), nullptr, nullptr); error < 0) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "prx/libSceFont/include/FontInternal.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -280,7 +281,11 @@ int APS5_VABI sceFontOpenFontFile(FontLibrary library, const char* path, std::ui
|
|||
return SCE_FONT_ERROR_INVALID_LIBRARY;
|
||||
}
|
||||
if (!pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER;
|
||||
const std::filesystem::path hostPath = path ? ResolvePath_nid_no_patch(path) : std::filesystem::path{};
|
||||
std::filesystem::path hostPath;
|
||||
if (path && ResolvePathError_nid_no_patch(path, hostPath) != 0) {
|
||||
*pFontHandle = nullptr;
|
||||
return SCE_FONT_ERROR_FS_OPEN_FAILED;
|
||||
}
|
||||
const std::string hostPathString = hostPath.string();
|
||||
std::uint32_t libraryLock = 0;
|
||||
const int beginRc = BeginOpen(library, pFontHandle, libraryLock);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define CORE_LIBS_PRX_LIBC_INCLUDE_FILESYSTEMERROR_HPP
|
||||
|
||||
#include <system_error>
|
||||
#include "General.hpp"
|
||||
|
||||
inline int FilesystemError(const std::error_code& error) {
|
||||
if (error == std::errc::no_such_file_or_directory) return 2;
|
||||
|
|
@ -22,4 +23,14 @@ inline int FilesystemError(const std::error_code& error) {
|
|||
return 5;
|
||||
}
|
||||
|
||||
inline int ResolvePathError_nid_no_patch(const char* path, std::filesystem::path& resolved) {
|
||||
try {
|
||||
resolved = ResolvePath_nid_no_patch(path);
|
||||
return 0;
|
||||
} catch (const std::filesystem::filesystem_error& error) {
|
||||
if (error.code() == std::errc::illegal_byte_sequence) return 2;
|
||||
return FilesystemError(error.code());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "prx/libc/include/GuestDirectory.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include <dirent.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
|
@ -79,7 +80,7 @@ void* APS5_VABI opendir_nid_postfix(const char* path) {
|
|||
if (!*path) { errno = 2; return nullptr; }
|
||||
try {
|
||||
auto directory = std::make_unique<Directory>();
|
||||
directory->path = ResolvePath_nid_no_patch(path);
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, directory->path)) { errno = error; return nullptr; }
|
||||
directory->native = OpenNative(directory->path);
|
||||
if (!directory->native) { errno = DirectoryError(errno); return nullptr; }
|
||||
return directory.release();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "prx/libc/include/FileStream.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include "prx/libc/include/GuestArena.hpp"
|
||||
|
||||
struct GuestFileMode {
|
||||
|
|
@ -83,7 +84,15 @@ FileStream* APS5_VABI freopen_nid_postfix(const char* filename, const char* mode
|
|||
const auto parsed = ParseFileMode(mode);
|
||||
if (!parsed) { errno = 22; return nullptr; }
|
||||
try {
|
||||
const std::filesystem::path path = *filename ? ResolvePath_nid_no_patch(filename) : std::filesystem::path{};
|
||||
std::filesystem::path path;
|
||||
if (*filename) {
|
||||
if (const int error = ResolvePathError_nid_no_patch(filename, path)) {
|
||||
stream->Close();
|
||||
if (stream->IsDynamic()) delete stream;
|
||||
errno = error;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (stream->Reopen(path, parsed->native.c_str())) {
|
||||
if (!path.empty() && parsed->writes) RecordWrittenPath_nid_no_patch(path);
|
||||
return stream;
|
||||
|
|
@ -103,7 +112,8 @@ FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode)
|
|||
errno = EINVAL;
|
||||
return nullptr;
|
||||
}
|
||||
const std::filesystem::path fpath = ResolvePath_nid_no_patch(filename);
|
||||
std::filesystem::path fpath;
|
||||
if (const int error = ResolvePathError_nid_no_patch(filename, fpath)) { errno = error; return nullptr; }
|
||||
const auto abs_path = fpath.string();
|
||||
std::unique_ptr<std::FILE, int (*)(std::FILE*)> handle(OpenNative(fpath, parsed->native), std::fclose);
|
||||
if (!handle) {
|
||||
|
|
|
|||
|
|
@ -119,13 +119,24 @@ std::filesystem::path HostPath(WorkingDirectory& state, const std::filesystem::p
|
|||
if (auto aliased = ResolveAlias(guest.relative_path().generic_string())) return *aliased;
|
||||
return ResolveHostPath(state.root, guest.relative_path()).make_preferred();
|
||||
}
|
||||
bool HostDirectory(WorkingDirectory& state, const std::filesystem::path& guest) {
|
||||
try {
|
||||
std::error_code error;
|
||||
return std::filesystem::is_directory(HostPath(state, guest), error);
|
||||
} catch (const std::filesystem::filesystem_error&) {
|
||||
return false;
|
||||
void RequireHostDirectory(WorkingDirectory& state, const std::filesystem::path& guest) {
|
||||
const auto host = HostPath(state, guest);
|
||||
std::error_code error;
|
||||
const auto status = std::filesystem::status(host, error);
|
||||
if (std::filesystem::is_directory(status)) return;
|
||||
if (!error) error = std::make_error_code(std::filesystem::exists(status) ? std::errc::not_a_directory : std::errc::no_such_file_or_directory);
|
||||
#ifdef _WIN32
|
||||
if (error == std::errc::no_such_file_or_directory) {
|
||||
for (auto prefix = host.parent_path(); prefix.has_relative_path(); prefix = prefix.parent_path()) {
|
||||
std::error_code prefixError;
|
||||
const auto prefixStatus = std::filesystem::status(prefix, prefixError);
|
||||
if (!std::filesystem::exists(prefixStatus)) continue;
|
||||
if (!std::filesystem::is_directory(prefixStatus)) error = std::make_error_code(std::errc::not_a_directory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
throw std::filesystem::filesystem_error("Guest path component is not a directory", host, error);
|
||||
}
|
||||
std::filesystem::path Resolve(WorkingDirectory& state, const char* path) {
|
||||
std::string text(path);
|
||||
|
|
@ -141,11 +152,7 @@ std::filesystem::path Resolve(WorkingDirectory& state, const char* path) {
|
|||
for (auto part = relative.begin(); part != relative.end(); ++part) {
|
||||
if (*part == "..") {
|
||||
const auto parent = guest.lexically_normal();
|
||||
if (parent.has_relative_path() && !HostDirectory(state, parent)) {
|
||||
auto host = HostPath(state, parent);
|
||||
for (; part != relative.end(); ++part) host /= *part;
|
||||
return host.make_preferred();
|
||||
}
|
||||
if (parent.has_relative_path()) RequireHostDirectory(state, parent);
|
||||
}
|
||||
guest /= *part;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include "prx/libc/include/PreciseWait.hpp"
|
||||
#include "prx/libc/include/GuestArena.hpp"
|
||||
#include "prx/libkernel/Apr/include/AprCommandBuffer.hpp"
|
||||
|
|
@ -107,7 +108,8 @@ int _fail(int guestErrno) {
|
|||
|
||||
bool _resolve(const char* guestPath, std::uint32_t* id, std::uint64_t* size) {
|
||||
if (!guestPath) return false;
|
||||
auto hostPath = ResolvePath_nid_no_patch(guestPath);
|
||||
std::filesystem::path hostPath;
|
||||
if (ResolvePathError_nid_no_patch(guestPath, hostPath) != 0) return false;
|
||||
std::uint64_t bytes = 0;
|
||||
if (!_fileSize(hostPath, bytes)) return false;
|
||||
std::lock_guard lock(g_filesLock);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "prx/libkernel/File/include/FileFlags.hpp"
|
||||
#include "prx/libkernel/File/include/NativeStat.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include "prx/libc/include/GuestArena.hpp"
|
||||
#include "prx/libkernel/File/include/File.hpp"
|
||||
#include "prx/libkernel/File/include/DirectoryDescriptor.hpp"
|
||||
|
|
@ -142,7 +143,8 @@ int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) {
|
|||
const int fd = File::OpenRandomDevice();
|
||||
return fd < 0 ? SceErrorFromErrno(errno) : fd;
|
||||
}
|
||||
auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
#ifdef _WIN32
|
||||
if ((flags & (SCE_KERNEL_O_DIRECTORY | SCE_KERNEL_O_CREAT)) == SCE_KERNEL_O_DIRECTORY) {
|
||||
std::error_code error;
|
||||
|
|
@ -229,11 +231,7 @@ int APS5_VABI sceKernelStat(const char* path, FileStat* sb) {
|
|||
}
|
||||
if (*path == '\0') return SceErrorFromErrno(2);
|
||||
std::filesystem::path native;
|
||||
try {
|
||||
native = ResolvePath_nid_no_patch(path);
|
||||
} catch (const std::filesystem::filesystem_error&) {
|
||||
return SceErrorFromErrno(2);
|
||||
}
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
std::error_code error;
|
||||
constexpr int GuestEnotdir = 20;
|
||||
const auto status = std::filesystem::status(native, error);
|
||||
|
|
@ -252,7 +250,8 @@ int APS5_VABI sceKernelUnlink(const char* path) {
|
|||
throw std::invalid_argument(std::string(__func__) + ": path is null");
|
||||
}
|
||||
if (*path == '\0') return SceErrorFromErrno(2);
|
||||
auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
constexpr int GuestEperm = 1;
|
||||
std::error_code error;
|
||||
if (std::filesystem::is_directory(std::filesystem::symlink_status(native, error))) return SceErrorFromErrno(GuestEperm);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <limits>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include "prx/libc/include/GuestArena.hpp"
|
||||
#include "prx/libkernel/File/include/File.hpp"
|
||||
#include "prx/libkernel/File/include/FileFlags.hpp"
|
||||
|
|
@ -436,7 +437,8 @@ extern "C" {
|
|||
|
||||
int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
|
||||
if (const int error = PathError(path)) return PosixFailure(error);
|
||||
const auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return PosixFailure(error);
|
||||
if (NativeChmod(native, mode & 07777) != 0) return PosixResult(SceErrorFromErrno(errno));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -575,7 +577,8 @@ int64_t APS5_VABI lseek_nid_postfix(int d, int64_t offset, int whence) {
|
|||
|
||||
int APS5_VABI mkdir_nid_postfix(const char* path, uint16_t mode) {
|
||||
if (const int error = PathError(path)) return PosixFailure(error);
|
||||
auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return PosixFailure(error);
|
||||
if (NativeMkdir(native, mode) != 0) {
|
||||
const int nativeError = errno;
|
||||
const bool blockedByFile = (nativeError == ENOENT || nativeError == EINVAL) && HasNonDirectoryPrefix(native);
|
||||
|
|
@ -685,7 +688,9 @@ int APS5_VABI stat_nid_postfix(const char* path, FileStat* sb) {
|
|||
int APS5_VABI lstat_nid_postfix(const char* path, FileStat* sb) {
|
||||
if (sb == nullptr) return PosixFailure(GUEST_EFAULT);
|
||||
if (const int error = PathError(path)) return PosixFailure(error);
|
||||
if (!File::FillLinkStat(ResolvePath_nid_no_patch(path), sb)) return PosixResult(SceErrorFromErrno(errno));
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return PosixFailure(error);
|
||||
if (!File::FillLinkStat(native, sb)) return PosixResult(SceErrorFromErrno(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -698,8 +703,10 @@ int APS5_VABI sceKernelCheckReachability(const char* path) {
|
|||
if (path == nullptr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
if (*path == '\0') return SCE_KERNEL_ERROR_ENOENT;
|
||||
if (std::strlen(path) > 255) return SCE_KERNEL_ERROR_ENAMETOOLONG;
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
std::error_code error;
|
||||
return std::filesystem::exists(ResolvePath_nid_no_patch(path), error) ? 0 : SCE_KERNEL_ERROR_ENOENT;
|
||||
return std::filesystem::exists(native, error) ? 0 : SCE_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelFstat(int d, FileStat* sb) {
|
||||
|
|
@ -808,7 +815,8 @@ int APS5_VABI sceKernelMkdir(const char* path, uint16_t mode) {
|
|||
(void)mode;
|
||||
if (path == nullptr) throw std::invalid_argument("sceKernelMkdir: path is null");
|
||||
if (!*path) return SceErrorFromErrno(GUEST_ENOENT);
|
||||
auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
while (!native.has_filename() && native.has_relative_path()) native = native.parent_path();
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(native, error)) return SceErrorFromErrno(GUEST_EEXIST);
|
||||
|
|
@ -1001,10 +1009,12 @@ int64_t APS5_VABI pwritev_nid_postfix(int d, const KernelIovec* iov, int iovcnt,
|
|||
int APS5_VABI sceKernelRename(const char* from, const char* to) {
|
||||
if (from == nullptr || to == nullptr) throw std::invalid_argument("sceKernelRename: path is null");
|
||||
if (*from == '\0' || *to == '\0') return SceErrorFromErrno(GUEST_ENOENT);
|
||||
const auto source = ResolvePath_nid_no_patch(from);
|
||||
std::filesystem::path source;
|
||||
if (const int error = ResolvePathError_nid_no_patch(from, source)) return SceKernelError(error);
|
||||
std::error_code error;
|
||||
if (!std::filesystem::exists(source, error)) return SceErrorFromErrno(GUEST_ENOENT);
|
||||
const auto destination = ResolvePath_nid_no_patch(to);
|
||||
std::filesystem::path destination;
|
||||
if (const int result = ResolvePathError_nid_no_patch(to, destination)) return SceKernelError(result);
|
||||
std::filesystem::rename(source, destination, error);
|
||||
if (error) return SceErrorFromErrno(GUEST_EIO);
|
||||
RecordWrittenPath_nid_no_patch(source);
|
||||
|
|
@ -1015,7 +1025,8 @@ int APS5_VABI sceKernelRename(const char* from, const char* to) {
|
|||
int APS5_VABI sceKernelRmdir(const char* path) {
|
||||
if (path == nullptr) throw std::invalid_argument("sceKernelRmdir: path is null");
|
||||
if (*path == '\0') return SceErrorFromErrno(GUEST_ENOENT);
|
||||
const auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
std::error_code error;
|
||||
if (!std::filesystem::is_directory(native, error)) {
|
||||
const bool blockedByFile = HasNonDirectoryPrefix(native) || std::filesystem::exists(WithoutTrailingSeparator(native), error);
|
||||
|
|
@ -1040,7 +1051,8 @@ extern "C" {
|
|||
int APS5_VABI sceKernelChmod_nid_postfix(const char* path, std::uint16_t mode) {
|
||||
if (!path) return SceErrorFromErrno(GUEST_EFAULT);
|
||||
if (!*path) return SceErrorFromErrno(GUEST_ENOENT);
|
||||
const auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
if (NativeChmod(native, mode & 07777) != 0) return SceErrorFromErrno(errno);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1059,7 +1071,8 @@ int APS5_VABI sceKernelTruncate_nid_postfix(const char* path, std::int64_t lengt
|
|||
if (path == nullptr) throw std::invalid_argument("sceKernelTruncate: path is null");
|
||||
if (*path == '\0') return SceErrorFromErrno(GUEST_ENOENT);
|
||||
if (length < 0) return SceErrorFromErrno(GUEST_EINVAL);
|
||||
const auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
std::error_code error;
|
||||
const auto status = std::filesystem::status(native, error);
|
||||
if (!std::filesystem::exists(status)) return SceErrorFromErrno(GUEST_ENOENT);
|
||||
|
|
@ -1082,7 +1095,8 @@ int APS5_VABI sceKernelUtimes_nid_postfix(const char* path, const KernelTimeval*
|
|||
if (times[i].tv_usec < 0 || times[i].tv_usec >= 1000000) return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
const auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error);
|
||||
if (NativeUtimes(native, times) != 0) return SceErrorFromErrno(errno);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#endif
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FilesystemError.hpp"
|
||||
#include "prx/libkernel/File/include/DirectoryDescriptor.hpp"
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
|
|
@ -75,7 +76,8 @@ std::int64_t APS5_VABI readlink_nid_postfix(const char* path, char* buffer, std:
|
|||
if (path == nullptr) return Fail(GuestEfault);
|
||||
if (*path == '\0') return Fail(GuestEnoent);
|
||||
if (size > static_cast<std::size_t>(std::numeric_limits<std::int64_t>::max())) return Fail(GuestEinval);
|
||||
const auto native = ResolvePath_nid_no_patch(path);
|
||||
std::filesystem::path native;
|
||||
if (const int error = ResolvePathError_nid_no_patch(path, native)) return Fail(error);
|
||||
#ifdef _WIN32
|
||||
const DWORD attributes = GetFileAttributesW(native.wstring().c_str());
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|
||||
|
|
@ -99,8 +101,9 @@ std::int64_t APS5_VABI readlink_nid_postfix(const char* path, char* buffer, std:
|
|||
int APS5_VABI link_nid_postfix(const char* existing, const char* created) {
|
||||
if (existing == nullptr || created == nullptr) return static_cast<int>(Fail(GuestEfault));
|
||||
if (*existing == '\0' || *created == '\0') return static_cast<int>(Fail(GuestEnoent));
|
||||
const auto source = ResolvePath_nid_no_patch(existing);
|
||||
const auto destination = ResolvePath_nid_no_patch(created);
|
||||
std::filesystem::path source, destination;
|
||||
if (const int error = ResolvePathError_nid_no_patch(existing, source)) return static_cast<int>(Fail(error));
|
||||
if (const int error = ResolvePathError_nid_no_patch(created, destination)) return static_cast<int>(Fail(error));
|
||||
std::error_code error;
|
||||
const auto status = std::filesystem::symlink_status(source, error);
|
||||
if (!std::filesystem::exists(status)) return static_cast<int>(Fail(GuestEnoent));
|
||||
|
|
|
|||
|
|
@ -576,6 +576,10 @@ void TestResolveFailures() {
|
|||
Require(sceKernelAprResolveFilepathsToIdsAndFileSizesForEach(paths, 2, ids, sizes, results) == 1);
|
||||
Require(results[0] == 0 && results[1] == static_cast<int>(0x80020002));
|
||||
Require(ids[1] == 0xffffffffu && sizes[0] == 4 && sizes[1] == 0);
|
||||
for (const char* invalid : {"ampr_resolve_missing.bin/../ampr_resolve_existing.bin", "ampr_resolve_existing.bin/../ampr_resolve_existing.bin"}) {
|
||||
Require(sceKernelAprResolveFilepathsToIds(&invalid, 1, ids, &failed) == -1);
|
||||
Require(*__error_nid_postfix() == 2 && failed == 0 && ids[0] == 0xffffffffu);
|
||||
}
|
||||
std::remove(paths[0]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -723,6 +723,8 @@ void TestPlayback() {
|
|||
Check(sceAvPlayerStreamCount(player) == OperationFailed, "stream count without a source");
|
||||
Check(!sceAvPlayerIsActive(player), "active without a source");
|
||||
Check(sceAvPlayerAddSource(player, "/app0/missing.mp4") == OperationFailed, "missing file accepted");
|
||||
Check(sceAvPlayerAddSource(player, "/app0/missing/../avplayer.mp4") == OperationFailed, "missing parent accepted");
|
||||
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4/../avplayer.mp4") == OperationFailed, "file parent accepted");
|
||||
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == 0, "add source failed");
|
||||
Check(sceAvPlayerAddSource(player, "/app0/avplayer.mp4") == OperationFailed, "second source accepted");
|
||||
Check(WaitFor([&] { return events.Seen(EventReady); }), "ready event missing");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/FileStream.hpp"
|
||||
#include "prx/libkernel/File/include/File.hpp"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cerrno>
|
||||
|
|
@ -51,6 +54,13 @@ int APS5_VABI sceKernelUnlink(const char*);
|
|||
int APS5_VABI sceKernelRename(const char*, const char*);
|
||||
int APS5_VABI sceKernelRmdir(const char*);
|
||||
int APS5_VABI sceKernelMkdir(const char*, unsigned short);
|
||||
int APS5_VABI sceKernelRename(const char*, const char*);
|
||||
int APS5_VABI sceKernelCheckReachability(const char*);
|
||||
int APS5_VABI access_nid_postfix(const char*, int);
|
||||
int APS5_VABI chdir_nid_postfix(const char*);
|
||||
void* APS5_VABI opendir_nid_postfix(const char*);
|
||||
FileStream* APS5_VABI fopen_nid_postfix(const char*, const char*);
|
||||
FileStream* APS5_VABI freopen_nid_postfix(const char*, const char*, FileStream*);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
int APS5_VABI pipe_nid_postfix(int*);
|
||||
std::int64_t APS5_VABI read_nid_postfix(int, void*, std::size_t);
|
||||
|
|
@ -76,7 +86,91 @@ static void Check(bool value, int line) {
|
|||
}
|
||||
}
|
||||
#define Require(value) Check((value), __LINE__)
|
||||
|
||||
static void TestParentTraversal() {
|
||||
const auto root = std::filesystem::path("anyps5-parent-test-" +
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
||||
Require(std::filesystem::create_directories(root / "Existing"));
|
||||
const auto present = (root / "present.txt").generic_string();
|
||||
{ std::ofstream file(present); file << "preserved"; }
|
||||
const auto directory = (root / "victim").generic_string();
|
||||
Require(std::filesystem::create_directory(directory));
|
||||
const auto checkInvalid = [&](const std::string& prefix, int error) {
|
||||
const auto path = prefix + "/present.txt";
|
||||
const auto missingLeaf = prefix + "/new.txt";
|
||||
const int kernelError = static_cast<int>(0x80020000u | static_cast<unsigned>(error));
|
||||
FileStat status{};
|
||||
Require(stat_nid_postfix(path.c_str(), &status) == -1 && *__error_nid_postfix() == error);
|
||||
Require(lstat_nid_postfix(path.c_str(), &status) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelStat(path.c_str(), &status) == kernelError);
|
||||
Require(open_nid_postfix(path.c_str(), 0, 0) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelOpen(path.c_str(), 0x601, 0600) == kernelError);
|
||||
Require(sceKernelOpen(missingLeaf.c_str(), 0x601, 0600) == kernelError);
|
||||
Require(access_nid_postfix(path.c_str(), 0) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelCheckReachability(path.c_str()) == kernelError);
|
||||
Require(chmod_nid_postfix(path.c_str(), 0400) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelChmod_nid_postfix(path.c_str(), 0400) == kernelError);
|
||||
Require(sceKernelTruncate_nid_postfix(path.c_str(), 0) == kernelError);
|
||||
Require(sceKernelUtimes_nid_postfix(path.c_str(), nullptr) == kernelError);
|
||||
Require(unlink_nid_postfix(path.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelUnlink(path.c_str()) == kernelError);
|
||||
Require(remove_nid_postfix(path.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(rename_nid_postfix(path.c_str(), missingLeaf.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(rename_nid_postfix(present.c_str(), missingLeaf.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelRename(path.c_str(), missingLeaf.c_str()) == kernelError);
|
||||
Require(sceKernelRename(present.c_str(), missingLeaf.c_str()) == kernelError);
|
||||
const auto victim = prefix + "/victim";
|
||||
Require(rmdir_nid_postfix(victim.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelRmdir(victim.c_str()) == kernelError);
|
||||
Require(remove_nid_postfix(victim.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(mkdir_nid_postfix(missingLeaf.c_str(), 0700) == -1 && *__error_nid_postfix() == error);
|
||||
Require(sceKernelMkdir(missingLeaf.c_str(), 0700) == kernelError);
|
||||
Require(chdir_nid_postfix(prefix.c_str()) == -1 && *__error_nid_postfix() == error);
|
||||
Require(opendir_nid_postfix(prefix.c_str()) == nullptr && *__error_nid_postfix() == error);
|
||||
Require(fopen_nid_postfix(path.c_str(), "wb") == nullptr && *__error_nid_postfix() == error);
|
||||
auto* stream = fopen_nid_postfix(present.c_str(), "rb");
|
||||
Require(stream != nullptr);
|
||||
const int descriptor = stream->GuestState().descriptor;
|
||||
Require(freopen_nid_postfix(path.c_str(), "wb", stream) == nullptr && *__error_nid_postfix() == error);
|
||||
Require(!DescriptorIsOpen_nid_no_patch(descriptor));
|
||||
std::ifstream file(present);
|
||||
std::string contents;
|
||||
std::getline(file, contents);
|
||||
Require(contents == "preserved" && std::filesystem::is_directory(directory));
|
||||
Require(!std::filesystem::exists(root / "new.txt"));
|
||||
};
|
||||
checkInvalid(root.generic_string() + "/missing/..", 2);
|
||||
checkInvalid(root.generic_string() + "/present.txt/..", 20);
|
||||
checkInvalid(root.generic_string() + "/present.txt/child/../..", 20);
|
||||
checkInvalid("/" + root.generic_string() + "/missing/..", 2);
|
||||
checkInvalid(root.generic_string() + "/missing/../../" + root.generic_string(), 2);
|
||||
AddPathAlias_nid_no_patch("parent-test-mount", std::filesystem::absolute(root).string().c_str());
|
||||
checkInvalid("/PARENT-TEST-MOUNT/missing/..", 2);
|
||||
checkInvalid("/parent-test-mount/present.txt/..", 20);
|
||||
FileStat status{};
|
||||
Require(sceKernelStat("/parent-test-mount/existing/../present.txt", &status) == 0 && status.st_size == 9);
|
||||
RemovePathAlias_nid_no_patch("parent-test-mount");
|
||||
std::error_code linkError;
|
||||
std::filesystem::create_directory_symlink("Existing", root / "directory-link", linkError);
|
||||
if (!linkError) {
|
||||
Require(sceKernelStat((root.generic_string() + "/directory-link/../present.txt").c_str(), &status) == 0 && status.st_size == 9);
|
||||
std::filesystem::create_symlink("present.txt", root / "file-link");
|
||||
std::filesystem::create_directory_symlink("missing", root / "dangling-link");
|
||||
checkInvalid(root.generic_string() + "/file-link/..", 20);
|
||||
checkInvalid(root.generic_string() + "/dangling-link/..", 2);
|
||||
}
|
||||
Require(chdir_nid_postfix(root.generic_string().c_str()) == 0);
|
||||
Require(sceKernelStat("missing/../present.txt", &status) == static_cast<int>(0x80020002u));
|
||||
Require(sceKernelStat("present.txt/..", &status) == static_cast<int>(0x80020014u));
|
||||
Require(sceKernelStat("EXISTING/../present.txt", &status) == 0 && status.st_size == 9);
|
||||
const int created = sceKernelOpen("Existing/../new.txt", 0x601, 0600);
|
||||
Require(created >= 0 && sceKernelClose(created) == 0);
|
||||
Require(chdir_nid_postfix("/") == 0);
|
||||
std::filesystem::remove_all(root);
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestParentTraversal();
|
||||
Require(chmod_nid_postfix(nullptr, 0600) == -1 && *__error_nid_postfix() == 14);
|
||||
Require(sceKernelChmod_nid_postfix(nullptr, 0600) == static_cast<int>(0x8002000eu));
|
||||
Require(chmod_nid_postfix("", 0600) == -1 && *__error_nid_postfix() == 2);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ int APS5_VABI sceFontSupportSystemFonts(FontLibrary);
|
|||
int APS5_VABI sceFontSupportExternalFonts(FontLibrary, std::uint32_t, std::uint32_t);
|
||||
int APS5_VABI sceFontOpenFontSet(FontLibrary, std::uint32_t, std::uint32_t, const FontOpenDetail*, FontHandle*);
|
||||
int APS5_VABI sceFontOpenFontMemory(FontLibrary, const void*, std::uint32_t, const FontOpenDetail*, FontHandle*);
|
||||
int APS5_VABI sceFontOpenFontFile(FontLibrary, const char*, std::uint32_t, const FontOpenDetail*, FontHandle*);
|
||||
int APS5_VABI sceFontCloseFont(FontHandle);
|
||||
int APS5_VABI sceFontSetScriptLanguage(FontHandle, int, int);
|
||||
int APS5_VABI sceFontGetScriptLanguage(FontHandle, int, int*);
|
||||
|
|
@ -202,6 +203,8 @@ int main() {
|
|||
Require(sceFontOpenFontMemory(library, notAFont, sizeof(notAFont), nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FUNCTION && font == nullptr);
|
||||
Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_OK);
|
||||
Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_ERROR_ALREADY_SPECIFIED);
|
||||
font = reinterpret_cast<FontHandle>(&memory);
|
||||
Require(sceFontOpenFontFile(library, "anyps5-missing-font-parent/../font.ttf", 1, nullptr, &font) == SCE_FONT_ERROR_FS_OPEN_FAILED && font == nullptr);
|
||||
Require(sceFontOpenFontMemory(library, nullptr, 0, nullptr, &font) == SCE_FONT_ERROR_INVALID_PARAMETER && font == nullptr);
|
||||
Require(sceFontOpenFontMemory(library, notAFont, sizeof(notAFont), nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FORMAT && font == nullptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,16 @@ int main() {
|
|||
Require(close_nid_postfix(descriptor) == 0);
|
||||
Require(fstatfs_nid_postfix(-1, viaPublic) == -1 && Error() == 9);
|
||||
char buffer[64];
|
||||
for (const auto& component : {std::string("missing"), std::string("file.txt")}) {
|
||||
const auto prefix = root.generic_string() + "/" + component + "/../";
|
||||
const auto invalid = prefix + "file.txt";
|
||||
const auto created = prefix + "new.txt";
|
||||
const int expected = component == "missing" ? 2 : 20;
|
||||
Require(readlink_nid_postfix(invalid.c_str(), buffer, sizeof(buffer)) == -1 && Error() == expected);
|
||||
Require(link_nid_postfix(invalid.c_str(), created.c_str()) == -1 && Error() == expected);
|
||||
Require(link_nid_postfix(file.c_str(), created.c_str()) == -1 && Error() == expected);
|
||||
Require(std::filesystem::hard_link_count(file) == 1 && !std::filesystem::exists(root / "new.txt"));
|
||||
}
|
||||
Require(readlink_nid_postfix(file.c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
||||
Require(readlink_nid_postfix(root.string().c_str(), buffer, sizeof(buffer)) == -1 && Error() == 22);
|
||||
Require(readlink_nid_postfix((root / "missing").string().c_str(), buffer, sizeof(buffer)) == -1 && Error() == 2);
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [Running application ID](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - `sceSystemServiceGetAppIdOfRunningBigApp` reports 1 as the running title's app ID. The value is AnyPS5's own: the console's shell assigns app IDs and their values are not known here, so other calls that take an app ID must use the same one. `sceSystemServiceKillApp` with that ID and the arguments -1, 0 and 0, which [SwanStationPS5](https://github.com/darkxex/SwanStationPS5/blob/4cdc981d1a64ffb7f2af73f7a27a9e91a3cb5eb4/src/main.c#L1050-L1058) passes to close itself and [ps5-payload-dev's hbldr](https://github.com/ps5-payload-dev/websrv/blob/46374edee6627885abac717de24da7a1d73af6f1/src/ps5/hbldr.c#L425-L435) passes to close the running game, ends the title as its own `exit(0)` would: the guest's shutdown and `atexit` handlers run and the process exits with status 0. On the console the call returns and the shell then ends the title, and whether those handlers run there is unknown; here there is no shell, so the call does not return. Another app ID or other arguments throw.
|
||||
- [sceNpCheckNpAvailability](../../core/libs/prx/libSceNpManager/Export.cpp) (libSceNpManager) - the `(req_id, online_id)` signature and the errors follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/0fe263a4760dfbfa973366890061749b4af0de97/src/core/libraries/np/np_manager.cpp) with no user signed in to PSN: a null online id is `SCE_NP_ERROR_INVALID_ARGUMENT`, and any other online id matches no user, `SCE_NP_ERROR_USER_NOT_FOUND`; the request id is not checked, as in the other NpManager requests
|
||||
- [Temporary app-content storage](../../core/libs/prx/libSceAppContent/Export.cpp) tracks one `/temp0` mount, preserves its files on unmount, and rejects new path resolutions until remount. Mount, format, space-query and unmount errors follow [fpPS4](https://github.com/red-prig/fpPS4/blob/04cefd43e6fddd1ab033e7980cd356d14c964905/sys/sys_path.pas); the pointer ABI is also declared by [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/AppContent/AppContent.cs). Existing open host descriptors remain usable after unmount; console behaviour with open files is unverified. Host filesystem failures still throw.
|
||||
- [Guest path resolution](../../core/libs/prx/libc/src/General.cpp) falls back to ASCII case-insensitive component lookup, following [shadPS4's host filesystem](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/file_sys/backends/host_fs.cpp). PPSA03747 requests `Skyrim.INI`, `Data/Video` and lowercase archive/plugin names that differ from the dump's filenames. Exact host names take precedence; multiple fallback matches throw instead of selecting an arbitrary file. Existing parent names are resolved when creating a new file, and guest mount aliases use the same ASCII comparison. Results are not cached, so renames and newly created files remain visible. Unicode case folding and PS5 behaviour for case-colliding host entries are unverified; host roots are used as supplied.
|
||||
- [Guest path resolution](../../core/libs/prx/libc/src/General.cpp) falls back to ASCII case-insensitive component lookup, following [shadPS4's host filesystem](https://github.com/shadps4-emu/shadPS4/blob/945dbc3cc3eee80ac3e053b438502ed936fa6bb2/src/core/file_sys/backends/host_fs.cpp). PPSA03747 requests `Skyrim.INI`, `Data/Video` and lowercase archive/plugin names that differ from the dump's filenames. Exact host names take precedence; multiple fallback matches throw instead of selecting an arbitrary file. Existing parent names are resolved when creating a new file, and guest mount aliases use the same ASCII comparison. Results are not cached, so renames and newly created files remain visible. Unicode case folding and PS5 behaviour for case-colliding host entries are unverified; host roots are used as supplied. Explicit native Windows drive and UNC paths bypass guest parent-component validation.
|
||||
- [localtime, mktime](../../core/libs/prx/libc/src/Time.cpp) (libc) on Windows convert through the CRT's `localtime_s` and `mktime`, which accept about 1970 to January 3001 UTC and apply the CRT's daylight-saving rule; outside that range `localtime` returns null and `mktime` returns -1, where the console converts. `gmtime` converts every time whose year fits an `int` on both hosts.
|
||||
- [strftime](../../core/libs/prx/libc/src/Time.cpp) (libc) expands FreeBSD's `%k`, `%l`, `%s`, `%v` and `%+` itself, ignoring a leading `E` or `O` as FreeBSD does, and passes every other conversion to the host's `strftime`. On Windows the conversions the CRT lacks (`%P` and the `%-`, `%_` and `%0` padding flags) still produce nothing, and the padding flags are not supported on the five new conversions at all (`%-k`, `%_k` and `%0k` go to the host), an unknown conversion, a doubled `E` or `O` modifier (`%EE`) or a trailing lone `%` returns 0 where FreeBSD prints `%` and the character, and a standalone `%z` or `%Z` uses the host's zone instead of the broken-down time's `tm_zone`. `%s` follows FreeBSD 9 and 11 and is the local-time `mktime` of the broken-down time (current FreeBSD uses `tm_gmtoff`), which is -1 on Windows outside the range above. `%+` is the C locale's `%a %b %e %H:%M:%S %Z %Y` with `%Z` taken from `tm_zone`, or from `tzname[tm_isdst != 0]` when it is null.
|
||||
- [LINE_STIPPLE_TEX](../../core/shader/recompiler/Translation/src/InstructionTranslator.cpp) (recompiler) reads as 0: line stipple is not emulated. RADV enables the input only as the required pixel input of a shader that reads no interpolants, because it initializes the fewest VGPRs ([Mesa](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/amd/vulkan/radv_shader.c) `radv_compute_spi_ps_input`), and never reads it
|
||||
|
|
|
|||
Loading…
Reference in New Issue