misc: Improve kernel syscall checks

Fix some stuffs around.

Based on kernel 11.14
This commit is contained in:
Mary 2023-06-17 00:32:55 +02:00
parent d0ae5f0546
commit 6d9964c64b
5 changed files with 116 additions and 38 deletions

View file

@ -104,6 +104,8 @@ private:
std::string getProcessName(u32 pid);
const char* resetTypeToString(u32 type);
int copyStringFromUser(u8 *dst, u32 src, u32 size);
MAKE_LOG_FUNCTION(log, kernelLogger)
MAKE_LOG_FUNCTION(logSVC, svcLogger)
MAKE_LOG_FUNCTION(logThread, threadLogger)

View file

@ -4,11 +4,13 @@
DEFINE_HORIZON_RESULT_MODULE(Result::OS, OS);
namespace Result::OS {
DEFINE_HORIZON_RESULT(InvalidPortName, 20, WrongArgument, Permanent);
DEFINE_HORIZON_RESULT(PortNameTooLong, 30, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(InvalidHandle, 1015, WrongArgument, Permanent);
DEFINE_HORIZON_RESULT(InvalidCombination, 1006, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(MisalignedAddress, 1009, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(MisalignedSize, 1010, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(MisalignedSize, 1010, InvalidArgument, Usage);\
DEFINE_HORIZON_RESULT(InvalidAddress, 1013, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(OutOfRange, 1021, InvalidArgument, Usage);
DEFINE_HORIZON_RESULT(Timeout, 1022, StatusChanged, Info);
};

View file

@ -70,6 +70,25 @@ void Kernel::setVersion(u8 major, u8 minor) {
mem.kernelVersion = descriptor; // The memory objects needs a copy because you can read the kernel ver from config mem
}
int Kernel::copyStringFromUser(u8 *dst, u32 src, u32 size) {
if (size == 0) {
return 0;
}
u32 i;
for (i = 0; i < size; i++) {
u8 c = mem.read8(src + i);
*dst++ = c;
if (c == 0) {
break;
}
}
return i + 1;
}
Handle Kernel::makeProcess(u32 id) {
const Handle processHandle = makeObject(KernelObjectType::Process);
const Handle resourceLimitHandle = makeObject(KernelObjectType::ResourceLimit);
@ -162,12 +181,23 @@ void Kernel::getSystemTick() {
}
// Result OutputDebugString(const char* str, s32 size)
// TODO: Does this actually write an error code in r0 and is the above signature correct?
void Kernel::outputDebugString() {
const u32 pointer = regs[0];
const u32 size = regs[1];
const s32 size = regs[1];
if (size < 0 || ~pointer < size) {
regs[0] = Result::OS::OutOfRange;
return;
}
else if (pointer >= 0x40000000) {
regs[0] = Result::OS::InvalidAddress;
return;
}
std::string message = mem.readString(pointer, size);
// TODO: Dispatch debug event
logDebugString("[OutputDebugString] %s\n", message.c_str());
regs[0] = Result::Success;
}
@ -198,6 +228,8 @@ void Kernel::getProcessInfo() {
return;
}
regs[0] = Result::Success;
switch (type) {
// According to 3DBrew: Amount of private (code, data, heap) memory used by the process + total supervisor-mode
// stack size + page-rounded size of the external handle table
@ -213,9 +245,9 @@ void Kernel::getProcessInfo() {
default:
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
regs[0] = Result::Kernel::InvalidEnumValue;
break;
}
regs[0] = Result::Success;
}
// Result DuplicateHandle(Handle* out, Handle original)
@ -231,6 +263,7 @@ void Kernel::duplicateHandle() {
regs[1] = ret;
} else {
Helpers::panic("DuplicateHandle: unimplemented handle type");
regs[0] = Result::Kernel::InvalidHandle;
}
}

View file

@ -61,8 +61,16 @@ void Kernel::controlMemory() {
if (x)
Helpers::panic("ControlMemory: attempted to allocate executable memory");
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) {
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
if (!isAligned(addr0) || !isAligned(addr1)) [[unlikely]] {
Helpers::panic("ControlMemory: Unaligned addresses\nAddr0: %08X\nAddr1: %08X\n", addr0, addr1);
regs[0] = Result::OS::MisalignedAddress;
return;
}
if (!isAligned(size)) [[unlikely]] {
Helpers::panic("ControlMemory: Unaligned size\nSize: %08X", size);
regs[0] = Result::OS::MisalignedSize;
return;
}
logSVC("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n",
@ -87,7 +95,10 @@ void Kernel::controlMemory() {
Helpers::warn("Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement this\n");
break;
default: Helpers::panic("ControlMemory: unknown operation %X\n", operation);
default:
Helpers::panic("ControlMemory: unknown operation %X\n", operation);
regs[0] = Result::OS::InvalidCombination;
return;
}
regs[0] = Result::Success;
@ -114,17 +125,37 @@ void Kernel::queryMemory() {
void Kernel::mapMemoryBlock() {
const Handle block = regs[0];
u32 addr = regs[1];
const u32 myPerms = regs[2];
const u32 otherPerms = regs[3];
logSVC("MapMemoryBlock(block = %X, addr = %08X, myPerms = %X, otherPerms = %X\n", block, addr, myPerms, otherPerms);
const u32 myPermission = regs[2];
const u32 otherPermission = regs[3];
logSVC("MapMemoryBlock(block = %X, addr = %08X, myPermission = %X, otherPermission = %X\n", block, addr, myPermission, otherPermission);
if (!isAligned(addr)) [[unlikely]] {
Helpers::panic("MapMemoryBlock: Unaligned address");
regs[0] = Result::OS::MisalignedAddress;
return;
}
if (myPermission != MemoryPermissions::Read &&
myPermission != MemoryPermissions::Write &&
myPermission != MemoryPermissions::ReadWrite) [[unlikely]] {
Helpers::panic("MapMemoryBlock: Invalid myPermission");
regs[0] = Result::OS::InvalidCombination;
return;
}
if (otherPermission != MemoryPermissions::None &&
otherPermission != MemoryPermissions::Read &&
otherPermission != MemoryPermissions::Write &&
otherPermission != MemoryPermissions::ReadWrite &&
otherPermission != MemoryPermissions::DontCare) [[unlikely]] {
Helpers::panic("MapMemoryBlock: Invalid otherPermission");
regs[0] = Result::OS::InvalidCombination;
return;
}
if (KernelHandles::isSharedMemHandle(block)) {
if (block == KernelHandles::FontSharedMemHandle && addr == 0) addr = 0x18000000;
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
u8* ptr = mem.mapSharedMemory(block, addr, myPermission, otherPermission); // Map shared memory block
// Pass pointer to shared memory to the appropriate service
switch (block) {
@ -140,10 +171,15 @@ void Kernel::mapMemoryBlock() {
std::memcpy(ptr, _shared_font_bin, _shared_font_len);
break;
default: Helpers::panic("Mapping unknown shared memory block: %X", block);
default:
Helpers::panic("Mapping unknown shared memory block: %X", block);
regs[0] = Result::OS::InvalidHandle;
return;
}
} else {
Helpers::panic("MapMemoryBlock where the handle does not refer to a known piece of kernel shared mem");
regs[0] = Result::OS::InvalidHandle;
return;
}
regs[0] = Result::Success;
@ -163,29 +199,25 @@ void Kernel::createMemoryBlock() {
u32 otherPermission = mem.read32(regs[13] + 4); // This is placed on the stack rather than r4
logSVC("CreateMemoryBlock (addr = %08X, size = %08X, myPermission = %d, otherPermission = %d)\n", addr, size, myPermission, otherPermission);
// Returns whether a permission is valid
auto isPermValid = [](u32 permission) {
switch (permission) {
case MemoryPermissions::None:
case MemoryPermissions::Read:
case MemoryPermissions::Write:
case MemoryPermissions::ReadWrite:
case MemoryPermissions::DontCare:
return true;
default: // Permissions with the executable flag enabled or invalid permissions are not allowed
return false;
}
};
// Throw error if the size of the shared memory block is not aligned to page boundary
if (!isAligned(size)) {
regs[0] = Result::OS::MisalignedSize;
return;
}
// Throw error if one of the permissions is not valid
if (!isPermValid(myPermission) || !isPermValid(otherPermission)) {
if (myPermission != MemoryPermissions::Read &&
myPermission != MemoryPermissions::Write &&
myPermission != MemoryPermissions::ReadWrite) [[unlikely]] {
Helpers::panic("MapMemoryBlock: Invalid myPermission");
regs[0] = Result::OS::InvalidCombination;
return;
}
if (otherPermission != MemoryPermissions::None &&
otherPermission != MemoryPermissions::Read &&
otherPermission != MemoryPermissions::Write &&
otherPermission != MemoryPermissions::ReadWrite) [[unlikely]] {
Helpers::panic("MapMemoryBlock: Invalid otherPermission");
regs[0] = Result::OS::InvalidCombination;
return;
}
@ -195,10 +227,6 @@ void Kernel::createMemoryBlock() {
if (addr == 0)
Helpers::panic("CreateMemoryBlock: Tried to use addr = 0");
// Implement "Don't care" permission as RW
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
if (otherPermission == MemoryPermissions::DontCare) otherPermission = MemoryPermissions::ReadWrite;
regs[0] = Result::Success;
regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission);
}

View file

@ -37,16 +37,29 @@ std::optional<Handle> Kernel::getPortHandle(const char* name) {
// Result ConnectToPort(Handle* out, const char* portName)
void Kernel::connectToPort() {
const u32 handlePointer = regs[0];
// Read up to max + 1 characters to see if the name is too long
std::string port = mem.readString(regs[1], Port::maxNameLen + 1);
logSVC("ConnectToPort(handle pointer = %X, port = \"%s\")\n", handlePointer, port.c_str());
const u32 portNameUserPtr = regs[1];
if (port.size() > Port::maxNameLen) {
const u32 PortNameSize = Port::maxNameLen + 1;
u8 portName[PortNameSize];
int portSizeRead = copyStringFromUser(portName, portNameUserPtr, PortNameSize);
if (portSizeRead < 0) {
Helpers::panic("ConnectToPort: Port name pointer was invalid\n");
regs[0] = Result::OS::InvalidPortName;
return;
} else if (portSizeRead == PortNameSize && portName[Port::maxNameLen] != '\0') {
Helpers::panic("ConnectToPort: Port name too long\n");
regs[0] = Result::OS::PortNameTooLong;
return;
}
portSizeRead = std::max(portSizeRead, 0);
std::string port(portName, portName + portSizeRead - 1);
logSVC("ConnectToPort(handle pointer = %X, port = \"%s\")\n", handlePointer, port.c_str());
// Try getting a handle to the port
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
if (!optionalHandle.has_value()) [[unlikely]] {