This is an automated email from the ASF dual-hosted git repository.

leginee pushed a commit to branch win10-msvc-trunk
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit a3156910b1af41bd7af5f541f99fcb655fd494ab
Author: Peter Kovacs <[email protected]>
AuthorDate: Fri Aug 21 20:01:43 2026 +0200

    climaker: do not let TypeResolve re-enter itself
    
        Process is terminated due to StackOverflowException.
    
    climaker compiled and ran --help after the C++/CLI port, then died emitting
    cli_uretypes.dll.  A managed stack showed 164 nested 
TypeEmitter::type_resolve
    frames on unoidl.com.sun.star.container.XElementAccess.
    
    AppDomain::TypeResolve is not re-entrancy-guarded.  type_resolve began by
    asking the ModuleBuilder for the very name it had just been asked about, and
    when that name cannot be resolved the runtime raises TypeResolve again, for
    the same name, straight back into the handler.
    
    It only bites the SECOND time an interface is looked up: the first pass
    DefineType's it, and the recursion needs a type that has been defined but 
not
    yet created.  Tracing shows exactly that -- XElementAccess resolved, defined
    and added to m_incomplete_ifaces on the way through XEnumerationAccess, then
    asked for again as a base of XIdentifierAccess, and never returning.
    
    The code already knew about this state.  get_type carries the table lookup
    COMMENTED OUT, under
    
        //We get the type from the ModuleBuilder even if the type is not 
complete
        //but have been defined.
    
    which was true of the .NET this was written against and is not true of .NET 
4,
    where ModuleBuilder::GetType returns null for a TypeBuilder that has not 
been
    CreateType'd.  VC9 targeted 2.0/3.5; the v142 toolset targets 4.x.  That
    runtime change is the difference, not the syntax port.
    
    So: consult m_incomplete_ifaces BEFORE the ModuleBuilder, in both get_type 
and
    type_resolve, and keep a set of names currently being answered so a handler
    that is re-entered for one of them returns nullptr -- "I cannot help", which
    is true, and ends the chain rather than extending it.
    
    climaker now exits 0 and emits a 108KB cli_uretypes.dll.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01W7pjcp2sXU1HaUwXT7kc29
---
 main/cli_ure/source/climaker/climaker_emit.cxx | 70 +++++++++++++++++++++-----
 main/cli_ure/source/climaker/climaker_share.h  |  3 ++
 2 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/main/cli_ure/source/climaker/climaker_emit.cxx 
