1c2c66affSColin Finck /*
2*b22eefacSGeorge Bișoc * PROJECT: ReactOS Kernel
3*b22eefacSGeorge Bișoc * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4*b22eefacSGeorge Bișoc * PURPOSE: Process Pool Quotas Support
5*b22eefacSGeorge Bișoc * COPYRIGHT: Copyright 2005 Alex Ionescu <alex@relsoft.net>
6*b22eefacSGeorge Bișoc * Copyright 2007 Mike Nordell
7*b22eefacSGeorge Bișoc * Copyright 2021 George Bișoc <george.bisoc@reactos.org>
8c2c66affSColin Finck */
9c2c66affSColin Finck
10c2c66affSColin Finck /* INCLUDES **************************************************************/
11c2c66affSColin Finck
12c2c66affSColin Finck #include <ntoskrnl.h>
13c2c66affSColin Finck #define NDEBUG
14c2c66affSColin Finck #include <debug.h>
15c2c66affSColin Finck
16c2c66affSColin Finck EPROCESS_QUOTA_BLOCK PspDefaultQuotaBlock;
17c2c66affSColin Finck static LIST_ENTRY PspQuotaBlockList = {&PspQuotaBlockList, &PspQuotaBlockList};
18c2c66affSColin Finck static KSPIN_LOCK PspQuotaLock;
19c2c66affSColin Finck
20c2c66affSColin Finck #define VALID_QUOTA_FLAGS (QUOTA_LIMITS_HARDWS_MIN_ENABLE | \
21c2c66affSColin Finck QUOTA_LIMITS_HARDWS_MIN_DISABLE | \
22c2c66affSColin Finck QUOTA_LIMITS_HARDWS_MAX_ENABLE | \
23c2c66affSColin Finck QUOTA_LIMITS_HARDWS_MAX_DISABLE)
24c2c66affSColin Finck
25c2c66affSColin Finck /* PRIVATE FUNCTIONS *******************************************************/
26c2c66affSColin Finck
27*b22eefacSGeorge Bișoc /**
28*b22eefacSGeorge Bișoc * @brief
29*b22eefacSGeorge Bișoc * Returns pool quotas back to the Memory Manager
30*b22eefacSGeorge Bișoc * when the pool quota block is no longer being
31*b22eefacSGeorge Bișoc * used by anybody.
32*b22eefacSGeorge Bișoc *
33*b22eefacSGeorge Bișoc * @param[in] QuotaBlock
34*b22eefacSGeorge Bișoc * The pool quota block of which quota resources
35*b22eefacSGeorge Bișoc * are to be sent back.
36*b22eefacSGeorge Bișoc *
37*b22eefacSGeorge Bișoc * @return
38*b22eefacSGeorge Bișoc * Nothing.
39*b22eefacSGeorge Bișoc *
40*b22eefacSGeorge Bișoc * @remarks
41*b22eefacSGeorge Bișoc * The function only returns quotas back to Memory
42*b22eefacSGeorge Bișoc * Manager that is paged or non paged. It does not
43*b22eefacSGeorge Bișoc * return page file quotas as page file quota
44*b22eefacSGeorge Bișoc * management is done in a different way. Furthermore,
45*b22eefacSGeorge Bișoc * quota spin lock has to be held when returning quotas.
46*b22eefacSGeorge Bișoc */
_Requires_lock_held_(PspQuotaLock)47*b22eefacSGeorge Bișoc _Requires_lock_held_(PspQuotaLock)
48*b22eefacSGeorge Bișoc VOID
49*b22eefacSGeorge Bișoc NTAPI
50*b22eefacSGeorge Bișoc PspReturnQuotasOnDestroy(
51*b22eefacSGeorge Bișoc _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)
52*b22eefacSGeorge Bișoc {
53*b22eefacSGeorge Bișoc ULONG PsQuotaTypeIndex;
54*b22eefacSGeorge Bișoc SIZE_T QuotaToReturn;
55*b22eefacSGeorge Bișoc
56c2c66affSColin Finck /*
57*b22eefacSGeorge Bișoc * We must be in a dispatch level interrupt here
58*b22eefacSGeorge Bișoc * as we should be under a spin lock.
59*b22eefacSGeorge Bișoc */
60*b22eefacSGeorge Bișoc ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
61*b22eefacSGeorge Bișoc
62*b22eefacSGeorge Bișoc /* Make sure that the quota block is not plain garbage */
63*b22eefacSGeorge Bișoc ASSERT(QuotaBlock);
64*b22eefacSGeorge Bișoc
65*b22eefacSGeorge Bișoc /* Loop over the Process quota types */
66*b22eefacSGeorge Bișoc for (PsQuotaTypeIndex = PsNonPagedPool; PsQuotaTypeIndex < PsPageFile; PsQuotaTypeIndex++)
67*b22eefacSGeorge Bișoc {
68*b22eefacSGeorge Bișoc /* The amount needed to return to Mm is the limit and return fields */
69*b22eefacSGeorge Bișoc QuotaToReturn = QuotaBlock->QuotaEntry[PsQuotaTypeIndex].Limit + QuotaBlock->QuotaEntry[PsQuotaTypeIndex].Return;
70*b22eefacSGeorge Bișoc MmReturnPoolQuota(PsQuotaTypeIndex, QuotaToReturn);
71*b22eefacSGeorge Bișoc }
72*b22eefacSGeorge Bișoc }
73*b22eefacSGeorge Bișoc
74*b22eefacSGeorge Bișoc /**
75*b22eefacSGeorge Bișoc * @brief
76*b22eefacSGeorge Bișoc * Releases some of excess quotas in order to attempt
77*b22eefacSGeorge Bișoc * free up some resources. This is done primarily in
78*b22eefacSGeorge Bișoc * in case the Memory Manager fails to raise the quota
79*b22eefacSGeorge Bișoc * limit.
80*b22eefacSGeorge Bișoc *
81*b22eefacSGeorge Bișoc * @param[in] QuotaType
82*b22eefacSGeorge Bișoc * Process pool quota type.
83*b22eefacSGeorge Bișoc *
84*b22eefacSGeorge Bișoc * @param[out] ReturnedQuotas
85*b22eefacSGeorge Bișoc * A pointer to the returned amount of quotas
86*b22eefacSGeorge Bișoc * back to Memory Manager.
87*b22eefacSGeorge Bișoc *
88*b22eefacSGeorge Bișoc * @return
89*b22eefacSGeorge Bișoc * Nothing.
90*b22eefacSGeorge Bișoc *
91*b22eefacSGeorge Bișoc * @remarks
92*b22eefacSGeorge Bișoc * The function releases excess paged or non
93*b22eefacSGeorge Bișoc * paged pool quotas. Page file quota type is
94*b22eefacSGeorge Bișoc * not permitted. Furthermore, quota spin lock
95*b22eefacSGeorge Bișoc * has to be held when returning quotas.
96*b22eefacSGeorge Bișoc */
_Requires_lock_held_(PspQuotaLock)97*b22eefacSGeorge Bișoc _Requires_lock_held_(PspQuotaLock)
98*b22eefacSGeorge Bișoc VOID
99*b22eefacSGeorge Bișoc NTAPI
100*b22eefacSGeorge Bișoc PspReturnExcessQuotas(
101*b22eefacSGeorge Bișoc _In_ PS_QUOTA_TYPE QuotaType,
102*b22eefacSGeorge Bișoc _Outptr_ PSIZE_T ReturnedQuotas)
103*b22eefacSGeorge Bișoc {
104*b22eefacSGeorge Bișoc PLIST_ENTRY PspQuotaList;
105*b22eefacSGeorge Bișoc PEPROCESS_QUOTA_BLOCK QuotaBlockFromList;
106*b22eefacSGeorge Bișoc SIZE_T AmountToReturn = 0;
107*b22eefacSGeorge Bișoc
108*b22eefacSGeorge Bișoc /*
109*b22eefacSGeorge Bișoc * We must be in a dispatch level interrupt here
110*b22eefacSGeorge Bișoc * as we should be under a spin lock.
111*b22eefacSGeorge Bișoc */
112*b22eefacSGeorge Bișoc ASSERT_IRQL_EQUAL(DISPATCH_LEVEL);
113*b22eefacSGeorge Bișoc
114*b22eefacSGeorge Bișoc /*
115*b22eefacSGeorge Bișoc * Loop over the quota block lists and reap
116*b22eefacSGeorge Bișoc * whatever quotas we haven't returned which
117*b22eefacSGeorge Bișoc * is needed to free up resources.
118*b22eefacSGeorge Bișoc */
119*b22eefacSGeorge Bișoc for (PspQuotaList = PspQuotaBlockList.Flink;
120*b22eefacSGeorge Bișoc PspQuotaList != &PspQuotaBlockList;
121*b22eefacSGeorge Bișoc PspQuotaList = PspQuotaList->Flink)
122*b22eefacSGeorge Bișoc {
123*b22eefacSGeorge Bișoc /* Gather the quota block from the list */
124*b22eefacSGeorge Bișoc QuotaBlockFromList = CONTAINING_RECORD(PspQuotaList, EPROCESS_QUOTA_BLOCK, QuotaList);
125*b22eefacSGeorge Bișoc
126*b22eefacSGeorge Bișoc /*
127*b22eefacSGeorge Bișoc * Gather any unreturned quotas and cache
128*b22eefacSGeorge Bișoc * them to a variable.
129*b22eefacSGeorge Bișoc */
130*b22eefacSGeorge Bișoc AmountToReturn += InterlockedExchangeSizeT(&QuotaBlockFromList->QuotaEntry[QuotaType].Return, 0);
131*b22eefacSGeorge Bișoc
132*b22eefacSGeorge Bișoc /*
133*b22eefacSGeorge Bișoc * If no other process is taking use of this
134*b22eefacSGeorge Bișoc * block, then it means that this block has
135*b22eefacSGeorge Bișoc * only shared pool quota and the last process
136*b22eefacSGeorge Bișoc * no longer uses this block. If the limit is
137*b22eefacSGeorge Bișoc * grater than the usage then trim the limit
138*b22eefacSGeorge Bișoc * and use that as additional amount of quota
139*b22eefacSGeorge Bișoc * to return.
140*b22eefacSGeorge Bișoc */
141*b22eefacSGeorge Bișoc if (QuotaBlockFromList->ProcessCount == 0)
142*b22eefacSGeorge Bișoc {
143*b22eefacSGeorge Bișoc if (QuotaBlockFromList->QuotaEntry[QuotaType].Usage <
144*b22eefacSGeorge Bișoc QuotaBlockFromList->QuotaEntry[QuotaType].Limit)
145*b22eefacSGeorge Bișoc {
146*b22eefacSGeorge Bișoc InterlockedExchangeSizeT(&QuotaBlockFromList->QuotaEntry[QuotaType].Limit,
147*b22eefacSGeorge Bișoc QuotaBlockFromList->QuotaEntry[QuotaType].Usage);
148*b22eefacSGeorge Bișoc AmountToReturn += QuotaBlockFromList->QuotaEntry[QuotaType].Limit;
149*b22eefacSGeorge Bișoc }
150*b22eefacSGeorge Bișoc }
151*b22eefacSGeorge Bișoc }
152*b22eefacSGeorge Bișoc
153*b22eefacSGeorge Bișoc /* Invoke Mm to return quotas */
154*b22eefacSGeorge Bișoc DPRINT("PspReturnExcessQuotas(): Amount of quota released -- %lu\n", AmountToReturn);
155*b22eefacSGeorge Bișoc MmReturnPoolQuota(QuotaType, AmountToReturn);
156*b22eefacSGeorge Bișoc *ReturnedQuotas = AmountToReturn;
157*b22eefacSGeorge Bișoc }
158*b22eefacSGeorge Bișoc
159*b22eefacSGeorge Bișoc /**
160*b22eefacSGeorge Bișoc * @brief
161*b22eefacSGeorge Bișoc * Internal kernel function that provides the
162*b22eefacSGeorge Bișoc * bulk logic of process quota charging,
163*b22eefacSGeorge Bișoc * necessary for exported kernel routines
164*b22eefacSGeorge Bișoc * needed for quota management.
165*b22eefacSGeorge Bișoc *
166*b22eefacSGeorge Bișoc * @param[in] Process
167*b22eefacSGeorge Bișoc * A process, represented as a EPROCESS object.
168*b22eefacSGeorge Bișoc * This parameter is used to charge the own
169*b22eefacSGeorge Bișoc * process' quota usage.
170*b22eefacSGeorge Bișoc *
171*b22eefacSGeorge Bișoc * @param[in] QuotaBlock
172*b22eefacSGeorge Bișoc * The quota block which quotas are to be charged.
173*b22eefacSGeorge Bișoc * This block can either come from the process itself
174*b22eefacSGeorge Bișoc * or from an object with specified quota charges.
175*b22eefacSGeorge Bișoc *
176*b22eefacSGeorge Bișoc * @param[in] QuotaType
177*b22eefacSGeorge Bișoc * The quota type which quota in question is to
178*b22eefacSGeorge Bișoc * be charged. The permitted types are PsPagedPool,
179*b22eefacSGeorge Bișoc * PsNonPagedPool and PsPageFile.
180*b22eefacSGeorge Bișoc *
181*b22eefacSGeorge Bișoc * @param[in] Amount
182*b22eefacSGeorge Bișoc * The amount of quota to be charged.
183*b22eefacSGeorge Bișoc *
184*b22eefacSGeorge Bișoc * @return
185*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS if quota charging has
186*b22eefacSGeorge Bișoc * been done successfully without problemns.
187*b22eefacSGeorge Bișoc * STATUS_QUOTA_EXCEEDED is returned if the caller
188*b22eefacSGeorge Bișoc * wants to charge quotas with amount way over
189*b22eefacSGeorge Bișoc * the limits. STATUS_PAGEFILE_QUOTA_EXCEEDED
190*b22eefacSGeorge Bișoc * is returned for the same situation but
191*b22eefacSGeorge Bișoc * specific to page files instead.
192c2c66affSColin Finck */
193c2c66affSColin Finck NTSTATUS
194c2c66affSColin Finck NTAPI
PspChargeProcessQuotaSpecifiedPool(_In_opt_ PEPROCESS Process,_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,_In_ PS_QUOTA_TYPE QuotaType,_In_ SIZE_T Amount)195*b22eefacSGeorge Bișoc PspChargeProcessQuotaSpecifiedPool(
196*b22eefacSGeorge Bișoc _In_opt_ PEPROCESS Process,
197*b22eefacSGeorge Bișoc _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,
198*b22eefacSGeorge Bișoc _In_ PS_QUOTA_TYPE QuotaType,
199*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
200c2c66affSColin Finck {
201a340ec17SGeorge Bișoc KIRQL OldIrql;
202*b22eefacSGeorge Bișoc SIZE_T ReturnedQuotas;
203*b22eefacSGeorge Bișoc SIZE_T UpdatedLimit;
204c2c66affSColin Finck
205*b22eefacSGeorge Bișoc /* Sanity checks */
206*b22eefacSGeorge Bișoc ASSERT(QuotaType < PsQuotaTypes);
207*b22eefacSGeorge Bișoc ASSERT((SSIZE_T)Amount >= 0);
208*b22eefacSGeorge Bișoc
209*b22eefacSGeorge Bișoc /* Guard ourselves in a spin lock */
210a340ec17SGeorge Bișoc KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
211a340ec17SGeorge Bișoc
212*b22eefacSGeorge Bișoc /* Are we within the bounds of quota limit? */
213*b22eefacSGeorge Bișoc if (QuotaBlock->QuotaEntry[QuotaType].Usage + Amount >
214*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit &&
215*b22eefacSGeorge Bișoc QuotaBlock != &PspDefaultQuotaBlock)
216c2c66affSColin Finck {
217*b22eefacSGeorge Bișoc /* We aren't... Is this a page file quota charging? */
218*b22eefacSGeorge Bișoc if (QuotaType == PsPageFile)
219c2c66affSColin Finck {
220*b22eefacSGeorge Bișoc /* It is, return the appropriate status code */
221*b22eefacSGeorge Bișoc DPRINT1("PspChargeProcessQuotaSpecifiedPool(): Quota amount exceeds the limit on page file quota (limit -- %lu || amount -- %lu)\n",
222*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit, Amount);
223*b22eefacSGeorge Bișoc return STATUS_PAGEFILE_QUOTA_EXCEEDED;
224c2c66affSColin Finck }
225c2c66affSColin Finck
226c2c66affSColin Finck /*
227*b22eefacSGeorge Bișoc * This is not a page file charge. What we can do at best
228*b22eefacSGeorge Bișoc * in this scenario is to attempt to raise (expand) the
229*b22eefacSGeorge Bișoc * quota limit charges of the block.
230*b22eefacSGeorge Bișoc */
231*b22eefacSGeorge Bișoc if (!MmRaisePoolQuota(QuotaType,
232*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit,
233*b22eefacSGeorge Bișoc &UpdatedLimit))
234*b22eefacSGeorge Bișoc {
235*b22eefacSGeorge Bișoc /*
236*b22eefacSGeorge Bișoc * We can't? It could be that we must free
237*b22eefacSGeorge Bișoc * up some resources in order to raise the
238*b22eefacSGeorge Bișoc * limit, which in that case we must return
239*b22eefacSGeorge Bișoc * the excess of quota that hasn't been
240*b22eefacSGeorge Bișoc * returned. If we haven't returned anything
241*b22eefacSGeorge Bișoc * then what we're doing here is futile.
242*b22eefacSGeorge Bișoc * Bail out...
243*b22eefacSGeorge Bișoc */
244*b22eefacSGeorge Bișoc PspReturnExcessQuotas(QuotaType, &ReturnedQuotas);
245*b22eefacSGeorge Bișoc if (ReturnedQuotas == 0)
246*b22eefacSGeorge Bișoc {
247*b22eefacSGeorge Bișoc DPRINT1("PspChargeProcessQuotaSpecifiedPool(): Failed to free some resources in order to raise quota limits...\n");
248*b22eefacSGeorge Bișoc KeReleaseSpinLock(&PspQuotaLock, OldIrql);
249*b22eefacSGeorge Bișoc return STATUS_QUOTA_EXCEEDED;
250*b22eefacSGeorge Bișoc }
251*b22eefacSGeorge Bișoc
252*b22eefacSGeorge Bișoc /* Try to raise the quota limits again */
253*b22eefacSGeorge Bișoc MmRaisePoolQuota(QuotaType,
254*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit,
255*b22eefacSGeorge Bișoc &UpdatedLimit);
256*b22eefacSGeorge Bișoc }
257*b22eefacSGeorge Bișoc
258*b22eefacSGeorge Bișoc /* Enforce a new raised limit */
259*b22eefacSGeorge Bișoc InterlockedExchangeSizeT(&QuotaBlock->QuotaEntry[QuotaType].Limit, UpdatedLimit);
260*b22eefacSGeorge Bișoc
261*b22eefacSGeorge Bișoc /*
262*b22eefacSGeorge Bișoc * Now determine if the current usage and the
263*b22eefacSGeorge Bișoc * amounting by the caller still exceeds the
264*b22eefacSGeorge Bișoc * quota limit of the process. If it's still
265*b22eefacSGeorge Bișoc * over the limit then there's nothing we can
266*b22eefacSGeorge Bișoc * do, so fail.
267*b22eefacSGeorge Bișoc */
268*b22eefacSGeorge Bișoc if (QuotaBlock->QuotaEntry[QuotaType].Usage + Amount >
269*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit)
270*b22eefacSGeorge Bișoc {
271*b22eefacSGeorge Bișoc DPRINT1("PspChargeProcessQuotaSpecifiedPool(): Quota amount exceeds the limit (limit -- %lu || amount -- %lu)\n",
272*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit, Amount);
273*b22eefacSGeorge Bișoc return STATUS_QUOTA_EXCEEDED;
274*b22eefacSGeorge Bișoc }
275*b22eefacSGeorge Bișoc }
276*b22eefacSGeorge Bișoc
277*b22eefacSGeorge Bișoc /* Update the quota usage */
278*b22eefacSGeorge Bișoc InterlockedExchangeAddSizeT(&QuotaBlock->QuotaEntry[QuotaType].Usage, Amount);
279*b22eefacSGeorge Bișoc
280*b22eefacSGeorge Bișoc /* Update the entry peak if it's less than the usage */
281*b22eefacSGeorge Bișoc if (QuotaBlock->QuotaEntry[QuotaType].Peak <
282*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Usage)
283*b22eefacSGeorge Bișoc {
284*b22eefacSGeorge Bișoc InterlockedExchangeSizeT(&QuotaBlock->QuotaEntry[QuotaType].Peak,
285*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Usage);
286*b22eefacSGeorge Bișoc }
287*b22eefacSGeorge Bișoc
288*b22eefacSGeorge Bișoc /* Are we being given a process as well? */
289*b22eefacSGeorge Bișoc if (Process)
290*b22eefacSGeorge Bișoc {
291*b22eefacSGeorge Bișoc /* We're being given, check that's not a system one */
292*b22eefacSGeorge Bișoc ASSERT(Process != PsInitialSystemProcess);
293*b22eefacSGeorge Bișoc
294*b22eefacSGeorge Bișoc InterlockedExchangeAddSizeT(&Process->QuotaUsage[QuotaType], Amount);
295*b22eefacSGeorge Bișoc
296*b22eefacSGeorge Bișoc /*
297*b22eefacSGeorge Bișoc * OK, we've now updated the quota usage of the process
298*b22eefacSGeorge Bișoc * based upon the amount that the caller wanted to charge.
299*b22eefacSGeorge Bișoc * Although the peak of process quota can be less than it was
300*b22eefacSGeorge Bișoc * before so update the peaks as well accordingly.
301*b22eefacSGeorge Bișoc */
302*b22eefacSGeorge Bișoc if (Process->QuotaPeak[QuotaType] < Process->QuotaUsage[QuotaType])
303*b22eefacSGeorge Bișoc {
304*b22eefacSGeorge Bișoc InterlockedExchangeSizeT(&Process->QuotaPeak[QuotaType],
305*b22eefacSGeorge Bișoc Process->QuotaUsage[QuotaType]);
306*b22eefacSGeorge Bișoc }
307*b22eefacSGeorge Bișoc }
308*b22eefacSGeorge Bișoc
309*b22eefacSGeorge Bișoc /* Release the lock */
310*b22eefacSGeorge Bișoc KeReleaseSpinLock(&PspQuotaLock, OldIrql);
311*b22eefacSGeorge Bișoc return STATUS_SUCCESS;
312*b22eefacSGeorge Bișoc }
313*b22eefacSGeorge Bișoc
314*b22eefacSGeorge Bișoc /**
315*b22eefacSGeorge Bișoc * @brief
316*b22eefacSGeorge Bișoc * Internal kernel function that provides the
317*b22eefacSGeorge Bișoc * bulk logic of process quota returning. It
318*b22eefacSGeorge Bișoc * returns (takes away) quotas back from a
319*b22eefacSGeorge Bișoc * process and/or quota block, which is
320*b22eefacSGeorge Bișoc * the opposite of charging quotas.
321*b22eefacSGeorge Bișoc *
322*b22eefacSGeorge Bișoc * @param[in] Process
323*b22eefacSGeorge Bișoc * A process, represented as a EPROCESS object.
324*b22eefacSGeorge Bișoc * This parameter is used to return the own
325*b22eefacSGeorge Bișoc * process' quota usage.
326*b22eefacSGeorge Bișoc *
327*b22eefacSGeorge Bișoc * @param[in] QuotaBlock
328*b22eefacSGeorge Bișoc * The quota block which quotas are to be returned.
329*b22eefacSGeorge Bișoc * This block can either come from the process itself
330*b22eefacSGeorge Bișoc * or from an object with specified quota charges.
331*b22eefacSGeorge Bișoc *
332*b22eefacSGeorge Bișoc * @param[in] QuotaType
333*b22eefacSGeorge Bișoc * The quota type which quota in question is to
334*b22eefacSGeorge Bișoc * be returned. The permitted types are PsPagedPool,
335*b22eefacSGeorge Bișoc * PsNonPagedPool and PsPageFile.
336*b22eefacSGeorge Bișoc *
337*b22eefacSGeorge Bișoc * @param[in] Amount
338*b22eefacSGeorge Bișoc * The amount of quota to be returned.
339*b22eefacSGeorge Bișoc *
340*b22eefacSGeorge Bișoc * @return
341*b22eefacSGeorge Bișoc * Nothing.
342c2c66affSColin Finck */
343c2c66affSColin Finck VOID
344c2c66affSColin Finck NTAPI
PspReturnProcessQuotaSpecifiedPool(_In_opt_ PEPROCESS Process,_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,_In_ PS_QUOTA_TYPE QuotaType,_In_ SIZE_T Amount)345*b22eefacSGeorge Bișoc PspReturnProcessQuotaSpecifiedPool(
346*b22eefacSGeorge Bișoc _In_opt_ PEPROCESS Process,
347*b22eefacSGeorge Bișoc _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,
348*b22eefacSGeorge Bișoc _In_ PS_QUOTA_TYPE QuotaType,
349*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
350c2c66affSColin Finck {
351a340ec17SGeorge Bișoc KIRQL OldIrql;
352*b22eefacSGeorge Bișoc SIZE_T ReturnThreshold;
353*b22eefacSGeorge Bișoc SIZE_T AmountToReturn = 0;
354a340ec17SGeorge Bișoc
355*b22eefacSGeorge Bișoc /* Sanity checks */
356*b22eefacSGeorge Bișoc ASSERT(QuotaType < PsQuotaTypes);
357*b22eefacSGeorge Bișoc ASSERT((SSIZE_T)Amount >= 0);
358*b22eefacSGeorge Bișoc
359*b22eefacSGeorge Bișoc /* Guard ourselves in a spin lock */
360a340ec17SGeorge Bișoc KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
361a340ec17SGeorge Bișoc
362*b22eefacSGeorge Bișoc /* Does the caller return more quota than it was previously charged? */
363*b22eefacSGeorge Bișoc if ((Process && Process->QuotaUsage[QuotaType] < Amount) ||
364*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Usage < Amount)
365c2c66affSColin Finck {
366*b22eefacSGeorge Bișoc /* It does, crash the system! */
367*b22eefacSGeorge Bișoc KeBugCheckEx(QUOTA_UNDERFLOW,
368*b22eefacSGeorge Bișoc (ULONG_PTR)Process,
369*b22eefacSGeorge Bișoc (ULONG_PTR)QuotaType,
370*b22eefacSGeorge Bișoc Process ? (ULONG_PTR)Process->QuotaUsage[QuotaType] :
371*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Usage,
372*b22eefacSGeorge Bișoc (ULONG_PTR)Amount);
373c2c66affSColin Finck }
374a340ec17SGeorge Bișoc
375*b22eefacSGeorge Bișoc /* The return threshold can be non paged or paged */
376*b22eefacSGeorge Bișoc ReturnThreshold = QuotaType ? PSP_NON_PAGED_POOL_QUOTA_THRESHOLD : PSP_PAGED_POOL_QUOTA_THRESHOLD;
377*b22eefacSGeorge Bișoc
378*b22eefacSGeorge Bișoc /*
379*b22eefacSGeorge Bișoc * We need to trim the quota limits based on the
380*b22eefacSGeorge Bișoc * amount we're going to return quotas back.
381*b22eefacSGeorge Bișoc */
382*b22eefacSGeorge Bișoc if ((QuotaType != PsPageFile && QuotaBlock != &PspDefaultQuotaBlock) &&
383*b22eefacSGeorge Bișoc (QuotaBlock->QuotaEntry[QuotaType].Limit > QuotaBlock->QuotaEntry[QuotaType].Usage + ReturnThreshold))
384*b22eefacSGeorge Bișoc {
385*b22eefacSGeorge Bișoc /*
386*b22eefacSGeorge Bișoc * If the amount to return exceeds the threshold,
387*b22eefacSGeorge Bișoc * the new amount becomes the default, otherwise
388*b22eefacSGeorge Bișoc * the amount is just the one given by the caller.
389*b22eefacSGeorge Bișoc */
390*b22eefacSGeorge Bișoc AmountToReturn = min(Amount, ReturnThreshold);
391*b22eefacSGeorge Bișoc
392*b22eefacSGeorge Bișoc /* Add up the lots to the Return field */
393*b22eefacSGeorge Bișoc InterlockedExchangeAddSizeT(&QuotaBlock->QuotaEntry[QuotaType].Return, AmountToReturn);
394*b22eefacSGeorge Bișoc
395*b22eefacSGeorge Bișoc /*
396*b22eefacSGeorge Bișoc * If the amount to return exceeds the threshold then
397*b22eefacSGeorge Bișoc * we have lots of quota to return to Mm. So do it so
398*b22eefacSGeorge Bișoc * and zerou out the Return field.
399*b22eefacSGeorge Bișoc */
400*b22eefacSGeorge Bișoc if (QuotaBlock->QuotaEntry[QuotaType].Return > ReturnThreshold)
401*b22eefacSGeorge Bișoc {
402*b22eefacSGeorge Bișoc MmReturnPoolQuota(QuotaType, QuotaBlock->QuotaEntry[QuotaType].Return);
403*b22eefacSGeorge Bișoc InterlockedExchangeSizeT(QuotaBlock->QuotaEntry[QuotaType].Return, 0);
404*b22eefacSGeorge Bișoc }
405*b22eefacSGeorge Bișoc
406*b22eefacSGeorge Bișoc /* And try to trim the limit */
407*b22eefacSGeorge Bișoc InterlockedExchangeSizeT(&QuotaBlock->QuotaEntry[QuotaType].Limit,
408*b22eefacSGeorge Bișoc QuotaBlock->QuotaEntry[QuotaType].Limit - AmountToReturn);
409*b22eefacSGeorge Bișoc }
410*b22eefacSGeorge Bișoc
411*b22eefacSGeorge Bișoc /* Update the usage member of the block */
412*b22eefacSGeorge Bișoc InterlockedExchangeAddSizeT(&QuotaBlock->QuotaEntry[QuotaType].Usage, -(LONG_PTR)Amount);
413*b22eefacSGeorge Bișoc
414*b22eefacSGeorge Bișoc /* Are we being given a process? */
415*b22eefacSGeorge Bișoc if (Process)
416*b22eefacSGeorge Bișoc {
417*b22eefacSGeorge Bișoc /* We're being given, check that's not a system one */
418*b22eefacSGeorge Bișoc ASSERT(Process != PsInitialSystemProcess);
419*b22eefacSGeorge Bișoc
420*b22eefacSGeorge Bișoc /* Decrease the process' quota usage */
421*b22eefacSGeorge Bișoc InterlockedExchangeAddSizeT(&Process->QuotaUsage[QuotaType], -(LONG_PTR)Amount);
422*b22eefacSGeorge Bișoc }
423*b22eefacSGeorge Bișoc
424*b22eefacSGeorge Bișoc /* We're done, release the lock */
425a340ec17SGeorge Bișoc KeReleaseSpinLock(&PspQuotaLock, OldIrql);
426c2c66affSColin Finck }
427c2c66affSColin Finck
428c2c66affSColin Finck /* FUNCTIONS ***************************************************************/
429c2c66affSColin Finck
430*b22eefacSGeorge Bișoc /**
431*b22eefacSGeorge Bișoc * @brief
432*b22eefacSGeorge Bișoc * Initializes the quota system during boot
433*b22eefacSGeorge Bișoc * phase of the system, which sets up the
434*b22eefacSGeorge Bișoc * default quota block that is used across
435*b22eefacSGeorge Bișoc * several processes.
436*b22eefacSGeorge Bișoc *
437*b22eefacSGeorge Bișoc * @return
438*b22eefacSGeorge Bișoc * Nothing.
439*b22eefacSGeorge Bișoc */
4405c7ce447SVictor Perevertkin CODE_SEG("INIT")
441c2c66affSColin Finck VOID
442c2c66affSColin Finck NTAPI
PsInitializeQuotaSystem(VOID)443c2c66affSColin Finck PsInitializeQuotaSystem(VOID)
444c2c66affSColin Finck {
445*b22eefacSGeorge Bișoc /* Initialize the default block */
446c2c66affSColin Finck RtlZeroMemory(&PspDefaultQuotaBlock, sizeof(PspDefaultQuotaBlock));
447*b22eefacSGeorge Bișoc
448*b22eefacSGeorge Bișoc /* Assign the default quota limits */
4496170b574SGeorge Bișoc PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit = (SIZE_T)-1;
4506170b574SGeorge Bișoc PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit = (SIZE_T)-1;
4516170b574SGeorge Bișoc PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit = (SIZE_T)-1;
452*b22eefacSGeorge Bișoc
453*b22eefacSGeorge Bișoc /*
454*b22eefacSGeorge Bișoc * Set up the count references as the
455*b22eefacSGeorge Bișoc * default block will going to be used.
456*b22eefacSGeorge Bișoc */
457*b22eefacSGeorge Bișoc PspDefaultQuotaBlock.ReferenceCount = 1;
458*b22eefacSGeorge Bișoc PspDefaultQuotaBlock.ProcessCount = 1;
459*b22eefacSGeorge Bișoc
460*b22eefacSGeorge Bișoc /* Assign that block to initial process */
461c2c66affSColin Finck PsGetCurrentProcess()->QuotaBlock = &PspDefaultQuotaBlock;
462c2c66affSColin Finck }
463c2c66affSColin Finck
464*b22eefacSGeorge Bișoc /**
465*b22eefacSGeorge Bișoc * @brief
466*b22eefacSGeorge Bișoc * Inherits the quota block to another newborn
467*b22eefacSGeorge Bișoc * (child) process. If there's no parent
468*b22eefacSGeorge Bișoc * process, the default quota block is
469*b22eefacSGeorge Bișoc * assigned.
470*b22eefacSGeorge Bișoc *
471*b22eefacSGeorge Bișoc * @param[in] Process
472*b22eefacSGeorge Bișoc * The child process which quota block
473*b22eefacSGeorge Bișoc * is to be given.
474*b22eefacSGeorge Bișoc *
475*b22eefacSGeorge Bișoc * @param[in] ParentProcess
476*b22eefacSGeorge Bișoc * The parent process.
477*b22eefacSGeorge Bișoc *
478*b22eefacSGeorge Bișoc * @return
479*b22eefacSGeorge Bișoc * Nothing.
480*b22eefacSGeorge Bișoc */
481c2c66affSColin Finck VOID
482c2c66affSColin Finck NTAPI
PspInheritQuota(_In_ PEPROCESS Process,_In_opt_ PEPROCESS ParentProcess)483*b22eefacSGeorge Bișoc PspInheritQuota(
484*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
485*b22eefacSGeorge Bișoc _In_opt_ PEPROCESS ParentProcess)
486c2c66affSColin Finck {
487*b22eefacSGeorge Bișoc PEPROCESS_QUOTA_BLOCK QuotaBlock;
488*b22eefacSGeorge Bișoc
489c2c66affSColin Finck if (ParentProcess != NULL)
490c2c66affSColin Finck {
491*b22eefacSGeorge Bișoc ASSERT(ParentProcess->QuotaBlock != NULL);
492*b22eefacSGeorge Bișoc QuotaBlock = ParentProcess->QuotaBlock;
493*b22eefacSGeorge Bișoc }
494*b22eefacSGeorge Bișoc else
495*b22eefacSGeorge Bișoc {
496*b22eefacSGeorge Bișoc QuotaBlock = &PspDefaultQuotaBlock;
497*b22eefacSGeorge Bișoc }
498c2c66affSColin Finck
499*b22eefacSGeorge Bișoc InterlockedIncrementSizeT(&QuotaBlock->ProcessCount);
500*b22eefacSGeorge Bișoc InterlockedIncrementSizeT(&QuotaBlock->ReferenceCount);
501c2c66affSColin Finck
502c2c66affSColin Finck Process->QuotaBlock = QuotaBlock;
503c2c66affSColin Finck }
504c2c66affSColin Finck
505*b22eefacSGeorge Bișoc /**
506*b22eefacSGeorge Bișoc * @brief
507*b22eefacSGeorge Bișoc * Inserts the new quota block into
508*b22eefacSGeorge Bișoc * the quota list.
509*b22eefacSGeorge Bișoc *
510*b22eefacSGeorge Bișoc * @param[in] QuotaBlock
511*b22eefacSGeorge Bișoc * The new quota block.
512*b22eefacSGeorge Bișoc *
513*b22eefacSGeorge Bișoc * @return
514*b22eefacSGeorge Bișoc * Nothing.
515*b22eefacSGeorge Bișoc */
516c2c66affSColin Finck VOID
517c2c66affSColin Finck NTAPI
PspInsertQuotaBlock(_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)518c2c66affSColin Finck PspInsertQuotaBlock(
519*b22eefacSGeorge Bișoc _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)
520c2c66affSColin Finck {
521c2c66affSColin Finck KIRQL OldIrql;
522c2c66affSColin Finck
523c2c66affSColin Finck KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
524c2c66affSColin Finck InsertTailList(&PspQuotaBlockList, &QuotaBlock->QuotaList);
525c2c66affSColin Finck KeReleaseSpinLock(&PspQuotaLock, OldIrql);
526c2c66affSColin Finck }
527c2c66affSColin Finck
528*b22eefacSGeorge Bișoc /**
529*b22eefacSGeorge Bișoc * @brief
530*b22eefacSGeorge Bișoc * De-references a quota block when quotas
531*b22eefacSGeorge Bișoc * have been returned back because of an
532*b22eefacSGeorge Bișoc * object de-allocation or when a process
533*b22eefacSGeorge Bișoc * gets destroyed. If the last instance
534*b22eefacSGeorge Bișoc * that held up the block gets de-referenced
535*b22eefacSGeorge Bișoc * the function will perform a cleanup against
536*b22eefacSGeorge Bișoc * that block and it'll free the quota block
537*b22eefacSGeorge Bișoc * from memory.
538*b22eefacSGeorge Bișoc *
539*b22eefacSGeorge Bișoc * @param[in] Process
540*b22eefacSGeorge Bișoc * A pointer to a process that de-references the
541*b22eefacSGeorge Bișoc * quota block.
542*b22eefacSGeorge Bișoc *
543*b22eefacSGeorge Bișoc * @param[in] QuotaBlock
544*b22eefacSGeorge Bișoc * A pointer to a quota block that is to be
545*b22eefacSGeorge Bișoc * de-referenced. This block can come from a
546*b22eefacSGeorge Bișoc * process that references it or an object.
547*b22eefacSGeorge Bișoc *
548*b22eefacSGeorge Bișoc * @return
549*b22eefacSGeorge Bișoc * Nothing.
550*b22eefacSGeorge Bișoc */
551c2c66affSColin Finck VOID
552c2c66affSColin Finck NTAPI
PspDereferenceQuotaBlock(_In_opt_ PEPROCESS Process,_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)553*b22eefacSGeorge Bișoc PspDereferenceQuotaBlock(
554*b22eefacSGeorge Bișoc _In_opt_ PEPROCESS Process,
555*b22eefacSGeorge Bișoc _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock)
556c2c66affSColin Finck {
557*b22eefacSGeorge Bișoc ULONG PsQuotaTypeIndex;
558c2c66affSColin Finck KIRQL OldIrql;
559c2c66affSColin Finck
560*b22eefacSGeorge Bișoc /* Make sure the quota block is not trash */
561*b22eefacSGeorge Bișoc ASSERT(QuotaBlock);
562*b22eefacSGeorge Bișoc
563*b22eefacSGeorge Bișoc /* Iterate over the process quota types if we have a process */
564*b22eefacSGeorge Bișoc if (Process)
565*b22eefacSGeorge Bișoc {
566*b22eefacSGeorge Bișoc for (PsQuotaTypeIndex = PsNonPagedPool; PsQuotaTypeIndex < PsQuotaTypes; PsQuotaTypeIndex++)
567*b22eefacSGeorge Bișoc {
568*b22eefacSGeorge Bișoc /*
569*b22eefacSGeorge Bișoc * We need to make sure that the quota usage
570*b22eefacSGeorge Bișoc * uniquely associated with the process is 0
571*b22eefacSGeorge Bișoc * on that moment the process gets destroyed.
572*b22eefacSGeorge Bișoc */
573*b22eefacSGeorge Bișoc ASSERT(Process->QuotaUsage[PsQuotaTypeIndex] == 0);
574*b22eefacSGeorge Bișoc }
575*b22eefacSGeorge Bișoc
576*b22eefacSGeorge Bișoc /* As the process is now gone, decrement the process count */
577*b22eefacSGeorge Bișoc InterlockedDecrementUL(&QuotaBlock->ProcessCount);
578*b22eefacSGeorge Bișoc }
579*b22eefacSGeorge Bișoc
580*b22eefacSGeorge Bișoc /* If no one is using this block, begin to destroy it */
581c2c66affSColin Finck if (QuotaBlock != &PspDefaultQuotaBlock &&
582c2c66affSColin Finck InterlockedDecrementUL(&QuotaBlock->ReferenceCount) == 0)
583c2c66affSColin Finck {
584*b22eefacSGeorge Bișoc /* Acquire the quota lock */
585c2c66affSColin Finck KeAcquireSpinLock(&PspQuotaLock, &OldIrql);
586*b22eefacSGeorge Bișoc
587*b22eefacSGeorge Bișoc /* Return all the quotas back to Mm and remove the quota from list */
588*b22eefacSGeorge Bișoc PspReturnQuotasOnDestroy(QuotaBlock);
589c2c66affSColin Finck RemoveEntryList(&QuotaBlock->QuotaList);
590*b22eefacSGeorge Bișoc
591*b22eefacSGeorge Bișoc /* Release the lock and free the block */
592c2c66affSColin Finck KeReleaseSpinLock(&PspQuotaLock, OldIrql);
593*b22eefacSGeorge Bișoc ExFreePoolWithTag(QuotaBlock, TAG_QUOTA_BLOCK);
594c2c66affSColin Finck }
595c2c66affSColin Finck }
596c2c66affSColin Finck
597*b22eefacSGeorge Bișoc /**
598*b22eefacSGeorge Bișoc * @brief
599*b22eefacSGeorge Bișoc * Returns the shared (paged and non paged)
600*b22eefacSGeorge Bișoc * pool quotas. The function is used exclusively
601*b22eefacSGeorge Bișoc * by the Object Manager to manage quota returns
602*b22eefacSGeorge Bișoc * handling of objects.
603*b22eefacSGeorge Bișoc *
604*b22eefacSGeorge Bișoc * @param[in] QuotaBlock
605*b22eefacSGeorge Bișoc * The quota block which quotas are to
606*b22eefacSGeorge Bișoc * be returned.
607*b22eefacSGeorge Bișoc *
608*b22eefacSGeorge Bișoc * @param[in] AmountToReturnPaged
609*b22eefacSGeorge Bișoc * The amount of paged quotas quotas to
610*b22eefacSGeorge Bișoc * be returned.
611*b22eefacSGeorge Bișoc *
612*b22eefacSGeorge Bișoc * @param[in] AmountToReturnNonPaged
613*b22eefacSGeorge Bișoc * The amount of non paged quotas to
614*b22eefacSGeorge Bișoc * be returned.
615*b22eefacSGeorge Bișoc *
616*b22eefacSGeorge Bișoc * @return
617*b22eefacSGeorge Bișoc * Nothing.
618*b22eefacSGeorge Bișoc */
619*b22eefacSGeorge Bișoc VOID
620*b22eefacSGeorge Bișoc NTAPI
PsReturnSharedPoolQuota(_In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,_In_ SIZE_T AmountToReturnPaged,_In_ SIZE_T AmountToReturnNonPaged)621*b22eefacSGeorge Bișoc PsReturnSharedPoolQuota(
622*b22eefacSGeorge Bișoc _In_ PEPROCESS_QUOTA_BLOCK QuotaBlock,
623*b22eefacSGeorge Bișoc _In_ SIZE_T AmountToReturnPaged,
624*b22eefacSGeorge Bișoc _In_ SIZE_T AmountToReturnNonPaged)
625*b22eefacSGeorge Bișoc {
626*b22eefacSGeorge Bișoc /* Sanity check */
627*b22eefacSGeorge Bișoc ASSERT(QuotaBlock);
628*b22eefacSGeorge Bișoc
629*b22eefacSGeorge Bișoc /* Return the pool quotas if there're any */
630*b22eefacSGeorge Bișoc if (AmountToReturnPaged != 0)
631*b22eefacSGeorge Bișoc {
632*b22eefacSGeorge Bișoc PspReturnProcessQuotaSpecifiedPool(NULL, QuotaBlock, PsPagedPool, AmountToReturnPaged);
633*b22eefacSGeorge Bișoc }
634*b22eefacSGeorge Bișoc
635*b22eefacSGeorge Bișoc if (AmountToReturnNonPaged != 0)
636*b22eefacSGeorge Bișoc {
637*b22eefacSGeorge Bișoc PspReturnProcessQuotaSpecifiedPool(NULL, QuotaBlock, PsNonPagedPool, AmountToReturnNonPaged);
638*b22eefacSGeorge Bișoc }
639*b22eefacSGeorge Bișoc
640*b22eefacSGeorge Bișoc DPRINT("PsReturnSharedPoolQuota(): Amount returned back (paged %lu -- non paged %lu)\n", AmountToReturnPaged, AmountToReturnNonPaged);
641*b22eefacSGeorge Bișoc
642*b22eefacSGeorge Bișoc /* Dereference the quota block */
643*b22eefacSGeorge Bișoc PspDereferenceQuotaBlock(NULL, QuotaBlock);
644*b22eefacSGeorge Bișoc }
645*b22eefacSGeorge Bișoc
646*b22eefacSGeorge Bișoc /**
647*b22eefacSGeorge Bișoc * @brief
648*b22eefacSGeorge Bișoc * Charges the shared (paged and non paged)
649*b22eefacSGeorge Bișoc * pool quotas. The function is used exclusively
650*b22eefacSGeorge Bișoc * by the Object Manager to manage quota charges
651*b22eefacSGeorge Bișoc * handling of objects.
652*b22eefacSGeorge Bișoc *
653*b22eefacSGeorge Bișoc * @param[in] Process
654*b22eefacSGeorge Bișoc * The process which quotas are to
655*b22eefacSGeorge Bișoc * be charged within its quota block.
656*b22eefacSGeorge Bișoc *
657*b22eefacSGeorge Bișoc * @param[in] AmountToChargePaged
658*b22eefacSGeorge Bișoc * The amount of paged quotas quotas to
659*b22eefacSGeorge Bișoc * be charged.
660*b22eefacSGeorge Bișoc *
661*b22eefacSGeorge Bișoc * @param[in] AmountToChargeNonPaged
662*b22eefacSGeorge Bișoc * The amount of non paged quotas to
663*b22eefacSGeorge Bișoc * be charged.
664*b22eefacSGeorge Bișoc *
665*b22eefacSGeorge Bișoc * @return
666*b22eefacSGeorge Bișoc * Returns the charged quota block, which it'll
667*b22eefacSGeorge Bișoc * be used by the Object Manager to attach
668*b22eefacSGeorge Bișoc * the charged quotas information to the object.
669*b22eefacSGeorge Bișoc * If the function fails to charge quotas, NULL
670*b22eefacSGeorge Bișoc * is returned to the caller.
671*b22eefacSGeorge Bișoc */
672*b22eefacSGeorge Bișoc PEPROCESS_QUOTA_BLOCK
673*b22eefacSGeorge Bișoc NTAPI
PsChargeSharedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T AmountToChargePaged,_In_ SIZE_T AmountToChargeNonPaged)674*b22eefacSGeorge Bișoc PsChargeSharedPoolQuota(
675*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
676*b22eefacSGeorge Bișoc _In_ SIZE_T AmountToChargePaged,
677*b22eefacSGeorge Bișoc _In_ SIZE_T AmountToChargeNonPaged)
678*b22eefacSGeorge Bișoc {
679*b22eefacSGeorge Bișoc NTSTATUS Status;
680*b22eefacSGeorge Bișoc
681*b22eefacSGeorge Bișoc /* Sanity checks */
682*b22eefacSGeorge Bișoc ASSERT(Process);
683*b22eefacSGeorge Bișoc ASSERT(Process->QuotaBlock);
684*b22eefacSGeorge Bișoc
685*b22eefacSGeorge Bișoc /* Do we have some paged pool quota to charge? */
686*b22eefacSGeorge Bișoc if (AmountToChargePaged != 0)
687*b22eefacSGeorge Bișoc {
688*b22eefacSGeorge Bișoc /* We do, charge! */
689*b22eefacSGeorge Bișoc Status = PspChargeProcessQuotaSpecifiedPool(NULL, Process->QuotaBlock, PsPagedPool, AmountToChargePaged);
690*b22eefacSGeorge Bișoc if (!NT_SUCCESS(Status))
691*b22eefacSGeorge Bișoc {
692*b22eefacSGeorge Bișoc DPRINT1("PsChargeSharedPoolQuota(): Failed to charge the shared pool quota (Status 0x%lx)\n", Status);
693*b22eefacSGeorge Bișoc return NULL;
694*b22eefacSGeorge Bișoc }
695*b22eefacSGeorge Bișoc }
696*b22eefacSGeorge Bișoc
697*b22eefacSGeorge Bișoc /* Do we have some non paged pool quota to charge? */
698*b22eefacSGeorge Bișoc if (AmountToChargeNonPaged != 0)
699*b22eefacSGeorge Bișoc {
700*b22eefacSGeorge Bișoc /* We do, charge! */
701*b22eefacSGeorge Bișoc Status = PspChargeProcessQuotaSpecifiedPool(NULL, Process->QuotaBlock, PsNonPagedPool, AmountToChargeNonPaged);
702*b22eefacSGeorge Bișoc if (!NT_SUCCESS(Status))
703*b22eefacSGeorge Bișoc {
704*b22eefacSGeorge Bișoc DPRINT1("PsChargeSharedPoolQuota(): Failed to charge the shared pool quota (Status 0x%lx). Attempting to return some paged pool back...\n", Status);
705*b22eefacSGeorge Bișoc PspReturnProcessQuotaSpecifiedPool(NULL, Process->QuotaBlock, PsPagedPool, AmountToChargePaged);
706*b22eefacSGeorge Bișoc return NULL;
707*b22eefacSGeorge Bișoc }
708*b22eefacSGeorge Bișoc }
709*b22eefacSGeorge Bișoc
710*b22eefacSGeorge Bișoc /* We have charged the quotas of an object, increment the reference */
711*b22eefacSGeorge Bișoc InterlockedIncrementSizeT(&Process->QuotaBlock->ReferenceCount);
712*b22eefacSGeorge Bișoc
713*b22eefacSGeorge Bișoc DPRINT("PsChargeSharedPoolQuota(): Amount charged (paged %lu -- non paged %lu)\n", AmountToChargePaged, AmountToChargeNonPaged);
714*b22eefacSGeorge Bișoc return Process->QuotaBlock;
715*b22eefacSGeorge Bișoc }
716*b22eefacSGeorge Bișoc
717*b22eefacSGeorge Bișoc /**
718*b22eefacSGeorge Bișoc * @brief
719*b22eefacSGeorge Bișoc * Charges the process page file quota.
720*b22eefacSGeorge Bișoc * The function is used internally by
721*b22eefacSGeorge Bișoc * the kernel.
722*b22eefacSGeorge Bișoc *
723*b22eefacSGeorge Bișoc * @param[in] Process
724*b22eefacSGeorge Bișoc * The process which page file quota is
725*b22eefacSGeorge Bișoc * to be charged.
726*b22eefacSGeorge Bișoc *
727*b22eefacSGeorge Bișoc * @param[in] Amount
728*b22eefacSGeorge Bișoc * The amount of page file quota to charge.
729*b22eefacSGeorge Bișoc *
730*b22eefacSGeorge Bișoc * @return
731*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS if quota charging has
732*b22eefacSGeorge Bișoc * been done with success, otherwise a NTSTATUS
733*b22eefacSGeorge Bișoc * code of STATUS_PAGEFILE_QUOTA_EXCEEDED is
734*b22eefacSGeorge Bișoc * returned.
735c2c66affSColin Finck */
736c2c66affSColin Finck NTSTATUS
737c2c66affSColin Finck NTAPI
PsChargeProcessPageFileQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)738*b22eefacSGeorge Bișoc PsChargeProcessPageFileQuota(
739*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
740*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
741c2c66affSColin Finck {
742c2c66affSColin Finck /* Don't do anything for the system process */
743c2c66affSColin Finck if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
744c2c66affSColin Finck
745*b22eefacSGeorge Bișoc return PspChargeProcessQuotaSpecifiedPool(Process, Process->QuotaBlock, PsPageFile, Amount);
746c2c66affSColin Finck }
747c2c66affSColin Finck
748*b22eefacSGeorge Bișoc /**
749*b22eefacSGeorge Bișoc * @brief
750*b22eefacSGeorge Bișoc * Charges the pool quota of a given process.
751*b22eefacSGeorge Bișoc * The kind of pool quota to charge is determined
752*b22eefacSGeorge Bișoc * by the PoolType parameter.
753*b22eefacSGeorge Bișoc *
754*b22eefacSGeorge Bișoc * @param[in] Process
755*b22eefacSGeorge Bișoc * The process which quota is to be
756*b22eefacSGeorge Bișoc * charged.
757*b22eefacSGeorge Bișoc *
758*b22eefacSGeorge Bișoc * @param[in] PoolType
759*b22eefacSGeorge Bișoc * The pool type to choose to charge quotas
760*b22eefacSGeorge Bișoc * (e.g. PagedPool or NonPagedPool).
761*b22eefacSGeorge Bișoc *
762*b22eefacSGeorge Bișoc * @param[in] Amount
763*b22eefacSGeorge Bișoc * The amount of quotas to charge into a process.
764*b22eefacSGeorge Bișoc *
765*b22eefacSGeorge Bișoc * @return
766*b22eefacSGeorge Bișoc * Nothing.
767*b22eefacSGeorge Bișoc *
768*b22eefacSGeorge Bișoc * @remarks
769*b22eefacSGeorge Bișoc * The function raises an exception if STATUS_QUOTA_EXCEEDED
770*b22eefacSGeorge Bișoc * status code is returned. Callers are responsible on their
771*b22eefacSGeorge Bișoc * own to handle the raised exception.
772c2c66affSColin Finck */
773c2c66affSColin Finck VOID
774c2c66affSColin Finck NTAPI
PsChargePoolQuota(_In_ PEPROCESS Process,_In_ POOL_TYPE PoolType,_In_ SIZE_T Amount)775*b22eefacSGeorge Bișoc PsChargePoolQuota(
776*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
777*b22eefacSGeorge Bișoc _In_ POOL_TYPE PoolType,
778*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
779c2c66affSColin Finck {
780c2c66affSColin Finck NTSTATUS Status;
781c2c66affSColin Finck ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
782c2c66affSColin Finck
783c2c66affSColin Finck /* Don't do anything for the system process */
784c2c66affSColin Finck if (Process == PsInitialSystemProcess) return;
785c2c66affSColin Finck
786c2c66affSColin Finck /* Charge the usage */
787c2c66affSColin Finck Status = PsChargeProcessPoolQuota(Process, PoolType, Amount);
788c2c66affSColin Finck if (!NT_SUCCESS(Status)) ExRaiseStatus(Status);
789c2c66affSColin Finck }
790c2c66affSColin Finck
791*b22eefacSGeorge Bișoc /**
792*b22eefacSGeorge Bișoc * @brief
793*b22eefacSGeorge Bișoc * Charges the non paged pool quota
794*b22eefacSGeorge Bișoc * of a given process.
795*b22eefacSGeorge Bișoc *
796*b22eefacSGeorge Bișoc * @param[in] Process
797*b22eefacSGeorge Bișoc * The process which non paged quota
798*b22eefacSGeorge Bișoc * is to be charged.
799*b22eefacSGeorge Bișoc *
800*b22eefacSGeorge Bișoc * @param[in] Amount
801*b22eefacSGeorge Bișoc * The amount of quotas to charge into a process.
802*b22eefacSGeorge Bișoc *
803*b22eefacSGeorge Bișoc * @return
804*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS if quota charing has
805*b22eefacSGeorge Bișoc * suceeded, STATUS_QUOTA_EXCEEDED is returned
806*b22eefacSGeorge Bișoc * otherwise to indicate the caller attempted
807*b22eefacSGeorge Bișoc * to charge quotas over the limits.
808c2c66affSColin Finck */
809c2c66affSColin Finck NTSTATUS
810c2c66affSColin Finck NTAPI
PsChargeProcessNonPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)811*b22eefacSGeorge Bișoc PsChargeProcessNonPagedPoolQuota(
812*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
813*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
814c2c66affSColin Finck {
815c2c66affSColin Finck /* Call the general function */
816c2c66affSColin Finck return PsChargeProcessPoolQuota(Process, NonPagedPool, Amount);
817c2c66affSColin Finck }
818c2c66affSColin Finck
819*b22eefacSGeorge Bișoc /**
820*b22eefacSGeorge Bișoc * @brief
821*b22eefacSGeorge Bișoc * Charges the paged pool quota of a
822*b22eefacSGeorge Bișoc * given process.
823*b22eefacSGeorge Bișoc *
824*b22eefacSGeorge Bișoc * @param[in] Process
825*b22eefacSGeorge Bișoc * The process which paged quota
826*b22eefacSGeorge Bișoc * is to be charged.
827*b22eefacSGeorge Bișoc *
828*b22eefacSGeorge Bișoc * @param[in] Amount
829*b22eefacSGeorge Bișoc * The amount of quotas to charge into a process.
830*b22eefacSGeorge Bișoc *
831*b22eefacSGeorge Bișoc * @return
832*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS if quota charing has
833*b22eefacSGeorge Bișoc * suceeded, STATUS_QUOTA_EXCEEDED is returned
834*b22eefacSGeorge Bișoc * otherwise to indicate the caller attempted
835*b22eefacSGeorge Bișoc * to charge quotas over the limits.
836c2c66affSColin Finck */
837c2c66affSColin Finck NTSTATUS
838c2c66affSColin Finck NTAPI
PsChargeProcessPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)839*b22eefacSGeorge Bișoc PsChargeProcessPagedPoolQuota(
840*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
841*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
842c2c66affSColin Finck {
843c2c66affSColin Finck /* Call the general function */
844c2c66affSColin Finck return PsChargeProcessPoolQuota(Process, PagedPool, Amount);
845c2c66affSColin Finck }
846c2c66affSColin Finck
847*b22eefacSGeorge Bișoc /**
848*b22eefacSGeorge Bișoc * @brief
849*b22eefacSGeorge Bișoc * Charges the process' quota pool.
850*b22eefacSGeorge Bișoc * The type of quota to be charged
851*b22eefacSGeorge Bișoc * depends upon the PoolType parameter.
852*b22eefacSGeorge Bișoc *
853*b22eefacSGeorge Bișoc * @param[in] Process
854*b22eefacSGeorge Bișoc * The process which quota is to
855*b22eefacSGeorge Bișoc * be charged.
856*b22eefacSGeorge Bișoc *
857*b22eefacSGeorge Bișoc * @param[in] PoolType
858*b22eefacSGeorge Bișoc * The type of quota pool to charge (e.g.
859*b22eefacSGeorge Bișoc * PagedPool or NonPagedPool).
860*b22eefacSGeorge Bișoc *
861*b22eefacSGeorge Bișoc * @param[in] Amount
862*b22eefacSGeorge Bișoc * The amount of quotas to charge into a process.
863*b22eefacSGeorge Bișoc *
864*b22eefacSGeorge Bișoc * @return
865*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS if quota charing has
866*b22eefacSGeorge Bișoc * suceeded, STATUS_QUOTA_EXCEEDED is returned
867*b22eefacSGeorge Bișoc * otherwise to indicate the caller attempted
868*b22eefacSGeorge Bișoc * to charge quotas over the limits.
869c2c66affSColin Finck */
870c2c66affSColin Finck NTSTATUS
871c2c66affSColin Finck NTAPI
PsChargeProcessPoolQuota(_In_ PEPROCESS Process,_In_ POOL_TYPE PoolType,_In_ SIZE_T Amount)872*b22eefacSGeorge Bișoc PsChargeProcessPoolQuota(
873*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
874*b22eefacSGeorge Bișoc _In_ POOL_TYPE PoolType,
875*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
876c2c66affSColin Finck {
877c2c66affSColin Finck /* Don't do anything for the system process */
878c2c66affSColin Finck if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
879c2c66affSColin Finck
880c2c66affSColin Finck return PspChargeProcessQuotaSpecifiedPool(Process,
881*b22eefacSGeorge Bișoc Process->QuotaBlock,
882c2c66affSColin Finck (PoolType & PAGED_POOL_MASK),
883c2c66affSColin Finck Amount);
884c2c66affSColin Finck }
885c2c66affSColin Finck
886*b22eefacSGeorge Bișoc /**
887*b22eefacSGeorge Bișoc * @brief
888*b22eefacSGeorge Bișoc * Returns the pool quota that the
889*b22eefacSGeorge Bișoc * process was taking up.
890*b22eefacSGeorge Bișoc *
891*b22eefacSGeorge Bișoc * @param[in] Process
892*b22eefacSGeorge Bișoc * The process which quota is to
893*b22eefacSGeorge Bișoc * be returned.
894*b22eefacSGeorge Bișoc *
895*b22eefacSGeorge Bișoc * @param[in] PoolType
896*b22eefacSGeorge Bișoc * The type of quota pool to return (e.g.
897*b22eefacSGeorge Bișoc * PagedPool or NonPagedPool).
898*b22eefacSGeorge Bișoc *
899*b22eefacSGeorge Bișoc * @param[in] Amount
900*b22eefacSGeorge Bișoc * The amount of quotas to return from a process.
901*b22eefacSGeorge Bișoc *
902*b22eefacSGeorge Bișoc * @return
903*b22eefacSGeorge Bișoc * Nothing.
904c2c66affSColin Finck */
905c2c66affSColin Finck VOID
906c2c66affSColin Finck NTAPI
PsReturnPoolQuota(_In_ PEPROCESS Process,_In_ POOL_TYPE PoolType,_In_ SIZE_T Amount)907*b22eefacSGeorge Bișoc PsReturnPoolQuota(
908*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
909*b22eefacSGeorge Bișoc _In_ POOL_TYPE PoolType,
910*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
911c2c66affSColin Finck {
912c2c66affSColin Finck /* Don't do anything for the system process */
913c2c66affSColin Finck if (Process == PsInitialSystemProcess) return;
914c2c66affSColin Finck
915c2c66affSColin Finck PspReturnProcessQuotaSpecifiedPool(Process,
916*b22eefacSGeorge Bișoc Process->QuotaBlock,
917c2c66affSColin Finck (PoolType & PAGED_POOL_MASK),
918c2c66affSColin Finck Amount);
919c2c66affSColin Finck }
920c2c66affSColin Finck
921*b22eefacSGeorge Bișoc /**
922*b22eefacSGeorge Bișoc * @brief
923*b22eefacSGeorge Bișoc * Returns the non paged quota pool
924*b22eefacSGeorge Bișoc * that the process was taking up.
925*b22eefacSGeorge Bișoc *
926*b22eefacSGeorge Bișoc * @param[in] Process
927*b22eefacSGeorge Bișoc * The process which non paged quota
928*b22eefacSGeorge Bișoc * is to be returned.
929*b22eefacSGeorge Bișoc *
930*b22eefacSGeorge Bișoc * @param[in] Amount
931*b22eefacSGeorge Bișoc * The amount of quotas to return from a process.
932*b22eefacSGeorge Bișoc *
933*b22eefacSGeorge Bișoc * @return
934*b22eefacSGeorge Bișoc * Nothing.
935c2c66affSColin Finck */
936c2c66affSColin Finck VOID
937c2c66affSColin Finck NTAPI
PsReturnProcessNonPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)938*b22eefacSGeorge Bișoc PsReturnProcessNonPagedPoolQuota(
939*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
940*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
941c2c66affSColin Finck {
942c2c66affSColin Finck /* Don't do anything for the system process */
943c2c66affSColin Finck if (Process == PsInitialSystemProcess) return;
944c2c66affSColin Finck
945c2c66affSColin Finck PsReturnPoolQuota(Process, NonPagedPool, Amount);
946c2c66affSColin Finck }
947c2c66affSColin Finck
948*b22eefacSGeorge Bișoc /**
949*b22eefacSGeorge Bișoc * @brief
950*b22eefacSGeorge Bișoc * Returns the paged pool quota that
951*b22eefacSGeorge Bișoc * the process was taking up.
952*b22eefacSGeorge Bișoc *
953*b22eefacSGeorge Bișoc * @param[in] Process
954*b22eefacSGeorge Bișoc * The process which paged pool
955*b22eefacSGeorge Bișoc * quota is to be returned.
956*b22eefacSGeorge Bișoc *
957*b22eefacSGeorge Bișoc * @param[in] Amount
958*b22eefacSGeorge Bișoc * The amount of quotas to return from a process.
959*b22eefacSGeorge Bișoc *
960*b22eefacSGeorge Bișoc * @return
961*b22eefacSGeorge Bișoc * Nothing.
962c2c66affSColin Finck */
963c2c66affSColin Finck VOID
964c2c66affSColin Finck NTAPI
PsReturnProcessPagedPoolQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)965*b22eefacSGeorge Bișoc PsReturnProcessPagedPoolQuota(
966*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
967*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
968c2c66affSColin Finck {
969c2c66affSColin Finck /* Don't do anything for the system process */
970c2c66affSColin Finck if (Process == PsInitialSystemProcess) return;
971c2c66affSColin Finck
972c2c66affSColin Finck PsReturnPoolQuota(Process, PagedPool, Amount);
973c2c66affSColin Finck }
974c2c66affSColin Finck
975*b22eefacSGeorge Bișoc /**
976*b22eefacSGeorge Bișoc * @brief
977*b22eefacSGeorge Bișoc * Returns the page file quota that the
978*b22eefacSGeorge Bișoc * process was taking up. The function
979*b22eefacSGeorge Bișoc * is used exclusively by the kernel.
980*b22eefacSGeorge Bișoc *
981*b22eefacSGeorge Bișoc * @param[in] Process
982*b22eefacSGeorge Bișoc * The process which pagefile quota is
983*b22eefacSGeorge Bișoc * to be returned.
984*b22eefacSGeorge Bișoc *
985*b22eefacSGeorge Bișoc * @param[in] Amount
986*b22eefacSGeorge Bișoc * The amount of quotas to return from a process.
987*b22eefacSGeorge Bișoc *
988*b22eefacSGeorge Bișoc * @return
989*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS.
990c2c66affSColin Finck */
991c2c66affSColin Finck NTSTATUS
992c2c66affSColin Finck NTAPI
PsReturnProcessPageFileQuota(_In_ PEPROCESS Process,_In_ SIZE_T Amount)993*b22eefacSGeorge Bișoc PsReturnProcessPageFileQuota(
994*b22eefacSGeorge Bișoc _In_ PEPROCESS Process,
995*b22eefacSGeorge Bișoc _In_ SIZE_T Amount)
996c2c66affSColin Finck {
997c2c66affSColin Finck /* Don't do anything for the system process */
998c2c66affSColin Finck if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
999c2c66affSColin Finck
1000*b22eefacSGeorge Bișoc PspReturnProcessQuotaSpecifiedPool(Process, Process->QuotaBlock, PsPageFile, Amount);
1001c2c66affSColin Finck return STATUS_SUCCESS;
1002c2c66affSColin Finck }
1003c2c66affSColin Finck
1004*b22eefacSGeorge Bișoc /**
1005*b22eefacSGeorge Bișoc * @brief
1006*b22eefacSGeorge Bișoc * This function adjusts the working set limits
1007*b22eefacSGeorge Bișoc * of a process and sets up new quota limits
1008*b22eefacSGeorge Bișoc * when necessary. The function is used
1009*b22eefacSGeorge Bișoc * when the caller requests to set up
1010*b22eefacSGeorge Bișoc * new working set sizes.
1011*b22eefacSGeorge Bișoc *
1012*b22eefacSGeorge Bișoc * @param[in] Process
1013*b22eefacSGeorge Bișoc * The process which quota limits or working
1014*b22eefacSGeorge Bișoc * set sizes are to be changed.
1015*b22eefacSGeorge Bișoc *
1016*b22eefacSGeorge Bișoc * @param[in] Unused
1017*b22eefacSGeorge Bișoc * This parameter is unused.
1018*b22eefacSGeorge Bișoc *
1019*b22eefacSGeorge Bișoc * @param[in] QuotaLimits
1020*b22eefacSGeorge Bișoc * An arbitrary pointer that points to a quota
1021*b22eefacSGeorge Bișoc * limits structure, needed to determine on
1022*b22eefacSGeorge Bișoc * setting up new working set sizes.
1023*b22eefacSGeorge Bișoc *
1024*b22eefacSGeorge Bișoc * @param[in] QuotaLimitsLength
1025*b22eefacSGeorge Bișoc * The length of QuotaLimits buffer, which size
1026*b22eefacSGeorge Bișoc * is expressed in bytes.
1027*b22eefacSGeorge Bișoc *
1028*b22eefacSGeorge Bișoc * @param[in] PreviousMode
1029*b22eefacSGeorge Bișoc * The processor level access mode.
1030*b22eefacSGeorge Bișoc *
1031*b22eefacSGeorge Bișoc * @return
1032*b22eefacSGeorge Bișoc * Returns STATUS_SUCCESS if the function has completed
1033*b22eefacSGeorge Bișoc * successfully. STATUS_INVALID_PARAMETER is returned if
1034*b22eefacSGeorge Bișoc * the caller has given a quota limits structure with
1035*b22eefacSGeorge Bișoc * invalid data. STATUS_INFO_LENGTH_MISMATCH is returned
1036*b22eefacSGeorge Bișoc * if the length of QuotaLimits pointed by QuotaLimitsLength
1037*b22eefacSGeorge Bișoc * is not right. STATUS_PRIVILEGE_NOT_HELD is returned if
1038*b22eefacSGeorge Bișoc * the calling thread of the process doesn't hold the necessary
1039*b22eefacSGeorge Bișoc * right privilege to increase quotas. STATUS_NO_MEMORY is
1040*b22eefacSGeorge Bișoc * returned if a memory pool allocation has failed. A failure
1041*b22eefacSGeorge Bișoc * NTSTATUS code is returned otherwise.
1042*b22eefacSGeorge Bișoc */
1043c2c66affSColin Finck NTSTATUS
1044c2c66affSColin Finck NTAPI
PspSetQuotaLimits(_In_ PEPROCESS Process,_In_ ULONG Unused,_In_ PVOID QuotaLimits,_In_ ULONG QuotaLimitsLength,_In_ KPROCESSOR_MODE PreviousMode)1045c2c66affSColin Finck PspSetQuotaLimits(
1046c2c66affSColin Finck _In_ PEPROCESS Process,
1047c2c66affSColin Finck _In_ ULONG Unused,
1048c2c66affSColin Finck _In_ PVOID QuotaLimits,
1049c2c66affSColin Finck _In_ ULONG QuotaLimitsLength,
1050c2c66affSColin Finck _In_ KPROCESSOR_MODE PreviousMode)
1051c2c66affSColin Finck {
1052c2c66affSColin Finck QUOTA_LIMITS_EX CapturedQuotaLimits;
1053c2c66affSColin Finck PEPROCESS_QUOTA_BLOCK QuotaBlock, OldQuotaBlock;
1054c2c66affSColin Finck BOOLEAN IncreaseOkay;
1055c2c66affSColin Finck KAPC_STATE SavedApcState;
1056c2c66affSColin Finck NTSTATUS Status;
1057c2c66affSColin Finck
1058c2c66affSColin Finck UNREFERENCED_PARAMETER(Unused);
1059c2c66affSColin Finck
1060c2c66affSColin Finck _SEH2_TRY
1061c2c66affSColin Finck {
1062c2c66affSColin Finck ProbeForRead(QuotaLimits, QuotaLimitsLength, sizeof(ULONG));
1063c2c66affSColin Finck
1064c2c66affSColin Finck /* Check if we have the basic or extended structure */
1065c2c66affSColin Finck if (QuotaLimitsLength == sizeof(QUOTA_LIMITS))
1066c2c66affSColin Finck {
1067c2c66affSColin Finck /* Copy the basic structure, zero init the remaining fields */
1068c2c66affSColin Finck RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS));
1069c2c66affSColin Finck CapturedQuotaLimits.WorkingSetLimit = 0;
1070c2c66affSColin Finck CapturedQuotaLimits.Reserved2 = 0;
1071c2c66affSColin Finck CapturedQuotaLimits.Reserved3 = 0;
1072c2c66affSColin Finck CapturedQuotaLimits.Reserved4 = 0;
1073c2c66affSColin Finck CapturedQuotaLimits.CpuRateLimit.RateData = 0;
1074c2c66affSColin Finck CapturedQuotaLimits.Flags = 0;
1075c2c66affSColin Finck }
1076c2c66affSColin Finck else if (QuotaLimitsLength == sizeof(QUOTA_LIMITS_EX))
1077c2c66affSColin Finck {
1078c2c66affSColin Finck /* Copy the full structure */
1079c2c66affSColin Finck RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS_EX));
1080c2c66affSColin Finck
1081c2c66affSColin Finck /* Verify that the caller passed valid flags */
1082c2c66affSColin Finck if ((CapturedQuotaLimits.Flags & ~VALID_QUOTA_FLAGS) ||
1083c2c66affSColin Finck ((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_ENABLE) &&
1084c2c66affSColin Finck (CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_DISABLE)) ||
1085c2c66affSColin Finck ((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_ENABLE) &&
1086c2c66affSColin Finck (CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_DISABLE)))
1087c2c66affSColin Finck {
1088c2c66affSColin Finck DPRINT1("Invalid quota flags: 0x%lx\n", CapturedQuotaLimits.Flags);
1089c2c66affSColin Finck _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
1090c2c66affSColin Finck }
1091c2c66affSColin Finck
1092c2c66affSColin Finck /* Verify that the caller didn't pass reserved values */
1093c2c66affSColin Finck if ((CapturedQuotaLimits.WorkingSetLimit != 0) ||
1094c2c66affSColin Finck (CapturedQuotaLimits.Reserved2 != 0) ||
1095c2c66affSColin Finck (CapturedQuotaLimits.Reserved3 != 0) ||
1096c2c66affSColin Finck (CapturedQuotaLimits.Reserved4 != 0) ||
1097c2c66affSColin Finck (CapturedQuotaLimits.CpuRateLimit.RateData != 0))
1098c2c66affSColin Finck {
1099c2c66affSColin Finck DPRINT1("Invalid value: (%lx,%lx,%lx,%lx,%lx)\n",
1100c2c66affSColin Finck CapturedQuotaLimits.WorkingSetLimit,
1101c2c66affSColin Finck CapturedQuotaLimits.Reserved2,
1102c2c66affSColin Finck CapturedQuotaLimits.Reserved3,
1103c2c66affSColin Finck CapturedQuotaLimits.Reserved4,
1104c2c66affSColin Finck CapturedQuotaLimits.CpuRateLimit.RateData);
1105c2c66affSColin Finck _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
1106c2c66affSColin Finck }
1107c2c66affSColin Finck }
1108c2c66affSColin Finck else
1109c2c66affSColin Finck {
1110c2c66affSColin Finck DPRINT1("Invalid quota size: 0x%lx\n", QuotaLimitsLength);
1111c2c66affSColin Finck _SEH2_YIELD(return STATUS_INFO_LENGTH_MISMATCH);
1112c2c66affSColin Finck }
1113c2c66affSColin Finck }
1114c2c66affSColin Finck _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1115c2c66affSColin Finck {
1116c2c66affSColin Finck DPRINT1("Exception while copying data\n");
1117c2c66affSColin Finck _SEH2_YIELD(return _SEH2_GetExceptionCode());
1118c2c66affSColin Finck }
1119c2c66affSColin Finck _SEH2_END;
1120c2c66affSColin Finck
1121c2c66affSColin Finck /* Check the caller changes the working set size limits */
1122c2c66affSColin Finck if ((CapturedQuotaLimits.MinimumWorkingSetSize != 0) &&
1123c2c66affSColin Finck (CapturedQuotaLimits.MaximumWorkingSetSize != 0))
1124c2c66affSColin Finck {
1125c2c66affSColin Finck /* Check for special case: trimming the WS */
1126c2c66affSColin Finck if ((CapturedQuotaLimits.MinimumWorkingSetSize == SIZE_T_MAX) &&
1127c2c66affSColin Finck (CapturedQuotaLimits.MaximumWorkingSetSize == SIZE_T_MAX))
1128c2c66affSColin Finck {
1129c2c66affSColin Finck /* No increase allowed */
1130c2c66affSColin Finck IncreaseOkay = FALSE;
1131c2c66affSColin Finck }
1132c2c66affSColin Finck else
1133c2c66affSColin Finck {
1134c2c66affSColin Finck /* Check if the caller has the required privilege */
1135c2c66affSColin Finck IncreaseOkay = SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege,
1136c2c66affSColin Finck PreviousMode);
1137c2c66affSColin Finck }
1138c2c66affSColin Finck
1139c2c66affSColin Finck /* Attach to the target process and disable APCs */
1140c2c66affSColin Finck KeStackAttachProcess(&Process->Pcb, &SavedApcState);
1141c2c66affSColin Finck KeEnterGuardedRegion();
1142c2c66affSColin Finck
1143c2c66affSColin Finck /* Call Mm to adjust the process' working set size */
1144c2c66affSColin Finck Status = MmAdjustWorkingSetSize(CapturedQuotaLimits.MinimumWorkingSetSize,
1145c2c66affSColin Finck CapturedQuotaLimits.MaximumWorkingSetSize,
1146c2c66affSColin Finck 0,
1147c2c66affSColin Finck IncreaseOkay);
1148c2c66affSColin Finck
1149c2c66affSColin Finck /* Bring back APCs and detach from the process */
1150c2c66affSColin Finck KeLeaveGuardedRegion();
1151c2c66affSColin Finck KeUnstackDetachProcess(&SavedApcState);
1152c2c66affSColin Finck }
1153c2c66affSColin Finck else if (Process->QuotaBlock == &PspDefaultQuotaBlock)
1154c2c66affSColin Finck {
1155c2c66affSColin Finck /* Check if the caller has the required privilege */
1156c2c66affSColin Finck if (!SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege, PreviousMode))
1157c2c66affSColin Finck {
1158c2c66affSColin Finck return STATUS_PRIVILEGE_NOT_HELD;
1159c2c66affSColin Finck }
1160c2c66affSColin Finck
1161c2c66affSColin Finck /* Allocate a new quota block */
1162c2c66affSColin Finck QuotaBlock = ExAllocatePoolWithTag(NonPagedPool,
1163c2c66affSColin Finck sizeof(EPROCESS_QUOTA_BLOCK),
1164c2c66affSColin Finck TAG_QUOTA_BLOCK);
1165c2c66affSColin Finck if (QuotaBlock == NULL)
1166c2c66affSColin Finck {
1167c2c66affSColin Finck ObDereferenceObject(Process);
1168c2c66affSColin Finck return STATUS_NO_MEMORY;
1169c2c66affSColin Finck }
1170c2c66affSColin Finck
1171c2c66affSColin Finck /* Initialize the quota block */
1172c2c66affSColin Finck QuotaBlock->ReferenceCount = 1;
1173c2c66affSColin Finck QuotaBlock->ProcessCount = 1;
11746170b574SGeorge Bișoc QuotaBlock->QuotaEntry[PsNonPagedPool].Peak = Process->QuotaPeak[PsNonPagedPool];
11756170b574SGeorge Bișoc QuotaBlock->QuotaEntry[PsPagedPool].Peak = Process->QuotaPeak[PsPagedPool];
11766170b574SGeorge Bișoc QuotaBlock->QuotaEntry[PsPageFile].Peak = Process->QuotaPeak[PsPageFile];
11776170b574SGeorge Bișoc QuotaBlock->QuotaEntry[PsNonPagedPool].Limit = PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit;
11786170b574SGeorge Bișoc QuotaBlock->QuotaEntry[PsPagedPool].Limit = PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit;
11796170b574SGeorge Bișoc QuotaBlock->QuotaEntry[PsPageFile].Limit = PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit;
1180c2c66affSColin Finck
1181c2c66affSColin Finck /* Try to exchange the quota block, if that failed, just drop it */
1182c2c66affSColin Finck OldQuotaBlock = InterlockedCompareExchangePointer((PVOID*)&Process->QuotaBlock,
1183c2c66affSColin Finck QuotaBlock,
1184c2c66affSColin Finck &PspDefaultQuotaBlock);
1185c2c66affSColin Finck if (OldQuotaBlock == &PspDefaultQuotaBlock)
1186c2c66affSColin Finck {
1187c2c66affSColin Finck /* Success, insert the new quota block */
1188c2c66affSColin Finck PspInsertQuotaBlock(QuotaBlock);
1189c2c66affSColin Finck }
1190c2c66affSColin Finck else
1191c2c66affSColin Finck {
1192c2c66affSColin Finck /* Failed, free the quota block and ignore it */
1193c2c66affSColin Finck ExFreePoolWithTag(QuotaBlock, TAG_QUOTA_BLOCK);
1194c2c66affSColin Finck }
1195c2c66affSColin Finck
1196c2c66affSColin Finck Status = STATUS_SUCCESS;
1197c2c66affSColin Finck }
119892b8d327STimo Kreuzer else
119992b8d327STimo Kreuzer {
120092b8d327STimo Kreuzer Status = STATUS_SUCCESS;
120192b8d327STimo Kreuzer }
1202c2c66affSColin Finck
1203c2c66affSColin Finck return Status;
1204c2c66affSColin Finck }
1205c2c66affSColin Finck
1206c2c66affSColin Finck /* EOF */
1207