ldr: avoid out of cave memory with a warcrime (overwriting dead destructor)
This commit is contained in:
1 parent
69bd7e088f
commit
dbc37c6982
6 files changed
+208
No files matched your search
@@ -29,6 +29,7 @@
|
||||
#include "pcv_mariko_mtc.hpp"
|
||||
#include "pcv_mariko_soc.hpp"
|
||||
#include "pcv_mariko_log.hpp"
|
||||
#include "pcv_mariko_cave.hpp"
|
||||
#include "calculate_timings_mariko.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
@@ -39,6 +40,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData);
|
||||
|
||||
Result InstallHooks() {
|
||||
R_TRY(CaveInstall());
|
||||
|
||||
R_TRY(Hooks().CheckEnabled());
|
||||
|
||||
R_TRY(Hooks().CopyPayload());
|
||||
@@ -96,6 +99,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
{ "CPU Lut Max Asm2", &CpuLutMaxAsm2, 1, &CpuLutAsmPatternFn2 },
|
||||
{ "CPU Lut Writer", &LutWriterFind, 1, &LutWriterPatternFn },
|
||||
{ "CPU Dvco Rate Cfg", &CpuLutDvcoRateCfg, 1, &CpuLutDvcoRateCfgPatternFn },
|
||||
{ "Hook Cave", &DeadDestructorCaveFind, 0, &CaveFlagRunPatternFn, 0, 0, true },
|
||||
|
||||
/* Debugging patches */
|
||||
#if HOC_UART_LOG
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pcv_mariko_cave.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
namespace {
|
||||
|
||||
struct {
|
||||
u32 *site = nullptr;
|
||||
u32 *end = nullptr;
|
||||
} caveCache;
|
||||
|
||||
bool HasDirectCaller(const u32 *target, const u32 *begin, const u32 *end) {
|
||||
for (const u32 *ptr = begin; ptr < end; ++ptr) {
|
||||
const u32 top6 = *ptr & 0xFC000000u;
|
||||
if (top6 != _asm::op::B && top6 != _asm::op::Bl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_asm::Target26(*ptr, reinterpret_cast<uintptr_t>(ptr)) == reinterpret_cast<uintptr_t>(target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result DeadDestructorCaveFind(u32 *ptr) {
|
||||
u32 *textEnd = reinterpret_cast<u32 *>(g_pcv_cave);
|
||||
|
||||
u32 *site = _asm::FindFnPrologue(ptr, CavePrologueMargin, nsoStart);
|
||||
R_UNLESS(site != nullptr, ldr::ResultInvalidHookCave());
|
||||
|
||||
u32 *end = _asm::FindFnEnd(site, textEnd);
|
||||
R_UNLESS(end != nullptr, ldr::ResultInvalidHookCave());
|
||||
|
||||
/* Ensure this function is never called. */
|
||||
R_UNLESS(!HasDirectCaller(site, nsoStart, textEnd), ldr::ResultInvalidHookCave());
|
||||
|
||||
caveCache.site = site;
|
||||
caveCache.end = end;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CaveInstall() {
|
||||
if (Hooks().CaveFree() >= HookContext::RequiredCaveSize()) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
R_UNLESS(caveCache.site != nullptr, ldr::ResultInvalidHookCave());
|
||||
|
||||
const uintptr_t start = util::AlignUp(reinterpret_cast<uintptr_t>(caveCache.site + 1), sizeof(u64));
|
||||
const uintptr_t end = reinterpret_cast<uintptr_t>(caveCache.end);
|
||||
R_UNLESS(start < end && end - start >= HookContext::RequiredCaveSize(), ldr::ResultHookPayloadTooLarge());
|
||||
|
||||
/* Insert a break in case this function gets called for whatever reason. */
|
||||
*caveCache.site = _asm::BrkIns;
|
||||
|
||||
Hooks().SetCave(start, end - start);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../pcv.hpp"
|
||||
#include "../pcv_asm.hpp"
|
||||
#include "../pcv_hook.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
extern u32 *nsoStart;
|
||||
|
||||
constexpr u32 CaveFlagChunkCount = 4;
|
||||
constexpr u32 CaveFlagChunkInsCount = 4;
|
||||
constexpr u32 CaveFlagLdrbDelta = 0x30;
|
||||
constexpr u32 CavePrologueMargin = 8;
|
||||
|
||||
/* This function consists of multiple chunks. */
|
||||
/* Each chunk does a load, a comparison and a conditional branch. The next chunk's address setup (adrp) follows right after it. */
|
||||
inline bool IsFlagCheckChunk(const u32 *ptr) {
|
||||
return _asm::IsOp(ptr[0], _asm::op::LdrbImm, _asm::field::Rt, _asm::field::Rn, _asm::field::Off1)
|
||||
&& _asm::Ignoring(ptr[1], _asm::CmpImm32(0, 1), _asm::field::Rn)
|
||||
&& _asm::Ignoring(ptr[2], _asm::Encode(_asm::op::BCond, {_asm::field::BCond, _asm::cond::Eq}), _asm::field::Imm19);
|
||||
}
|
||||
|
||||
inline bool IsChunkDeltaValid(const u32 *prev, const u32 *next) {
|
||||
return IsFlagCheckChunk(next) && _asm::Get(*next, _asm::field::Off1) == _asm::Get(*prev, _asm::field::Off1) + CaveFlagLdrbDelta;
|
||||
}
|
||||
|
||||
inline bool ValidateChunkConsistency(const u32 *first) {
|
||||
for (u32 i = 1; i < CaveFlagChunkCount; ++i) {
|
||||
if (!IsChunkDeltaValid(first + (i - 1) * CaveFlagChunkInsCount, first + i * CaveFlagChunkInsCount)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool CaveFlagRunPatternFn(u32 *ptr) {
|
||||
if (Hooks().CaveFree() >= HookContext::RequiredCaveSize()) {
|
||||
return false;
|
||||
}
|
||||
/* In case we don't have enough cave memory, we need to commit this warcrime. */
|
||||
|
||||
/* Ensure we don't dereference invalid memory. */
|
||||
u32 *textEnd = reinterpret_cast<u32 *>(g_pcv_cave);
|
||||
if (ptr - CaveFlagChunkInsCount < nsoStart || ptr + CaveFlagChunkCount * CaveFlagChunkInsCount > textEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate the current chunk and the previous chunk and ensure the delta of the ldrb instructions is consistent. */
|
||||
if (!IsFlagCheckChunk(ptr) || IsChunkDeltaValid(ptr - CaveFlagChunkInsCount, ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ValidateChunkConsistency(ptr);
|
||||
}
|
||||
|
||||
Result DeadDestructorCaveFind(u32 *ptr);
|
||||
Result CaveInstall();
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user