ntdll: Implement the cross-process work list functions.

This commit is contained in:
Alexandre Julliard 2023-08-28 11:32:18 +02:00
parent dbe7788817
commit 049fb065c4
4 changed files with 316 additions and 2 deletions

View file

@ -1108,6 +1108,11 @@
@ stdcall -arch=win64 RtlWow64GetThreadContext(long ptr)
@ stdcall -arch=win64 RtlWow64GetThreadSelectorEntry(long ptr long ptr)
@ stdcall RtlWow64IsWowGuestMachineSupported(long ptr)
@ stdcall -arch=win64 RtlWow64PopAllCrossProcessWorkFromWorkList(ptr ptr)
@ stdcall -arch=win64 RtlWow64PopCrossProcessWorkFromFreeList(ptr)
@ stdcall -arch=win64 RtlWow64PushCrossProcessWorkOntoFreeList(ptr ptr)
@ stdcall -arch=win64 RtlWow64PushCrossProcessWorkOntoWorkList(ptr ptr ptr)
@ stdcall -arch=win64 RtlWow64RequestCrossProcessHeavyFlush(ptr)
@ stdcall -arch=win64 RtlWow64SetThreadContext(long ptr)
@ stub RtlWriteMemoryStream
@ stdcall RtlWriteRegistryValue(long ptr wstr long ptr long)

View file

