From c969c4012d2afd17f05dcfc4e1d45871d6cbde91 Mon Sep 17 00:00:00 2001 From: Peter Kovacs Date: Mon, 27 Jul 2026 07:06:55 +0200 Subject: [PATCH 1/2] bridges/msvc_win64: pass simple argument values, not the temp's address uno2cpp.cxx converts each simple by-value argument into an 8-byte alloca temp and stores the pointer in pCppArgs[nPos]: uno_copyAndConvertData( pCppArgs[nPos] = alloca( 8 ), ... ); Copying the value into the outgoing slot therefore has to dereference that pointer. The marshalling switch used &pCppArgs[nPos] instead, which reads the pCppArgs array slot itself, i.e. the temp's address -- so every HYPER, LONG, ENUM, SHORT, CHAR, BOOLEAN, BYTE, FLOAT and DOUBLE parameter reached the callee as a stack address (or its low 16/32 bits) rather than a value. The same expression is correct in the complex/ref branch, where the pointer IS the argument; written there as (sal_uInt64)pCppArgs[nPos] to make the difference between the two branches obvious. This only affects calls that cross between the uno and cpp environments, so a pure-C++ session never trips it; it surfaces via pyuno and the invocation adapters. Co-Authored-By: Claude Opus 5 --- .../cpp_uno/msvc_win64_x86-64/uno2cpp.cxx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/main/bridges/source/cpp_uno/msvc_win64_x86-64/uno2cpp.cxx b/main/bridges/source/cpp_uno/msvc_win64_x86-64/uno2cpp.cxx index 2f8e80ca4df..b9e43adb45a 100644 --- a/main/bridges/source/cpp_uno/msvc_win64_x86-64/uno2cpp.cxx +++ b/main/bridges/source/cpp_uno/msvc_win64_x86-64/uno2cpp.cxx @@ -105,29 +105,34 @@ static void cpp_call( uno_copyAndConvertData( pCppArgs[nPos] = alloca( 8 ), pUnoArgs[nPos], pParamTypeDescr, pThis->getBridge()->getUno2Cpp() ); + // pCppArgs[nPos] is the alloca(8) temp above, i.e. a POINTER to the + // converted value -- the value is *pCppArgs[nPos]. Using + // &pCppArgs[nPos] would read the array slot instead, passing the + // temp's address as the argument. (That form is correct only in the + // complex/ref branch below, where the pointer IS the argument.) switch (pParamTypeDescr->eTypeClass) { case typelib_TypeClass_HYPER: case typelib_TypeClass_UNSIGNED_HYPER: - *pStack++ = *(sal_uInt64*)&pCppArgs[nPos]; + *pStack++ = *(sal_uInt64*)pCppArgs[nPos]; break; case typelib_TypeClass_LONG: case typelib_TypeClass_UNSIGNED_LONG: case typelib_TypeClass_ENUM: - *pStack++ = *(sal_uInt32*)&pCppArgs[nPos]; + *pStack++ = *(sal_uInt32*)pCppArgs[nPos]; break; case typelib_TypeClass_SHORT: case typelib_TypeClass_UNSIGNED_SHORT: case typelib_TypeClass_CHAR: - *pStack++ = *(sal_uInt16*)&pCppArgs[nPos]; + *pStack++ = *(sal_uInt16*)pCppArgs[nPos]; break; case typelib_TypeClass_BOOLEAN: case typelib_TypeClass_BYTE: - *pStack++ = *(sal_uInt8*)&pCppArgs[nPos]; + *pStack++ = *(sal_uInt8*)pCppArgs[nPos]; break; case typelib_TypeClass_FLOAT: case typelib_TypeClass_DOUBLE: - *pStack++ = *(sal_uInt64*)&pCppArgs[nPos]; // verbatim! + *pStack++ = *(sal_uInt64*)pCppArgs[nPos]; // verbatim! break; default: break; @@ -164,7 +169,8 @@ static void cpp_call( // no longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } - *pStack++ = *(sal_uInt64*)&pCppArgs[nPos]; + // here the POINTER is the argument (complex value passed by ref) + *pStack++ = (sal_uInt64)pCppArgs[nPos]; } } From a59e4e8aabf0d4392ae6ab7078032a4b8ca5fe95 Mon Sep 17 00:00:00 2001 From: Peter Kovacs Date: Mon, 27 Jul 2026 07:07:08 +0200 Subject: [PATCH 2/2] bridges/msvc_win64: return queryInterface's Any in the hidden return buffer The Win64 call stack is "ret addr, this, [ret *], params", so a method with a hidden return pointer finds it at pCallStack[2] -- cpp2uno_call() reads pCppReturn from [2], and the queryInterface shortcut in cpp_mediate() reads its Type argument from [3] accordingly. That shortcut nevertheless built the returned Any at pCallStack[1] and returned [1] in the register slot. [1] is `this`, so it constructed a 24-byte Any over the proxy object and never wrote the caller's own return buffer. The caller then read and destructed an uninitialised Any, faulting in uno_any_destruct() on a stale stack value used as a typelib_TypeDescription*. The x86 bridge has always used pCallStack[2] here; the Win64 port carried over the x86 index, where the layout puts the hidden pointer before `this`. Reproducer: any queryInterface on a raw uno_Interface proxy -- e.g. pyuno's Adapter::getOutIndexes() inspecting an InvocationAdapterFactory adapter, which crashed on every use of the Python macro organiser. Co-Authored-By: Claude Opus 5 --- .../bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx b/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx index 5deb06b112f..a37c37bc215 100644 --- a/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx +++ b/main/bridges/source/cpp_uno/msvc_win64_x86-64/cpp2uno.cxx @@ -301,13 +301,18 @@ extern "C" typelib_TypeClass cpp_vtable_call( if ( pInterface ) { - ::uno_any_construct( reinterpret_cast( pCallStack[1] ), + // pCallStack is "ret addr, this, [ret *], params", so the + // hidden return buffer is [2]; [1] is `this`. Writing the + // Any to [1] overwrote the proxy object and left the + // caller's return buffer untouched, so the caller then + // destructed an uninitialised Any. + ::uno_any_construct( reinterpret_cast( pCallStack[2] ), &pInterface, pTD, cpp_acquire ); pInterface->release(); TYPELIB_DANGER_RELEASE( pTD ); - reinterpret_cast( pRegisterReturn )[0] = pCallStack[1]; + reinterpret_cast( pRegisterReturn )[0] = pCallStack[2]; eRet = typelib_TypeClass_ANY; break; }