1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * TestSyncAPC
4 *
5 * Copyright 2021 David Fort <contact@hardening-consulting.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19 #include <winpr/wtypes.h>
20 #include <winpr/thread.h>
21 #include <winpr/synch.h>
22
23 typedef struct
24 {
25 BOOL error;
26 BOOL called;
27 } UserApcArg;
28
userApc(ULONG_PTR arg)29 void CALLBACK userApc(ULONG_PTR arg)
30 {
31 UserApcArg* userArg = (UserApcArg*)arg;
32 userArg->called = TRUE;
33 }
34
uncleanThread(LPVOID lpThreadParameter)35 static DWORD WINAPI uncleanThread(LPVOID lpThreadParameter)
36 {
37 /* this thread post an APC that will never get executed */
38 UserApcArg* userArg = (UserApcArg*)lpThreadParameter;
39 if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)lpThreadParameter))
40 {
41 userArg->error = TRUE;
42 return 1;
43 }
44
45 return 0;
46 }
47
cleanThread(LPVOID lpThreadParameter)48 static DWORD WINAPI cleanThread(LPVOID lpThreadParameter)
49 {
50 Sleep(500);
51
52 SleepEx(500, TRUE);
53 return 0;
54 }
55
56 typedef struct
57 {
58 HANDLE timer1;
59 DWORD timer1Calls;
60 HANDLE timer2;
61 DWORD timer2Calls;
62 BOOL endTest;
63 } UncleanCloseData;
64
Timer1APCProc(LPVOID lpArg,DWORD dwTimerLowValue,DWORD dwTimerHighValue)65 static VOID CALLBACK Timer1APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
66 {
67 UncleanCloseData* data = (UncleanCloseData*)lpArg;
68 data->timer1Calls++;
69 CloseHandle(data->timer2);
70 data->endTest = TRUE;
71 }
72
Timer2APCProc(LPVOID lpArg,DWORD dwTimerLowValue,DWORD dwTimerHighValue)73 static VOID CALLBACK Timer2APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
74 {
75 UncleanCloseData* data = (UncleanCloseData*)lpArg;
76 data->timer2Calls++;
77 }
78
closeHandleTest(LPVOID lpThreadParameter)79 static DWORD /*WINAPI*/ closeHandleTest(LPVOID lpThreadParameter)
80 {
81 LARGE_INTEGER dueTime;
82 UncleanCloseData* data = (UncleanCloseData*)lpThreadParameter;
83 data->endTest = FALSE;
84
85 dueTime.QuadPart = -500;
86 if (!SetWaitableTimer(data->timer1, &dueTime, 0, Timer1APCProc, lpThreadParameter, FALSE))
87 return 1;
88
89 dueTime.QuadPart = -900;
90 if (!SetWaitableTimer(data->timer2, &dueTime, 0, Timer2APCProc, lpThreadParameter, FALSE))
91 return 1;
92
93 while (!data->endTest)
94 {
95 SleepEx(100, TRUE);
96 }
97 return 0;
98 }
99
TestSynchAPC(int argc,char * argv[])100 int TestSynchAPC(int argc, char* argv[])
101 {
102 HANDLE thread = NULL;
103 UserApcArg userApcArg;
104 UncleanCloseData uncleanCloseData;
105
106 userApcArg.error = FALSE;
107 userApcArg.called = FALSE;
108
109 WINPR_UNUSED(argc);
110 WINPR_UNUSED(argv);
111
112 /* first post an APC and check it is executed during a SleepEx */
113 if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)&userApcArg))
114 return 1;
115
116 if (SleepEx(100, FALSE) != 0)
117 return 2;
118
119 if (SleepEx(100, TRUE) != WAIT_IO_COMPLETION)
120 return 3;
121
122 if (!userApcArg.called)
123 return 4;
124
125 userApcArg.called = FALSE;
126
127 /* test that the APC is cleaned up even when not called */
128 thread = CreateThread(NULL, 0, uncleanThread, &userApcArg, 0, NULL);
129 if (!thread)
130 return 10;
131 WaitForSingleObject(thread, INFINITE);
132 CloseHandle(thread);
133
134 if (userApcArg.called || userApcArg.error)
135 return 11;
136
137 /* test a remote APC queuing */
138 thread = CreateThread(NULL, 0, cleanThread, &userApcArg, 0, NULL);
139 if (!thread)
140 return 20;
141
142 if (!QueueUserAPC((PAPCFUNC)userApc, thread, (ULONG_PTR)&userApcArg))
143 return 21;
144
145 WaitForSingleObject(thread, INFINITE);
146 CloseHandle(thread);
147
148 if (!userApcArg.called)
149 return 22;
150
151 #if 0
152 /* test cleanup of timer completions */
153 memset(&uncleanCloseData, 0, sizeof(uncleanCloseData));
154 uncleanCloseData.timer1 = CreateWaitableTimerA(NULL, FALSE, NULL);
155 if (!uncleanCloseData.timer1)
156 return 31;
157
158 uncleanCloseData.timer2 = CreateWaitableTimerA(NULL, FALSE, NULL);
159 if (!uncleanCloseData.timer2)
160 return 32;
161
162 thread = CreateThread(NULL, 0, closeHandleTest, &uncleanCloseData, 0, NULL);
163 if (!thread)
164 return 33;
165
166 WaitForSingleObject(thread, INFINITE);
167 CloseHandle(thread);
168
169 if (uncleanCloseData.timer1Calls != 1 || uncleanCloseData.timer2Calls != 0)
170 return 34;
171 CloseHandle(uncleanCloseData.timer1);
172 #endif
173 return 0;
174 }
175