diff --git a/src/TruePBR/BSLightingShaderMaterialPBR.cpp b/src/TruePBR/BSLightingShaderMaterialPBR.cpp index 6ef70c0c8c..8b0be7f3de 100644 --- a/src/TruePBR/BSLightingShaderMaterialPBR.cpp +++ b/src/TruePBR/BSLightingShaderMaterialPBR.cpp @@ -20,7 +20,10 @@ BSLightingShaderMaterialPBR* BSLightingShaderMaterialPBR::Make() RE::BSShaderMaterial* BSLightingShaderMaterialPBR::Create() { - return Make(); + // Must use regular heap (not scrap heap like Make()). BSLightingShaderProperty::LinkObject + // calls ScrapHeap::Free() after LinkMaterial — if Create() used scrap heap, it would pop + // the canonical off the stack, causing immediate use-after-free in property->material. + return new BSLightingShaderMaterialPBR(); } void BSLightingShaderMaterialPBR::CopyMembers(RE::BSShaderMaterial* that) diff --git a/src/TruePBR/BSLightingShaderMaterialPBR.h b/src/TruePBR/BSLightingShaderMaterialPBR.h index c98e338a16..b86c97e887 100644 --- a/src/TruePBR/BSLightingShaderMaterialPBR.h +++ b/src/TruePBR/BSLightingShaderMaterialPBR.h @@ -54,6 +54,10 @@ class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase ~BSLightingShaderMaterialPBR(); // override (BSLightingShaderMaterialBase) + // Called by BSShaderMaterialHashMap::Link to produce the heap-allocated canonical copy. + // MUST use regular heap (new), NOT Make()/scrap heap. BSLightingShaderProperty::LinkObject + // calls ScrapHeap::Free() immediately after Link — a scrap-heap canonical would be popped + // off the stack and freed while property->material still points to it (use-after-free). RE::BSShaderMaterial* Create() override; // 01 void CopyMembers(RE::BSShaderMaterial* that) override; // 02 std::uint32_t ComputeCRC32(uint32_t srcHash) override; // 04 @@ -64,6 +68,10 @@ class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase uint32_t GetTextures(RE::NiSourceTexture** textures) override; // 0B void LoadBinary(RE::NiStream& stream) override; // 0D + // Allocates a scrap-heap temp for use during BSLightingShaderProperty::LoadBinary. + // The temp is direct-assigned to property->material so that BSLightingShaderProperty::LinkObject + // (NiStream link phase) can find it, call BSShaderMaterialHashMap::Link to produce the canonical, + // then ScrapHeap::Free() to pop this temp. Never use Make() as the Create() implementation. static BSLightingShaderMaterialPBR* Make(); void ApplyTextureSetData(const TruePBR::PBRTextureSetData& textureSetData); diff --git a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp index 4c8490795c..ea4f76638e 100644 --- a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp +++ b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp @@ -26,7 +26,10 @@ BSLightingShaderMaterialPBRLandscape* BSLightingShaderMaterialPBRLandscape::Make RE::BSShaderMaterial* BSLightingShaderMaterialPBRLandscape::Create() { - return Make(); + // Must use regular heap (not scrap heap like Make()). BSLightingShaderProperty::LinkObject + // calls ScrapHeap::Free() after LinkMaterial — if Create() used scrap heap, it would pop + // the canonical off the stack, causing immediate use-after-free in property->material. + return new BSLightingShaderMaterialPBRLandscape(); } void BSLightingShaderMaterialPBRLandscape::CopyMembers(RE::BSShaderMaterial* that) diff --git a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h index d043650570..789d444560 100644 --- a/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h +++ b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h @@ -18,6 +18,10 @@ class BSLightingShaderMaterialPBRLandscape : public RE::BSLightingShaderMaterial ~BSLightingShaderMaterialPBRLandscape(); // override (BSLightingShaderMaterialBase) + // Called by BSShaderMaterialHashMap::Link to produce the heap-allocated canonical copy. + // MUST use regular heap (new), NOT Make()/scrap heap. BSLightingShaderProperty::LinkObject + // calls ScrapHeap::Free() immediately after Link — a scrap-heap canonical would be popped + // off the stack and freed while property->material still points to it (use-after-free). RE::BSShaderMaterial* Create() override; // 01 void CopyMembers(RE::BSShaderMaterial* that) override; // 02 Feature GetFeature() const override; // 06 @@ -25,6 +29,10 @@ class BSLightingShaderMaterialPBRLandscape : public RE::BSLightingShaderMaterial void ReceiveValuesFromRootMaterial(bool skinned, bool rimLighting, bool softLighting, bool backLighting, bool MSN) override; // 0A uint32_t GetTextures(RE::NiSourceTexture** textures) override; // 0B + // Allocates a scrap-heap temp for use during BSLightingShaderProperty::LoadBinary. + // The temp is direct-assigned to property->material so that BSLightingShaderProperty::LinkObject + // (NiStream link phase) can find it, call BSShaderMaterialHashMap::Link to produce the canonical, + // then ScrapHeap::Free() to pop this temp. Never use Make() as the Create() implementation. static BSLightingShaderMaterialPBRLandscape* Make(); bool HasGlint() const;