fix(libkernel_sys): check the linkat source before resolving the target

This commit is contained in:
Retr0 committed 2026-10-24 16:00:43 +03:00
1 parent d44d5685eb
commit 2d32e8bd85
2 files changed
+21 -7

No files matched your search

+15 -7
View File
@@ -436,17 +436,21 @@ static int FollowLinkPath(const std::filesystem::path& source, std::filesystem::
}
#endif
static int LinkNative(const std::filesystem::path& source, const std::filesystem::path& destination) {
static int LinkSourceError(const std::filesystem::path& source) {
std::error_code error;
const auto status = std::filesystem::status(source, error);
if (!std::filesystem::exists(status)) {
#ifdef _WIN32
std::filesystem::path target;
if (const int followError = FollowLinkPath(source, target)) return static_cast<int>(Fail(followError));
if (const int followError = FollowLinkPath(source, target)) return followError;
#endif
return static_cast<int>(Fail(HasNonDirectoryPrefix(source) ? GuestEnotdir : GuestEnoent));
return HasNonDirectoryPrefix(source) ? GuestEnotdir : GuestEnoent;
}
if (std::filesystem::is_directory(status)) return static_cast<int>(Fail(GuestEperm));
return std::filesystem::is_directory(status) ? GuestEperm : 0;
}
static int LinkNative(const std::filesystem::path& source, const std::filesystem::path& destination) {
std::error_code error;
if (std::filesystem::exists(std::filesystem::symlink_status(destination, error))) return static_cast<int>(Fail(GuestEexist));
if (!std::filesystem::is_directory(destination.parent_path(), error)) return static_cast<int>(Fail(HasNonDirectoryPrefix(destination) ? GuestEnotdir : GuestEnoent));
#ifdef _WIN32
@@ -469,6 +473,7 @@ int APS5_VABI link_nid_postfix(const char* existing, const char* 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));
if (const int error = LinkSourceError(source)) return static_cast<int>(Fail(error));
return LinkNative(source, destination);
}
@@ -484,12 +489,15 @@ static int ResolveAtPath(int fd, const char* path, std::filesystem::path& native
int APS5_VABI linkat_nid_postfix(int fd1, const char* path1, int fd2, const char* path2, int flag) {
if (flag & ~GuestAtSymlinkFollow) return static_cast<int>(Fail(GuestEinval));
if (path1 == nullptr || path2 == nullptr) return static_cast<int>(Fail(GuestEfault));
if (*path1 == '\0' || *path2 == '\0') return static_cast<int>(Fail(GuestEnoent));
if (path1 == nullptr) return static_cast<int>(Fail(GuestEfault));
if (*path1 == '\0') return static_cast<int>(Fail(GuestEnoent));
std::filesystem::path source, destination;
if (const int error = ResolveAtPath(fd1, path1, source)) return static_cast<int>(Fail(error));
if (const int error = ResolveAtPath(fd2, path2, destination)) return static_cast<int>(Fail(error));
if (flag == 0 && IsSymbolicLink(source)) NotImplemented_nid_no_patch("linkat: a hard link to a symbolic link itself");
if (const int error = LinkSourceError(source)) return static_cast<int>(Fail(error));
if (path2 == nullptr) return static_cast<int>(Fail(GuestEfault));
if (*path2 == '\0') return static_cast<int>(Fail(GuestEnoent));
if (const int error = ResolveAtPath(fd2, path2, destination)) return static_cast<int>(Fail(error));
return LinkNative(source, destination);
}
+6
View File
@@ -171,6 +171,12 @@ void OwnershipAndLinkTests(const std::filesystem::path& root) {
Require(linkat_nid_postfix(directory, "missing.txt", directory, "absent.txt", 0) == -1 && Error() == 2);
Require(linkat_nid_postfix(4321, "owned.txt", directory, "absent.txt", 0) == -1 && Error() == 9);
Require(linkat_nid_postfix(directory, "owned.txt", 4321, "absent.txt", 0) == -1 && Error() == 9);
Require(linkat_nid_postfix(directory, "missing.txt", 4321, "absent.txt", 0) == -1 && Error() == 2);
Require(linkat_nid_postfix(directory, "missing.txt", -100, nullptr, 0) == -1 && Error() == 2);
Require(linkat_nid_postfix(-100, root.string().c_str(), 4321, "absent.txt", 0) == -1 && Error() == 1);
Require(linkat_nid_postfix(-100, root.string().c_str(), -100, "", 0) == -1 && Error() == 1);
Require(linkat_nid_postfix(directory, "owned.txt", -100, nullptr, 0) == -1 && Error() == 14);
Require(linkat_nid_postfix(directory, "owned.txt", -100, "", 0) == -1 && Error() == 2);
const int plain = sceKernelOpen(file.c_str(), SCE_KERNEL_O_RDONLY, 0);
Require(plain >= 0);
Require(linkat_nid_postfix(plain, "owned.txt", directory, "absent.txt", 0) == -1 && Error() == 20);