From 3a24db567f214fd2661d31ec4a75926fe0d711d1 Mon Sep 17 00:00:00 2001 From: Mike Saito Date: Sat, 11 Jul 2026 23:05:36 +0300 Subject: [PATCH] core: implement coalesced writes for gettimeofday and set POSIX EFAULT (#70) Follow-up task to enforce coalesced guest memory writes within the gettimeofday subsystem, removing remaining partial-write risks on virtual memory page boundaries. * sceKernelGettimeofday Hardening: Replaced consecutive isolated 8-byte scalar writes with a single 16-byte coalesced transaction buffer using stackalloc byte[16] and BinaryPrimitives. It preserves native Orbis semantics by returning ORBIS_GEN2_ERROR_MEMORY_FAULT on failure states without side-effect partial-writes. * POSIX gettimeofday Compliance: - Applied the identical single-transaction 16-byte write pattern for the timeval structure. - Implemented a single 8-byte coalesced zero-fill transaction for the deprecated/legacy timezone buffer (timezoneAddress != 0) using BinaryPrimitives.WriteInt32LittleEndian, aligning it with standard FreeBSD stub behavior. - Integrated proper TrySetErrno(ctx, Efault) tracking upon write failures. The method safely omits explicit manual Rax writes on error paths, allowing the import dispatcher to cleanly sign-extend the return -1 value to 0xFFFFFFFFFFFFFFFF. Out of scope: Subsystem clock and timeval validation is now fully complete; no further temporal partial-write vulnerabilities remain within the core runtime memory compat layers. Files: KernelRuntimeCompatExports.cs --- .../Kernel/KernelRuntimeCompatExports.cs | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs index 54ea069..9ffcf1c 100644 --- a/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs @@ -14,6 +14,7 @@ namespace SharpEmu.Libs.Kernel; public static class KernelRuntimeCompatExports { + private const int Efault = 14; private const ulong TlsErrnoOffset = 0x40; private const ulong TlsStackChkGuardBaseOffset = 0x800; private const ulong StackChkGuardFieldOffset = 0x10; @@ -226,8 +227,11 @@ public static class KernelRuntimeCompatExports var now = DateTimeOffset.UtcNow; var seconds = now.ToUnixTimeSeconds(); var microseconds = (now.Ticks % TimeSpan.TicksPerSecond) / 10; - if (!ctx.TryWriteUInt64(timeAddress, unchecked((ulong)seconds)) || - !ctx.TryWriteUInt64(timeAddress + sizeof(long), unchecked((ulong)microseconds))) + + Span timevalBuffer = stackalloc byte[16]; + BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer, seconds); + BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer[sizeof(long)..], microseconds); + if (!ctx.Memory.TryWrite(timeAddress, timevalBuffer)) { return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT; } @@ -249,18 +253,28 @@ public static class KernelRuntimeCompatExports var seconds = now.ToUnixTimeSeconds(); var microseconds = (now.Ticks % TimeSpan.TicksPerSecond) / 10; - if (timeAddress != 0 && - (!ctx.TryWriteUInt64(timeAddress, unchecked((ulong)seconds)) || - !ctx.TryWriteUInt64(timeAddress + sizeof(long), unchecked((ulong)microseconds)))) + if (timeAddress != 0) { - return -1; + Span timevalBuffer = stackalloc byte[16]; + BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer, seconds); + BinaryPrimitives.WriteInt64LittleEndian(timevalBuffer[sizeof(long)..], microseconds); + if (!ctx.Memory.TryWrite(timeAddress, timevalBuffer)) + { + TrySetErrno(ctx, Efault); + return -1; + } } - if (timezoneAddress != 0 && - (!ctx.TryWriteInt32(timezoneAddress, 0) || - !ctx.TryWriteInt32(timezoneAddress + sizeof(int), 0))) + if (timezoneAddress != 0) { - return -1; + Span timezoneBuffer = stackalloc byte[8]; + BinaryPrimitives.WriteInt32LittleEndian(timezoneBuffer, 0); + BinaryPrimitives.WriteInt32LittleEndian(timezoneBuffer[sizeof(int)..], 0); + if (!ctx.Memory.TryWrite(timezoneAddress, timezoneBuffer)) + { + TrySetErrno(ctx, Efault); + return -1; + } } ctx[CpuRegister.Rax] = 0;