mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
[Kernel] Clamp guest path traversal at the mount root (#437)
NormalizeMountRelativePath only stripped leading separators and swapped slashes; it never resolved "." or ".." segments. The bare-relative fallback in ResolveGuestPath did not even call it -- it combined the guest path against the app0 root verbatim. A guest path containing ".." therefore escaped its mount into the host filesystem. Unreal Engine titles hit this constantly: their base directory is <app>/binaries/<platform>, so they address content with "../../../" prefixes that resolve back inside /app0 on real hardware. Here those walked out of the game folder entirely -- The Invincible (PPSA06426) opened "/app0/.." and enumerated the host's Downloads directory, listing unrelated user files, and never located its own content tree. Resolve "." and ".." while walking the segments and clamp the result at the mount root, then route the bare-relative fallback through the same normalizer. This both closes the sandbox escape and makes the engine's relative content paths land where the title expects. Verified on The Invincible: no resolved host path contains ".." any more, and the title now enumerates its own content directories (content/paks, content/movies, content/locale/*) instead of an unrelated host folder. No regression on Dead Cells (PPSA15552): still reaches AGC rendering and presents frames with zero mutex errors.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
@@ -4729,7 +4729,7 @@ public static partial class KernelMemoryCompatExports
|
|||||||
!guestPath.StartsWith("/", StringComparison.Ordinal) &&
|
!guestPath.StartsWith("/", StringComparison.Ordinal) &&
|
||||||
!guestPath.StartsWith("\\", StringComparison.Ordinal))
|
!guestPath.StartsWith("\\", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var relative = guestPath.Replace('/', Path.DirectorySeparatorChar);
|
var relative = NormalizeMountRelativePath(guestPath);
|
||||||
return Path.Combine(app0Root, relative);
|
return Path.Combine(app0Root, relative);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4804,12 +4804,40 @@ public static partial class KernelMemoryCompatExports
|
|||||||
return _cachedApp0Root;
|
return _cachedApp0Root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolves "." and ".." inside a mount-relative guest path and clamps the
|
||||||
|
// result at the mount root, so a guest path can never escape into the host
|
||||||
|
// filesystem. Unreal Engine titles depend on this: their base directory is
|
||||||
|
// <app>/binaries/<platform>, so they address content with "../../../"
|
||||||
|
// prefixes that land back inside /app0 on real hardware. Combining those
|
||||||
|
// raw against the app0 root walked out of the game folder entirely, so the
|
||||||
|
// title enumerated an unrelated host directory and never found its .pak files.
|
||||||
private static string NormalizeMountRelativePath(string relativePath)
|
private static string NormalizeMountRelativePath(string relativePath)
|
||||||
{
|
{
|
||||||
return relativePath
|
var segments = relativePath.Split(
|
||||||
.TrimStart('/', '\\')
|
new[] { '/', '\\' },
|
||||||
.Replace('/', Path.DirectorySeparatorChar)
|
StringSplitOptions.RemoveEmptyEntries);
|
||||||
.Replace('\\', Path.DirectorySeparatorChar);
|
var resolved = new List<string>(segments.Length);
|
||||||
|
foreach (var segment in segments)
|
||||||
|
{
|
||||||
|
if (segment == ".")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment == "..")
|
||||||
|
{
|
||||||
|
if (resolved.Count > 0)
|
||||||
|
{
|
||||||
|
resolved.RemoveAt(resolved.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved.Add(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(Path.DirectorySeparatorChar, resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ResolveDevlogAppRoot()
|
private static string ResolveDevlogAppRoot()
|
||||||
|
|||||||
Reference in New Issue
Block a user