ReactOS 0.4.17-dev-736-g75e91de
JobObject.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Tests for the job object UI restrictions
5 * COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
6 */
7
8#include "precomp.h"
9
10/*
11 * Chromium 109, sandbox/win/src/job.cc. The JobLevel cases there fall through
12 * into each other, so each level takes the flags of the ones below it too:
13 * kLockdown adds the four here to kLimitedUser's, which comes to all eight.
14 */
15#define JOB_LIMITEDUSER_UI (JOB_OBJECT_UILIMIT_DISPLAYSETTINGS | \
16 JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS| \
17 JOB_OBJECT_UILIMIT_DESKTOP | \
18 JOB_OBJECT_UILIMIT_EXITWINDOWS)
19
20#define JOB_LOCKDOWN_UI (JOB_LIMITEDUSER_UI | \
21 JOB_OBJECT_UILIMIT_WRITECLIPBOARD | \
22 JOB_OBJECT_UILIMIT_READCLIPBOARD | \
23 JOB_OBJECT_UILIMIT_HANDLES | \
24 JOB_OBJECT_UILIMIT_GLOBALATOMS)
25
26static const ULONG SingleRestrictions[] =
27{
36};
37
38static
39BOOL
41 _In_ HANDLE hJob,
42 _In_ ULONG Restrictions)
43{
45
46 Info.UIRestrictionsClass = Restrictions;
47 return SetInformationJobObject(hJob,
49 &Info,
50 sizeof(Info));
51}
52
53static
54void
56{
58 HANDLE hJob;
61 ULONG i;
62
63 hJob = CreateJobObjectW(NULL, NULL);
64 ok(hJob != NULL, "CreateJobObject failed with %lu\n", GetLastError());
65 if (hJob == NULL)
66 return;
67
68 /* A fresh job restricts nothing */
69 memset(&Info, 0xAA, sizeof(Info));
70 Returned = 0;
71 SetLastError(0xDEADBEEF);
74 &Info,
75 sizeof(Info),
76 &Returned);
77 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
78 ok_long(Info.UIRestrictionsClass, 0);
79 ok_long(Returned, sizeof(Info));
80
81 /* Every flag on its own, so one mistake cannot hide behind the others */
82 for (i = 0; i < _countof(SingleRestrictions); i++)
83 {
84 SetLastError(0xDEADBEEF);
86 ok(Success != FALSE, "Setting 0x%lx failed with %lu\n",
88 if (Success == FALSE)
89 {
90 /* Everything below only means something once it is set */
91 skip("0x%lx could not be set\n", SingleRestrictions[i]);
92 continue;
93 }
94
95 memset(&Info, 0xAA, sizeof(Info));
96 Returned = 0;
99 &Info,
100 sizeof(Info),
101 &Returned);
102 ok(Success != FALSE, "Querying 0x%lx failed with %lu\n",
104 if (Success == FALSE)
105 {
106 skip("0x%lx could not be queried\n", SingleRestrictions[i]);
107 }
108 else
109 {
110 ok_long(Info.UIRestrictionsClass, SingleRestrictions[i]);
111 ok_long(Returned, sizeof(Info));
112 }
113
114 /* Back to nothing, which drops the per-job state win32k keeps */
115 SetLastError(0xDEADBEEF);
116 Success = SetRestrictions(hJob, 0);
117 ok(Success != FALSE, "Clearing 0x%lx failed with %lu\n",
119 if (Success == FALSE)
120 {
121 /* The job stays restricted, so the round after this one would be
122 starting from the wrong place */
123 skip("0x%lx could not be cleared\n", SingleRestrictions[i]);
124 continue;
125 }
126
127 memset(&Info, 0xAA, sizeof(Info));
130 &Info,
131 sizeof(Info),
132 NULL);
133 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
134 if (Success != FALSE)
135 ok_long(Info.UIRestrictionsClass, 0);
136 }
137
138 /* All of them at once */
139 SetLastError(0xDEADBEEF);
141 ok(Success != FALSE, "Setting JOB_OBJECT_UILIMIT_ALL failed with %lu\n", GetLastError());
142
143 memset(&Info, 0xAA, sizeof(Info));
146 &Info,
147 sizeof(Info),
148 NULL);
149 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
150 ok_long(Info.UIRestrictionsClass, JOB_OBJECT_UILIMIT_ALL);
151
152 /* Setting the same value twice is not an error */
153 SetLastError(0xDEADBEEF);
155 ok(Success != FALSE, "Setting the same restrictions again failed with %lu\n",
156 GetLastError());
157
158 /* Close it while still restricted, to exercise the delete path */
159 CloseHandle(hJob);
160}
161
162static
163void
165{
167 HANDLE hJob;
170
171 hJob = CreateJobObjectW(NULL, NULL);
172 ok(hJob != NULL, "CreateJobObject failed with %lu\n", GetLastError());
173 if (hJob == NULL)
174 return;
175
176 /* Undefined bits must be rejected, and must not be stored */
177 SetLastError(0xDEADBEEF);
178 Success = SetRestrictions(hJob, 0xDEAD0000);
179 ok(Success == FALSE, "Setting undefined restrictions succeeded\n");
181
182 SetLastError(0xDEADBEEF);
184 ok(Success == FALSE, "Setting one undefined bit succeeded\n");
186
187 memset(&Info, 0xAA, sizeof(Info));
190 &Info,
191 sizeof(Info),
192 NULL);
193 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
194 ok_long(Info.UIRestrictionsClass, 0);
195
196 /* The class is fixed length in both directions */
197 Info.UIRestrictionsClass = JOB_OBJECT_UILIMIT_HANDLES;
198 SetLastError(0xDEADBEEF);
201 &Info,
202 sizeof(Info) - 1);
203 ok(Success == FALSE, "SetInformationJobObject with a short buffer succeeded\n");
205
206 SetLastError(0xDEADBEEF);
209 &Info,
210 sizeof(Info) + 1);
211 ok(Success == FALSE, "SetInformationJobObject with a long buffer succeeded\n");
213
214 Returned = 0;
215 SetLastError(0xDEADBEEF);
218 &Info,
219 sizeof(Info) - 1,
220 &Returned);
221 ok(Success == FALSE, "QueryInformationJobObject with a short buffer succeeded\n");
223
224 /* An inaccessible buffer must be reported, not raised */
225 SetLastError(0xDEADBEEF);
228 NULL,
229 sizeof(Info));
230 ok(Success == FALSE, "SetInformationJobObject with a NULL buffer succeeded\n");
232
233 CloseHandle(hJob);
234}
235
236/* A handle without JOB_OBJECT_SET_ATTRIBUTES may not change the restrictions */
237static
238void
240{
242 HANDLE hJob, hQueryOnly;
244
245 hJob = CreateJobObjectW(NULL, NULL);
246 ok(hJob != NULL, "CreateJobObject failed with %lu\n", GetLastError());
247 if (hJob == NULL)
248 return;
249
250 hQueryOnly = NULL;
252 hJob,
254 &hQueryOnly,
256 FALSE,
257 0);
258 ok(Success != FALSE, "DuplicateHandle failed with %lu\n", GetLastError());
259 if (Success)
260 {
261 SetLastError(0xDEADBEEF);
263 ok(Success == FALSE, "Setting restrictions through a query handle succeeded\n");
265
266 memset(&Info, 0xAA, sizeof(Info));
269 &Info,
270 sizeof(Info),
271 NULL);
272 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
273 ok_long(Info.UIRestrictionsClass, 0);
274
275 CloseHandle(hQueryOnly);
276 }
277
278 CloseHandle(hJob);
279}
280
281/*
282 * The two policies the Chromium sandbox builds, in the order it builds them.
283 * The GPU one excepts every UI restriction away again, which has to end up
284 * clearing them rather than failing.
285 */
286static
287void
289{
292 HANDLE hJob;
294
295 /* sandbox::Job::Init(JOB_LOCKDOWN) */
296 hJob = CreateJobObjectW(NULL, NULL);
297 ok(hJob != NULL, "CreateJobObject failed with %lu\n", GetLastError());
298 if (hJob == NULL)
299 return;
300
301 memset(&ExtendedLimit, 0, sizeof(ExtendedLimit));
302 ExtendedLimit.BasicLimitInformation.LimitFlags =
307
308 SetLastError(0xDEADBEEF);
311 &ExtendedLimit,
312 sizeof(ExtendedLimit));
313 ok(Success != FALSE, "Setting the lockdown limits failed with %lu\n", GetLastError());
314
315 SetLastError(0xDEADBEEF);
317 ok(Success != FALSE, "Setting the lockdown restrictions failed with %lu\n",
318 GetLastError());
319
320 memset(&Info, 0xAA, sizeof(Info));
323 &Info,
324 sizeof(Info),
325 NULL);
326 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
327 ok_long(Info.UIRestrictionsClass, JOB_LOCKDOWN_UI);
328
329 CloseHandle(hJob);
330
331 /* Job::Init(JobLevel::kLimitedUser), which does not take kLockdown's four */
332 hJob = CreateJobObjectW(NULL, NULL);
333 ok(hJob != NULL, "CreateJobObject failed with %lu\n", GetLastError());
334 if (hJob == NULL)
335 return;
336
337 memset(&ExtendedLimit, 0, sizeof(ExtendedLimit));
338 ExtendedLimit.BasicLimitInformation.LimitFlags =
342
343 SetLastError(0xDEADBEEF);
346 &ExtendedLimit,
347 sizeof(ExtendedLimit));
348 ok(Success != FALSE, "Setting the limited user limits failed with %lu\n", GetLastError());
349
350 SetLastError(0xDEADBEEF);
352 ok(Success != FALSE, "Setting the limited user restrictions failed with %lu\n",
353 GetLastError());
354
355 memset(&Info, 0xAA, sizeof(Info));
358 &Info,
359 sizeof(Info),
360 NULL);
361 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
362 ok_long(Info.UIRestrictionsClass, JOB_LIMITEDUSER_UI);
363
364 CloseHandle(hJob);
365
366 /*
367 * job.cc applies the caller's exceptions as
368 * jbur.UIRestrictionsClass &= ~ui_exceptions before its single
369 * SetInformationJobObject, so a delegate that excepts everything away asks
370 * for a mask of zero. That has to be accepted, not refused.
371 */
372 hJob = CreateJobObjectW(NULL, NULL);
373 ok(hJob != NULL, "CreateJobObject failed with %lu\n", GetLastError());
374 if (hJob == NULL)
375 return;
376
377 SetLastError(0xDEADBEEF);
379 ok(Success != FALSE, "Excepting every restriction away failed with %lu\n",
380 GetLastError());
381
382 memset(&Info, 0xAA, sizeof(Info));
385 &Info,
386 sizeof(Info),
387 NULL);
388 ok(Success != FALSE, "QueryInformationJobObject failed with %lu\n", GetLastError());
389 ok_long(Info.UIRestrictionsClass, 0);
390
391 CloseHandle(hJob);
392}
393
394START_TEST(JobObject)
395{
398 test_Access();
400}
static BOOL SetRestrictions(_In_ HANDLE hJob, _In_ ULONG Restrictions)
Definition: JobObject.c:40
static void test_Access(void)
Definition: JobObject.c:239
static void test_InvalidParameters(void)
Definition: JobObject.c:164
static void test_RoundTrip(void)
Definition: JobObject.c:55
#define JOB_LIMITEDUSER_UI
Definition: JobObject.c:15
static void test_SandboxPolicies(void)
Definition: JobObject.c:288
static const ULONG SingleRestrictions[]
Definition: JobObject.c:26
#define JOB_LOCKDOWN_UI
Definition: JobObject.c:20
#define ok_long(expression, result)
Definition: atltest.h:133
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define ok_err(error)
Definition: atltest.h:124
#define START_TEST(x)
Definition: atltest.h:75
#define NULL
Definition: types.h:112
#define FALSE
Definition: types.h:117
#define CloseHandle
Definition: compat.h:739
#define ERROR_INVALID_PARAMETER
Definition: compat.h:101
#define SetLastError(x)
Definition: compat.h:752
#define GetCurrentProcess()
Definition: compat.h:759
#define ERROR_ACCESS_DENIED
Definition: compat.h:97
BOOL WINAPI DuplicateHandle(IN HANDLE hSourceProcessHandle, IN HANDLE hSourceHandle, IN HANDLE hTargetProcessHandle, OUT LPHANDLE lpTargetHandle, IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN DWORD dwOptions)
Definition: handle.c:149
BOOL WINAPI SetInformationJobObject(_In_ HANDLE hJob, _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, _In_reads_bytes_(cbJobObjectInformationLength) LPVOID lpJobObjectInformation, _In_ DWORD cbJobObjectInformationLength)
Definition: job.c:220
HANDLE WINAPI CreateJobObjectW(_In_ LPSECURITY_ATTRIBUTES lpJobAttributes, _In_ LPCWSTR lpName)
Definition: job.c:39
BOOL WINAPI QueryInformationJobObject(_In_opt_ HANDLE hJob, _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, _Out_writes_bytes_to_(cbJobObjectInformationLength, *lpReturnLength) LPVOID lpJobObjectInformation, _In_ DWORD cbJobObjectInformationLength, _Out_opt_ LPDWORD lpReturnLength)
Definition: job.c:125
@ Success
Definition: eventcreate.c:712
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define JOB_OBJECT_UILIMIT_ALL
Definition: pstypes.h:235
#define JOB_OBJECT_UILIMIT_WRITECLIPBOARD
Definition: pstypes.h:229
#define JOB_OBJECT_UILIMIT_DESKTOP
Definition: pstypes.h:233
#define JOB_OBJECT_UILIMIT_GLOBALATOMS
Definition: pstypes.h:232
#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
Definition: pstypes.h:221
@ JobObjectExtendedLimitInformation
Definition: pstypes.h:500
@ JobObjectBasicUIRestrictions
Definition: pstypes.h:495
#define JOB_OBJECT_UILIMIT_HANDLES
Definition: pstypes.h:227
#define JOB_OBJECT_LIMIT_ACTIVE_PROCESS
Definition: pstypes.h:211
#define JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION
Definition: pstypes.h:218
#define JOB_OBJECT_UILIMIT_READCLIPBOARD
Definition: pstypes.h:228
#define JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS
Definition: pstypes.h:230
#define JOB_OBJECT_QUERY
Definition: pstypes.h:198
#define JOB_OBJECT_UILIMIT_DISPLAYSETTINGS
Definition: pstypes.h:231
#define JOB_OBJECT_UILIMIT_EXITWINDOWS
Definition: pstypes.h:234
#define _In_
Definition: no_sal2.h:158
#define memset(x, y, z)
Definition: compat.h:39
#define _countof(array)
Definition: sndvol32.h:70
JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation
Definition: pstypes.h:1741
uint32_t ULONG
Definition: typedefs.h:59
_Must_inspect_result_ _In_ WDFCHILDLIST _In_ PWDF_CHILD_LIST_ITERATOR _Out_ WDFDEVICE _Inout_opt_ PWDF_CHILD_RETRIEVE_INFO Info
Definition: wdfchildlist.h:690
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
#define ERROR_BAD_LENGTH
Definition: winerror.h:249
#define ERROR_NOACCESS
Definition: winerror.h:902
DWORD Returned
Definition: ws2tcpip.h:624