@ -302,7 +302,119 @@ done:
return STATUS_SUCCESS;
}
#endif
/**********************************************************************
* RtlWow64PopAllCrossProcessWorkFromWorkList (NTDLL.@)
*/
CROSS_PROCESS_WORK_ENTRY * WINAPI RtlWow64PopAllCrossProcessWorkFromWorkList( CROSS_PROCESS_WORK_HDR *list, BOOLEAN *flush )
{
CROSS_PROCESS_WORK_HDR prev, new;
UINT pos, prev_pos = 0;
do
{
prev.hdr = list->hdr;
if (!prev.first) break;
new.first = 0;
new.counter = prev.counter + 1;
} while (InterlockedCompareExchange64( &list->hdr, new.hdr, prev.hdr ) != prev.hdr);
*flush = (prev.first & CROSS_PROCESS_LIST_FLUSH) != 0;
if (!(pos = prev.first & ~CROSS_PROCESS_LIST_FLUSH)) return NULL;
/* reverse the list */
for (;;)
{
CROSS_PROCESS_WORK_ENTRY *entry = CROSS_PROCESS_LIST_ENTRY( list, pos );
UINT next = entry->next;
entry->next = prev_pos;
if (!next) return entry;
prev_pos = pos;
pos = next;
}
}
/**********************************************************************
* RtlWow64PopCrossProcessWorkFromFreeList (NTDLL.@)
*/
CROSS_PROCESS_WORK_ENTRY * WINAPI RtlWow64PopCrossProcessWorkFromFreeList( CROSS_PROCESS_WORK_HDR *list )
{
CROSS_PROCESS_WORK_ENTRY *ret;
CROSS_PROCESS_WORK_HDR prev, new;
do
{
prev.hdr = list->hdr;
if (!prev.first) return NULL;
ret = CROSS_PROCESS_LIST_ENTRY( list, prev.first );
new.first = ret->next;
new.counter = prev.counter + 1;
} while (InterlockedCompareExchange64( &list->hdr, new.hdr, prev.hdr ) != prev.hdr);
ret->next = 0;
return ret;
}
/**********************************************************************
* RtlWow64PushCrossProcessWorkOntoFreeList (NTDLL.@)
*/
BOOLEAN WINAPI RtlWow64PushCrossProcessWorkOntoFreeList( CROSS_PROCESS_WORK_HDR *list, CROSS_PROCESS_WORK_ENTRY *entry )
{
CROSS_PROCESS_WORK_HDR prev, new;
do
{
prev.hdr = list->hdr;
entry->next = prev.first;
new.first = (char *)entry - (char *)list;
new.counter = prev.counter + 1;
} while (InterlockedCompareExchange64( &list->hdr, new.hdr, prev.hdr ) != prev.hdr);
return TRUE;
}
/**********************************************************************
* RtlWow64PushCrossProcessWorkOntoWorkList (NTDLL.@)
*/
BOOLEAN WINAPI RtlWow64PushCrossProcessWorkOntoWorkList( CROSS_PROCESS_WORK_HDR *list, CROSS_PROCESS_WORK_ENTRY *entry, void **unknown )
{
CROSS_PROCESS_WORK_HDR prev, new;
*unknown = NULL;
do
{
prev.hdr = list->hdr;
entry->next = prev.first;
new.first = ((char *)entry - (char *)list) | (prev.first & CROSS_PROCESS_LIST_FLUSH);
new.counter = prev.counter + 1;
} while (InterlockedCompareExchange64( &list->hdr, new.hdr, prev.hdr ) != prev.hdr);
return TRUE;
}
/**********************************************************************
* RtlWow64RequestCrossProcessHeavyFlush (NTDLL.@)
*/
BOOLEAN WINAPI RtlWow64RequestCrossProcessHeavyFlush( CROSS_PROCESS_WORK_HDR *list )
{
CROSS_PROCESS_WORK_HDR prev, new;
do
{
prev.hdr = list->hdr;
new.first = prev.first | CROSS_PROCESS_LIST_FLUSH;
new.counter = prev.counter + 1;
} while (InterlockedCompareExchange64( &list->hdr, new.hdr, prev.hdr ) != prev.hdr);
return TRUE;
}
#endif /* _WIN64 */
/**********************************************************************
* RtlCreateUserProcess (NTDLL.@)

View file

@ -36,6 +36,11 @@ static NTSTATUS (WINAPI *pNtMapViewOfSectionEx)(HANDLE,HANDLE,PVOID*,const LARGE
#ifdef _WIN64
static NTSTATUS (WINAPI *pRtlWow64GetCpuAreaInfo)(WOW64_CPURESERVED*,ULONG,WOW64_CPU_AREA_INFO*);
static NTSTATUS (WINAPI *pRtlWow64GetThreadSelectorEntry)(HANDLE,THREAD_DESCRIPTOR_INFORMATION*,ULONG,ULONG*);
static CROSS_PROCESS_WORK_ENTRY * (WINAPI *pRtlWow64PopAllCrossProcessWorkFromWorkList)(CROSS_PROCESS_WORK_HDR*,BOOLEAN*);
static CROSS_PROCESS_WORK_ENTRY * (WINAPI *pRtlWow64PopCrossProcessWorkFromFreeList)(CROSS_PROCESS_WORK_HDR*);
static BOOLEAN (WINAPI *pRtlWow64PushCrossProcessWorkOntoFreeList)(CROSS_PROCESS_WORK_HDR*,CROSS_PROCESS_WORK_ENTRY*);
static BOOLEAN (WINAPI *pRtlWow64PushCrossProcessWorkOntoWorkList)(CROSS_PROCESS_WORK_HDR*,CROSS_PROCESS_WORK_ENTRY*,void**);
static BOOLEAN (WINAPI *pRtlWow64RequestCrossProcessHeavyFlush)(CROSS_PROCESS_WORK_HDR*);
#else
static NTSTATUS (WINAPI *pNtWow64AllocateVirtualMemory64)(HANDLE,ULONG64*,ULONG64,ULONG64*,ULONG,ULONG);
static NTSTATUS (WINAPI *pNtWow64GetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*);
@ -95,6 +100,11 @@ static void init(void)
#ifdef _WIN64
GET_PROC( RtlWow64GetCpuAreaInfo );
GET_PROC( RtlWow64GetThreadSelectorEntry );
GET_PROC( RtlWow64PopAllCrossProcessWorkFromWorkList );
GET_PROC( RtlWow64PopCrossProcessWorkFromFreeList );
GET_PROC( RtlWow64PushCrossProcessWorkOntoFreeList );
GET_PROC( RtlWow64PushCrossProcessWorkOntoWorkList );
GET_PROC( RtlWow64RequestCrossProcessHeavyFlush );
#else
GET_PROC( NtWow64AllocateVirtualMemory64 );
GET_PROC( NtWow64GetNativeSystemInformation );
@ -834,6 +844,142 @@ static void test_image_mappings(void)
#ifdef _WIN64
static void test_cross_process_work_list(void)
{
UINT i, next, count = 10, size = offsetof( CROSS_PROCESS_WORK_LIST, entries[count] );
BOOLEAN res, flush;
CROSS_PROCESS_WORK_ENTRY *ptr, *ret;
CROSS_PROCESS_WORK_LIST *list = calloc( size, 1 );
if (!pRtlWow64PopAllCrossProcessWorkFromWorkList)
{
win_skip( "cross process list not supported\n" );
return;
}
list = calloc( size, 1 );
for (i = 0; i < count; i++)
{
res = pRtlWow64PushCrossProcessWorkOntoFreeList( &list->free_list, &list->entries[i] );
ok( res == TRUE, "%u: RtlWow64PushCrossProcessWorkOntoFreeList failed\n", i );
}
ok( list->free_list.counter == count, "wrong counter %u\n", list->free_list.counter );
ok( CROSS_PROCESS_LIST_ENTRY( &list->free_list, list->free_list.first ) == &list->entries[count - 1],
"wrong offset %u\n", list->free_list.first );
for (i = count; i > 1; i--)
ok( CROSS_PROCESS_LIST_ENTRY( &list->free_list, list->entries[i - 1].next ) == &list->entries[i - 2],
"%u: wrong offset %x / %x\n", i, list->entries[i - 1].next,
(UINT)((char *)&list->entries[i - 2] - (char *)&list->free_list) );
ok( !list->entries[0].next, "wrong last offset %x\n", list->entries[0].next );
next = list->entries[count - 1].next;
ptr = pRtlWow64PopCrossProcessWorkFromFreeList( &list->free_list );
ok( ptr == (void *)&list->entries[count - 1], "wrong ptr %p (%p)\n", ptr, list );
ok( !ptr->next, "next not reset %x\n", ptr->next );
ok( list->free_list.first == next, "wrong offset %x / %x\n", list->free_list.first, next );
ok( list->free_list.counter == count + 1, "wrong counter %u\n", list->free_list.counter );
ptr->next = 0xdead;
ptr->id = 3;
ptr->addr = 0xdeadbeef;
ptr->size = 0x1000;
ptr->args[0] = 7;
ret = (void *)0xdeadbeef;
res = pRtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, ptr, (void **)&ret );
ok( res == TRUE, "RtlWow64PushCrossProcessWorkOntoWorkList failed\n" );
ok( !ret, "got ret ptr %p\n", ret );
ok( list->work_list.counter == 1, "wrong counter %u\n", list->work_list.counter );
ok( ptr == CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first), "wrong ptr %p / %p\n",
ptr, CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first ));
ok( !ptr->next, "got next %x\n", ptr->next );
next = list->work_list.first;
ptr = pRtlWow64PopCrossProcessWorkFromFreeList( &list->free_list );
ok( list->free_list.counter == count + 2, "wrong counter %u\n", list->free_list.counter );
ptr->id = 20;
ptr->addr = 0x123456;
ptr->size = 0x2345;
res = pRtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, ptr, (void **)&ret );
ok( res == TRUE, "RtlWow64PushCrossProcessWorkOntoWorkList failed\n" );
ok( !ret, "got ret ptr %p\n", ret );
ok( list->work_list.counter == 2, "wrong counter %u\n", list->work_list.counter );
ok( list->work_list.first == (char *)ptr - (char *)&list->work_list, "wrong ptr %p / %p\n",
ptr, (char *)list + list->work_list.first );
ok( ptr->next == next, "got wrong next %x / %x\n", ptr->next, next );
flush = 0xcc;
ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush );
ok( !flush, "RtlWow64PopAllCrossProcessWorkFromWorkList flush is TRUE\n" );
ok( list->work_list.counter == 3, "wrong counter %u\n", list->work_list.counter );
ok( !list->work_list.first, "list not empty %x\n", list->work_list.first );
ok( ptr->addr == 0xdeadbeef, "wrong addr %s\n", wine_dbgstr_longlong(ptr->addr) );
ok( ptr->size == 0x1000, "wrong size %s\n", wine_dbgstr_longlong(ptr->size) );
ok( ptr->next, "next not set\n" );
ptr = CROSS_PROCESS_LIST_ENTRY( &list->work_list, ptr->next );
ok( ptr->addr == 0x123456, "wrong addr %s\n", wine_dbgstr_longlong(ptr->addr) );
ok( ptr->size == 0x2345, "wrong size %s\n", wine_dbgstr_longlong(ptr->size) );
ok( !ptr->next, "list not terminated\n" );
res = pRtlWow64PushCrossProcessWorkOntoWorkList( &list->work_list, ptr, (void **)&ret );
ok( res == TRUE, "RtlWow64PushCrossProcessWorkOntoWorkList failed\n" );
ok( !ret, "got ret ptr %p\n", ret );
ok( list->work_list.counter == 4, "wrong counter %u\n", list->work_list.counter );
res = pRtlWow64RequestCrossProcessHeavyFlush( &list->work_list );
ok( res == TRUE, "RtlWow64RequestCrossProcessHeavyFlush failed\n" );
ok( list->work_list.counter == 5, "wrong counter %u\n", list->work_list.counter );
ok( list->work_list.first & CROSS_PROCESS_LIST_FLUSH, "flush flag not set %x\n", list->work_list.first );
ok( ptr == CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first), "wrong ptr %p / %p\n",
ptr, CROSS_PROCESS_LIST_ENTRY( &list->work_list, list->work_list.first ));
flush = 0xcc;
ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush );
ok( flush == TRUE, "RtlWow64PopAllCrossProcessWorkFromWorkList flush not set\n" );
ok( list->work_list.counter == 6, "wrong counter %u\n", list->work_list.counter );
ok( !list->work_list.first, "list not empty %x\n", list->work_list.first );
ok( ptr->addr == 0x123456, "wrong addr %s\n", wine_dbgstr_longlong(ptr->addr) );
ok( ptr->size == 0x2345, "wrong size %s\n", wine_dbgstr_longlong(ptr->size) );
ok( !ptr->next, "next not set\n" );
flush = 0xcc;
ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush );
ok( flush == FALSE, "RtlWow64PopAllCrossProcessWorkFromWorkList flush set\n" );
ok( list->work_list.counter == 6, "wrong counter %u\n", list->work_list.counter );
ok( !list->work_list.first, "list not empty %x\n", list->work_list.first );
ok( !ptr, "got ptr %p\n", ptr );
res = pRtlWow64RequestCrossProcessHeavyFlush( &list->work_list );
ok( res == TRUE, "RtlWow64RequestCrossProcessHeavyFlush failed\n" );
ok( list->work_list.counter == 7, "wrong counter %u\n", list->work_list.counter );
ok( list->work_list.first & CROSS_PROCESS_LIST_FLUSH, "flush flag not set %x\n", list->work_list.first );
res = pRtlWow64RequestCrossProcessHeavyFlush( &list->work_list );
ok( res == TRUE, "RtlWow64RequestCrossProcessHeavyFlush failed\n" );
ok( list->work_list.counter == 8, "wrong counter %u\n", list->work_list.counter );
ok( list->work_list.first & CROSS_PROCESS_LIST_FLUSH, "flush flag not set %x\n", list->work_list.first );
flush = 0xcc;
ptr = pRtlWow64PopAllCrossProcessWorkFromWorkList( &list->work_list, &flush );
ok( flush == TRUE, "RtlWow64PopAllCrossProcessWorkFromWorkList flush set\n" );
ok( list->work_list.counter == 9, "wrong counter %u\n", list->work_list.counter );
ok( !list->work_list.first, "list not empty %x\n", list->work_list.first );
ok( !ptr, "got ptr %p\n", ptr );
for (i = 0; i < count; i++)
{
ptr = pRtlWow64PopCrossProcessWorkFromFreeList( &list->free_list );
if (!ptr) break;
ok( list->free_list.counter == count + 3 + i, "wrong counter %u\n", list->free_list.counter );
}
ok( list->free_list.counter == count + 2 + i, "wrong counter %u\n", list->free_list.counter );
ok( !list->free_list.first, "first still set %x\n", list->free_list.first );
free( list );
}
static void test_cpu_area(void)
{
if (pRtlWow64GetCpuAreaInfo)
@ -1634,7 +1780,9 @@ START_TEST(wow64)
test_peb_teb();
test_selectors();
test_image_mappings();
#ifndef _WIN64
#ifdef _WIN64
test_cross_process_work_list();
#else
test_nt_wow64();
test_modules();
test_init_block();

View file

@ -4117,6 +4117,50 @@ C_ASSERT( sizeof(WOW64INFO) == 40 );
#define WOW64_CPUFLAGS_MSFT64 0x01
#define WOW64_CPUFLAGS_SOFTWARE 0x02
/* undocumented layout of WOW64INFO.CrossProcessWorkList */
typedef struct
{
UINT next;
UINT id;
ULONGLONG addr;
ULONGLONG size;
UINT args[4];
} CROSS_PROCESS_WORK_ENTRY;
typedef union
{
struct
{
UINT first;
UINT counter;
};
volatile LONGLONG hdr;
} CROSS_PROCESS_WORK_HDR;
typedef struct
{
CROSS_PROCESS_WORK_HDR free_list;
CROSS_PROCESS_WORK_HDR work_list;
ULONGLONG unknown[4];
CROSS_PROCESS_WORK_ENTRY entries[1];
} CROSS_PROCESS_WORK_LIST;
typedef enum
{
CrossProcessPreVirtualAlloc = 0,
CrossProcessPostVirtualAlloc = 1,
CrossProcessPreVirtualFree = 2,
CrossProcessPostVirtualFree = 3,
CrossProcessPreVirtualProtect = 4,
CrossProcessPostVirtualProtect = 5,
CrossProcessFlushCache = 6,
} CROSS_PROCESS_NOTIFICATION;
#define CROSS_PROCESS_LIST_FLUSH 0x80000000
#define CROSS_PROCESS_LIST_ENTRY(list,pos) \
((CROSS_PROCESS_WORK_ENTRY *)((char *)(list) + ((pos) & ~CROSS_PROCESS_LIST_FLUSH)))
/* wow64.dll functions */
void * WINAPI Wow64AllocateTemp(SIZE_T);
void WINAPI Wow64ApcRoutine(ULONG_PTR,ULONG_PTR,ULONG_PTR,CONTEXT*);
@ -4977,6 +5021,11 @@ NTSYSAPI NTSTATUS WINAPI RtlWow64GetCpuAreaInfo(WOW64_CPURESERVED*,ULONG,WOW64_
NTSYSAPI NTSTATUS WINAPI RtlWow64GetCurrentCpuArea(USHORT*,void**,void**);
NTSYSAPI NTSTATUS WINAPI RtlWow64GetThreadContext(HANDLE,WOW64_CONTEXT*);
NTSYSAPI NTSTATUS WINAPI RtlWow64GetThreadSelectorEntry(HANDLE,THREAD_DESCRIPTOR_INFORMATION*,ULONG,ULONG*);
NTSYSAPI CROSS_PROCESS_WORK_ENTRY * WINAPI RtlWow64PopAllCrossProcessWorkFromWorkList(CROSS_PROCESS_WORK_HDR*,BOOLEAN*);
NTSYSAPI CROSS_PROCESS_WORK_ENTRY * WINAPI RtlWow64PopCrossProcessWorkFromFreeList(CROSS_PROCESS_WORK_HDR*);
NTSYSAPI BOOLEAN WINAPI RtlWow64PushCrossProcessWorkOntoFreeList(CROSS_PROCESS_WORK_HDR*,CROSS_PROCESS_WORK_ENTRY*);
NTSYSAPI BOOLEAN WINAPI RtlWow64PushCrossProcessWorkOntoWorkList(CROSS_PROCESS_WORK_HDR*,CROSS_PROCESS_WORK_ENTRY*,void**);
NTSYSAPI BOOLEAN WINAPI RtlWow64RequestCrossProcessHeavyFlush(CROSS_PROCESS_WORK_HDR*);
NTSYSAPI NTSTATUS WINAPI RtlWow64SetThreadContext(HANDLE,const WOW64_CONTEXT*);
#else
NTSYSAPI NTSTATUS WINAPI NtWow64AllocateVirtualMemory64(HANDLE,ULONG64*,ULONG64,ULONG64*,ULONG,ULONG);