b/main/cli_ure/source/climaker/climaker_emit.cxx
index db66f769c4..72ff713839 100644
--- a/main/cli_ure/source/climaker/climaker_emit.cxx
+++ b/main/cli_ure/source/climaker/climaker_emit.cxx
@@ -284,15 +284,43 @@ Assembly ^ TypeEmitter::type_resolve(
        ::System::Object ^, ::System::ResolveEventArgs ^ args )
 {
        ::System::String ^ cts_name = args->Name;
-       ::System::Type ^ ret_type = m_module_builder->GetType(
-               cts_name, false /* no exc */ );
-       if (nullptr == ret_type)
+
+       // Two things have to happen before anything else, and both exist 
because
+       // AppDomain::TypeResolve is not re-entrancy-guarded: asking the
+       // ModuleBuilder for a name it cannot resolve raises TypeResolve again, 
for
+       // the same name, and lands straight back here.
+       //
+       // First: refuse to answer a name we are already answering.  Returning
+       // nullptr tells the runtime we cannot help, which is true, and ends the
+       // chain instead of extending it.
+       if (m_resolving->ContainsKey( cts_name ))
+               return nullptr;
+
+       // Second: consult the incomplete-interface table BEFORE the 
ModuleBuilder.
+       // A type that has been DefineType'd but not yet CreateType'd is in that
+       // table, and on .NET 4 ModuleBuilder::GetType does not return it -- so
+       // asking the ModuleBuilder first is both slower and the thing that
+       // recurses.
+       ::System::Type ^ ret_type = nullptr;
        {
                iface_entry ^ entry = dynamic_cast< iface_entry ^ >(
                        m_incomplete_ifaces[ cts_name ] );
                if (nullptr != entry)
                        ret_type = entry->m_type_builder;
        }
+
+       if (nullptr == ret_type)
+       {
+               m_resolving->Add( cts_name, cts_name );
+               try
+               {
+                       ret_type = m_module_builder->GetType( cts_name, false 
/* no exc */ );
+               }
+               finally
+               {
+                       m_resolving->Remove( cts_name );
+               }
+       }
        if (nullptr == ret_type)
        {
                sal_Int32 len = m_extra_assemblies->Length;
@@ -322,15 +350,32 @@ Assembly ^ TypeEmitter::type_resolve(
        ::System::String ^ cts_name, bool throw_exc )
 {
        ::System::Type ^ ret_type = m_module_builder->GetType( cts_name, false 
);
-       //We get the type from the ModuleBuilder even if the type is not 
complete
-       //but have been defined.
-       //if (ret_type == 0)
-       //{
-       //      iface_entry ^ entry = dynamic_cast< iface_entry ^ >(
-       //              m_incomplete_ifaces[ cts_name ] );
-       //      if (nullptr != entry)
-       //              ret_type = entry->m_type_builder;
-       //}
+
+       // The lines below were commented out with the note "We get the type 
from
+       // the ModuleBuilder even if the type is not complete but have been
+       // defined."  That was true of the .NET this was written against; it is 
not
+       // true of .NET 4, where ModuleBuilder::GetType returns null for a type 
that
+       // has been DefineType'd but not yet CreateType'd.
+       //
+       // Leaving them commented out is not merely a missed lookup -- it does 
not
+       // terminate.  The caller falls through to Type::GetType, which raises
+       // AppDomain::TypeResolve, whose handler is type_resolve, which asks
+       // ModuleBuilder::GetType for the same name, which raises TypeResolve
+       // again.  AppDomain::TypeResolve has no re-entrancy guard, so climaker
+       // dies with a StackOverflowException the second time any interface is
+       // looked up -- the first time defines it, the second recurses:
+       //
+       //     Process is terminated due to StackOverflowException.
+       //
+       // The incomplete-interface table is exactly the right answer here, 
which
+       // is presumably why it was written in the first place.
+       if (ret_type == nullptr)
+       {
+               iface_entry ^ entry = dynamic_cast< iface_entry ^ >(
+                       m_incomplete_ifaces[ cts_name ] );
+               if (nullptr != entry)
+                       ret_type = entry->m_type_builder;
+       }
                //try the cli_basetypes assembly
        if (ret_type == nullptr)
        {
@@ -2234,6 +2279,7 @@ TypeEmitter::TypeEmitter(
          m_type_Exception( nullptr ),
          m_type_RuntimeException( nullptr ),
          m_incomplete_ifaces( gcnew ::System::Collections::Hashtable() ),
+         m_resolving( gcnew ::System::Collections::Hashtable() ),
          m_incomplete_structs( gcnew ::System::Collections::Hashtable() ),
          m_incomplete_services(gcnew ::System::Collections::Hashtable() ),
          m_incomplete_singletons(gcnew ::System::Collections::Hashtable() ),
diff --git a/main/cli_ure/source/climaker/climaker_share.h 
b/main/cli_ure/source/climaker/climaker_share.h
index df2447c940..68e21da01c 100644
--- a/main/cli_ure/source/climaker/climaker_share.h
+++ b/main/cli_ure/source/climaker/climaker_share.h
@@ -169,6 +169,9 @@ ref class TypeEmitter : public ::System::IDisposable
         ::System::Reflection::Emit::TypeBuilder ^ m_type_builder;
     };
     ::System::Collections::Hashtable ^ m_incomplete_ifaces;
+    // Names type_resolve is part-way through answering.  AppDomain's
+    // TypeResolve event has no re-entrancy guard of its own.
+    ::System::Collections::Hashtable ^ m_resolving;
     ::System::Type ^ complete_iface_type( iface_entry ^ entry );
 
     ref class struct_entry

Reply via email to