mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
fix(libkernel): fail the signal action, mask, stack and pending calls with EFAULT on an unusable pointer
FreeBSD 11.4's sys_sigaction, sys_sigprocmask and sys_sigaltstack copy their input in before validating it and copy the old value out after the change, and sys_sigpending copies out; a bad pointer gives EFAULT. The guest pointers were dereferenced directly. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
4e43321bd6
commit
1fbf3a42d2
4 files changed
+81
-10
No files matched your search
@@ -31,6 +31,7 @@ extern "C" int CopyGuestPath_nid_no_patch(const char* source, std::string& desti
|
||||
|
||||
extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path);
|
||||
extern "C" bool GuestCopyOut_nid_no_patch(void* destination, const void* source, std::size_t bytes);
|
||||
extern "C" bool GuestCopyIn_nid_no_patch(void* destination, const void* source, std::size_t bytes);
|
||||
extern "C" std::filesystem::path ResolvePathOrRelinkedModule_nid_no_patch(const char* path);
|
||||
extern "C" std::filesystem::path ResolveArchivedPath_nid_no_patch(const char* path, bool module = false);
|
||||
extern "C" std::filesystem::path ResolveDiskPath_nid_no_patch(const char* path);
|
||||
|
||||
@@ -837,6 +837,13 @@ extern "C" bool GuestCopyOut_nid_no_patch(void* destination, const void* source,
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" bool GuestCopyIn_nid_no_patch(void* destination, const void* source, std::size_t bytes) {
|
||||
if (bytes == 0) return true;
|
||||
const auto start = reinterpret_cast<std::uintptr_t>(source);
|
||||
if (start == 0 || bytes > UINTPTR_MAX - start) return false;
|
||||
return CopyGuestBytes(destination, source, bytes);
|
||||
}
|
||||
|
||||
extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path) {
|
||||
auto resolved = ResolveLocked(path, false, true);
|
||||
AppArchiveRefuseOnly_nid_no_patch(resolved, path, "reading");
|
||||
|
||||
@@ -542,7 +542,7 @@ thread_local GuestStack alternateStack{nullptr, 0, SsDisable};
|
||||
|
||||
namespace {
|
||||
|
||||
int ChangeBlockedMask(int how, const GuestSignalSet* set, GuestSignalSet* previousSet, bool releaseHolds, bool* caught = nullptr) {
|
||||
int ChangeBlockedMask(int how, const GuestSignalSet* set, GuestSignalSet* previousSet, bool releaseHolds, bool* caught = nullptr, GuestSignalSet* guestPrevious = nullptr) {
|
||||
if (set != nullptr && (how < 1 || how > 3)) { *__error_nid_postfix() = 22; return -1; }
|
||||
{
|
||||
std::lock_guard lock(registration);
|
||||
@@ -576,8 +576,10 @@ int ChangeBlockedMask(int how, const GuestSignalSet* set, GuestSignalSet* previo
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool faulted = guestPrevious != nullptr && !GuestCopyOut_nid_no_patch(guestPrevious, previousSet, sizeof(*previousSet));
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
if (DeliverUnblockedPending() && caught != nullptr) *caught = true;
|
||||
if (faulted) { *__error_nid_postfix() = 14; return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -603,6 +605,11 @@ GuestHandler APS5_VABI signal_nid_postfix(int guest, GuestHandler handler) {
|
||||
return previous;
|
||||
}
|
||||
int APS5_VABI sigaction_nid_postfix(int guest, const GuestSigaction* action, GuestSigaction* previous) {
|
||||
GuestSigaction requested{};
|
||||
if (action != nullptr) {
|
||||
if (!GuestCopyIn_nid_no_patch(&requested, action, sizeof(requested))) { *__error_nid_postfix() = 14; return -1; }
|
||||
action = &requested;
|
||||
}
|
||||
if (guest < 1 || guest > MaxSignal) { *__error_nid_postfix() = 22; return -1; }
|
||||
if (action && action->handler > 1 && (action->flags & ~SaDefinedFlags) != 0) { *__error_nid_postfix() = 22; return -1; }
|
||||
if (action && (guest == GuestSigkill || guest == GuestSigstop) && action->handler != 0) { *__error_nid_postfix() = 22; return -1; }
|
||||
@@ -614,7 +621,7 @@ int APS5_VABI sigaction_nid_postfix(int guest, const GuestSigaction* action, Gue
|
||||
dispositions[guest] = *action;
|
||||
DiscardPendingIfIgnored(guest, *action);
|
||||
}
|
||||
if (previous) *previous = old;
|
||||
if (previous && !GuestCopyOut_nid_no_patch(previous, &old, sizeof(old))) { *__error_nid_postfix() = 14; return -1; }
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI _sigaction_nid_postfix(int guest, const GuestSigaction* action, GuestSigaction* previous) {
|
||||
@@ -664,18 +671,27 @@ int APS5_VABI pthread_kill_nid_postfix(Pthread thread, int guest) {
|
||||
}
|
||||
int APS5_VABI sigaltstack_nid_postfix(const GuestStack* stack, GuestStack* previous) {
|
||||
GuestStack replacement = alternateStack;
|
||||
GuestStack requested{};
|
||||
if (stack != nullptr) {
|
||||
if (!GuestCopyIn_nid_no_patch(&requested, stack, sizeof(requested))) { *__error_nid_postfix() = 14; return -1; }
|
||||
stack = &requested;
|
||||
if ((stack->flags & ~SsDisable) != 0) { *__error_nid_postfix() = 22; return -1; }
|
||||
if (stack->flags & SsDisable) replacement.flags = SsDisable;
|
||||
else if (stack->size < MinSignalStackSize) { *__error_nid_postfix() = 12; return -1; }
|
||||
else replacement = {stack->sp, stack->size, 0};
|
||||
}
|
||||
if (previous != nullptr) *previous = alternateStack;
|
||||
const GuestStack old = alternateStack;
|
||||
alternateStack = replacement;
|
||||
if (previous != nullptr && !GuestCopyOut_nid_no_patch(previous, &old, sizeof(old))) { *__error_nid_postfix() = 14; return -1; }
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI _sigprocmask_nid_postfix(int how, const GuestSignalSet* set, GuestSignalSet* previousSet) {
|
||||
return ChangeBlockedMask(how, set, previousSet, true);
|
||||
GuestSignalSet requested{}, old{};
|
||||
if (set != nullptr) {
|
||||
if (!GuestCopyIn_nid_no_patch(&requested, set, sizeof(requested))) { *__error_nid_postfix() = 14; return -1; }
|
||||
set = &requested;
|
||||
}
|
||||
return ChangeBlockedMask(how, set, previousSet != nullptr ? &old : nullptr, true, nullptr, previousSet);
|
||||
}
|
||||
|
||||
int APS5_VABI sigprocmask_nid_postfix(int how, const void* set, void* previousSet) {
|
||||
@@ -684,8 +700,10 @@ int APS5_VABI sigprocmask_nid_postfix(int how, const void* set, void* previousSe
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_sigmask_nid_postfix(int how, const GuestSignalSet* set, GuestSignalSet* previousSet) {
|
||||
if (set != nullptr && (how < 1 || how > 3)) return 22;
|
||||
return _sigprocmask_nid_postfix(how, set, previousSet);
|
||||
const int saved = *__error_nid_postfix();
|
||||
const int error = _sigprocmask_nid_postfix(how, set, previousSet) == 0 ? 0 : *__error_nid_postfix();
|
||||
*__error_nid_postfix() = saved;
|
||||
return error;
|
||||
}
|
||||
|
||||
int APS5_VABI sigtimedwait_nid_postfix(const GuestSignalSet* set, void* info, const KernelTimespec* timeout) {
|
||||
@@ -745,10 +763,14 @@ int APS5_VABI _sigsuspend_nid_postfix(const GuestSignalSet* set) {
|
||||
|
||||
int APS5_VABI sigpending_nid_postfix(GuestSignalSet* set) {
|
||||
if (set == nullptr) { *__error_nid_postfix() = 14; return -1; }
|
||||
auto& pending = Pending();
|
||||
std::lock_guard lock(pending.lock);
|
||||
for (int word = 0; word < 4; ++word)
|
||||
set->bits[word] = pending.words[word].load(std::memory_order_relaxed) & BlockedWord(word);
|
||||
GuestSignalSet result{};
|
||||
{
|
||||
auto& pending = Pending();
|
||||
std::lock_guard lock(pending.lock);
|
||||
for (int word = 0; word < 4; ++word)
|
||||
result.bits[word] = pending.words[word].load(std::memory_order_relaxed) & BlockedWord(word);
|
||||
}
|
||||
if (!GuestCopyOut_nid_no_patch(set, &result, sizeof(result))) { *__error_nid_postfix() = 14; return -1; }
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +66,49 @@ void APS5_VABI MaskCallback(int value) {
|
||||
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
static void CheckSignalPointerFaults() {
|
||||
constexpr std::size_t page = 0x4000;
|
||||
void* memory = nullptr;
|
||||
Require(sceKernelMapFlexibleMemory(&memory, page, 3, 0) == 0 && memory != nullptr);
|
||||
Require(sceKernelMprotect(memory, page, 1) == 0);
|
||||
const auto readOnly = memory;
|
||||
const auto unreadable = reinterpret_cast<void*>(std::uintptr_t{0x10});
|
||||
const auto fails = [](int result) { return result == -1 && *__error_nid_postfix() == 14; };
|
||||
constexpr int user = 30;
|
||||
GuestSigaction previous{};
|
||||
Require(fails(sigaction_nid_postfix(user, static_cast<const GuestSigaction*>(unreadable), nullptr)));
|
||||
Require(fails(sigaction_nid_postfix(0, static_cast<const GuestSigaction*>(unreadable), nullptr)));
|
||||
Require(fails(sigaction_nid_postfix(user, nullptr, static_cast<GuestSigaction*>(unreadable))));
|
||||
Require(fails(sigaction_nid_postfix(user, nullptr, static_cast<GuestSigaction*>(readOnly))));
|
||||
Require(sigaction_nid_postfix(0, nullptr, &previous) == -1 && *__error_nid_postfix() == 22);
|
||||
Require(sigaction_nid_postfix(user, nullptr, &previous) == 0);
|
||||
GuestSignalSet blocked{}, set{{1u << (user - 1), 0, 0, 0}}, none{};
|
||||
Require(sigprocmask_nid_postfix(3, nullptr, &blocked) == 0);
|
||||
Require(fails(sigprocmask_nid_postfix(1, unreadable, nullptr)));
|
||||
Require(fails(sigprocmask_nid_postfix(9, unreadable, nullptr)));
|
||||
Require(sigprocmask_nid_postfix(9, &set, nullptr) == -1 && *__error_nid_postfix() == 22);
|
||||
Require(fails(sigprocmask_nid_postfix(1, nullptr, unreadable)));
|
||||
Require(fails(sigprocmask_nid_postfix(1, nullptr, readOnly)));
|
||||
Require(sigprocmask_nid_postfix(1, nullptr, &none) == 0 && std::memcmp(&none, &blocked, sizeof(none)) == 0);
|
||||
Require(fails(sigprocmask_nid_postfix(1, &set, readOnly)));
|
||||
Require(sigprocmask_nid_postfix(1, nullptr, &none) == 0 && (none.bits[0] & set.bits[0]) != 0);
|
||||
Require(sigprocmask_nid_postfix(3, &blocked, nullptr) == 0);
|
||||
Require(pthread_sigmask_nid_postfix(1, static_cast<const GuestSignalSet*>(unreadable), nullptr) == 14);
|
||||
Require(pthread_sigmask_nid_postfix(1, nullptr, static_cast<GuestSignalSet*>(readOnly)) == 14);
|
||||
Require(pthread_sigmask_nid_postfix(9, static_cast<const GuestSignalSet*>(unreadable), nullptr) == 14);
|
||||
Require(pthread_sigmask_nid_postfix(9, &set, nullptr) == 22);
|
||||
Require(sigprocmask_nid_postfix(3, &blocked, nullptr) == 0);
|
||||
Require(fails(sigaltstack_nid_postfix(static_cast<const GuestStack*>(unreadable), nullptr)));
|
||||
Require(fails(sigaltstack_nid_postfix(nullptr, static_cast<GuestStack*>(unreadable))));
|
||||
Require(fails(sigaltstack_nid_postfix(nullptr, static_cast<GuestStack*>(readOnly))));
|
||||
Require(fails(sigpending_nid_postfix(static_cast<GuestSignalSet*>(unreadable))));
|
||||
Require(fails(sigpending_nid_postfix(static_cast<GuestSignalSet*>(readOnly))));
|
||||
Require(fails(sigpending_nid_postfix(nullptr)));
|
||||
}
|
||||
|
||||
int main() {
|
||||
const auto invalid = reinterpret_cast<Handler>(static_cast<std::uintptr_t>(-1));
|
||||
CheckSignalPointerFaults();
|
||||
const auto ignore = reinterpret_cast<Handler>(std::uintptr_t{1});
|
||||
Require(signal_nid_postfix(9, Callback) == invalid);
|
||||
Require(*__error_nid_postfix() == 22);
|
||||
|
||||
Reference in new issue
Block a user