See this command's documentation:
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-dbgprint
and the section "DbgPrint buffer and the debugger"
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/reading-and-filtering-debugging-messages#dbgprint-buffer-and-the-debugger
for more details.
- Loosely implement the function, based on our existing circular printout
buffers in kdio.c.
- Enable its usage in the KdpPrint() and KdpPrompt() functions.
Notice that this function will *only* capture the strings being sent **to**
the debugger, and not the strings the debugger itself produce. (This means
that we cannot use the KdPrintCircularBuffer as a replacement for our
KDBG dmesg one, for example...)
How to test:
Run ReactOS under WinDbg, and use the !dbgprint command to view the
buffer. You can also use the Memory Window, place yourself at the
address pointed by KdPrintCircularBuffer and KdPrintWritePointer, and
read its contents.
What you should observe:
Prior notice: The circular buffer in debug builds of ReactOS and Windows
is 0x8000 bytes large. In release builds, its size is down to 0x1000.
1- When you start e.g. the 2nd-stage GUI installation of ReactOS, going
past the initial "devices installation" and letting it stabilize on
the Welcome page, break into WinDbg and run the !dbgprint command. You
should notice that the end of its output is weirdly truncated, compared
to what has been actually emitted to the debug output. Comparing this
with the actual contents of the circular buffer (via Memory Window),
shows that the buffer contents is actually correct.
2- Copy all the text that has been output by the !dbgprint command and
paste it in an editor; count the number of all characters appearing +
newlines (only CR or LF), and observe that this number is "mysteriously"
equal to 16384 == 0x4000.
3- Continue running ReactOS installation for a little while, breaking back
back into WinDbg and looking at !dbgprint again. Its output seems to be
still stopping at the same place as before (but the actual buffer memory
contents shows otherwise). Continue running ROS installation, and break
into the debugger when ROS is about to restart. You should now observe
that the dbgprint buffer rolled over:
dd nt!KdPrintRolloverCount shows 1.
Carefully analysing the output of !dbgprint, however, you will notice
that it looks a bit garbage-y: the first part of the output is actually
truncated after 16384 characters, then you get a second part of the
buffer showing what ReactOS was printing while shutting down. Then
you get again what was shown at the top of the !dbgprint output.
(Of course, comparing with the actual contents of the circular buffer
in memory shows that its contents are fine...)
The reason of these strange observations, is because there is an intrinsic
bug in the !dbgprint command implementation (in kdexts.dll). Essentially,
it displays the contents of the circular buffer in two single dprintf()
calls: one for the "older" (bottom) part of the buffer:
[WritePointer, EndOfBuffer]
and one for the "newer" (upper) part of the buffer:
[CircularBuffer, WritePointer[ .
The first aspect of the bug (causing observation 3), is that those two
parts are not necessarily NULL-terminated strings (especially after
rollover), so for example, displaying the upper part of the buffer, will
potentially also display part of the buffer's bottom part.
The second aspect of the bug (explaining observations 1 and 2), is due
to the implementation of the dprintf() function (callback in dbgenv.dll).
There, it uses a fixed-sized buffer of size 0x4000 == 16384 characters.
Since the output of the circular buffer is not done by little chunks,
but by the two large parts, if any of those are larger than 0x4000 they
get truncated on display.
(This last observation is confirmed in a completely different context by
https://community.osr.com/discussion/112439/dprintf-s-max-string-length .)
But the underlying GCC stupidity is still there (15 years later).
However, enable it only in 32-bit GCC builds, not in 64-bits nor with MSVC.
See commit b9cd3f2d9 (r25845) for some details.
GCC is indeed still incapable of casting 32-bit pointers up to 64-bits,
when static-initializing arrays (**outside** a function) without emitting
the error:
"error: initializer element is not constant"
(which might somehow indicate it actually tries to generate executable
code for casting the pointers, instead of doing it at compile-time).
Going down the rabbit hole, other stupidities show up:
Our PVOID64 type and the related POINTER_64 (in 32-bit archs), or the
PVOID32 and POINTER_32 (in 64-bit archs), are all silently broken in
GCC builds, because the pointer size attributes __ptr64 and __ptr32,
which are originally MSVC-specific, are defined to nothing in _mingw.h.
(And similarly for the __uptr and __sptr sign-extension attributes.)
Clang and other sane ompilers has since then implemented those (enabled
with -fms-extensions), but not GCC. The closest thing that could exist
for GCC is to do:
#define __ptr64 __attribute__((mode(DI)))
in order to get a 64-bit-sized pointer type with
typedef void* __ptr64 PVOID64;
but even this does not work, with the error:
"error: invalid pointer mode 'DI'"
Choose the correct element of the KiUnexpectedRange array,
depending on the interrupt vector, the same way as here:
a2c6af0da4/ntoskrnl/ke/amd64/except.c (L77)
And guard KeConnectInterrupt() execution with dispatcher lock.
CORE-14922
- Line-wrapping is enabled with 'ESC[?7h' (the '?' was forgotten).
Notice that the following reference also shows it wrong:
https://www.cse.psu.edu/~kxc104/class/cse472/09s/hw/hw7/vt100ansi.htm
- Terminal type is actually queried with 'ESC Z' (VT52-compatible), or
with 'ESC[c' (VT100-compatible). The antediluvian CTRL-E ('\x05')
control code gives instead a user-configurable (usually empty) string,
so it's not reliable.
Also, we don't really care about the returned result, we just need to
know that one has been sent.
Cross-checked with online documentation:
* "Digital VT100 User Guide" (EK-VT100-UG-001) (1st edition, August 1978,
reviewed November 1978).
* https://www.real-world-systems.com/docs/ANSIcode.html
* https://geoffg.net/Downloads/Terminal/TerminalEscapeCodes.pdf
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* https://en.wikipedia.org/wiki/Enquiry_character
- Retrieve the size of the *controlling terminal* with escape sequences
only when it's a serial one: serial output is enabled *and* KDSERIAL
is set (i.e. user input through serial). See code for the actual logic
(the corresponding truth table is left as an exercise for the reader).
- Fix also a "Buffer" vs. "InBuffer" mismatch, that caused the whole
code to fail.
- For fallback terminal size values, use meaningful ones when SCREEN
is instead the controlling "terminal" (based on full-size BOOTVID
values).
- When echoing read characters during line-cooking, do direct output by
using KdpDprintf() instead of going through the heavy KdbpPrint() function.
This fixes some input artifacts like: 1. extra slowdowns due to
querying terminal size at each keypress, and 2. getting obnoxious
"--- Press q to abort ..." prompts in the middle of typing a long
comamnd because you are at the very bottom of the screen.
- Remove KdbInit() macro and directly use KdbpCliInit() (since the place
where it was used was already within an #ifdef KDBG block).
- Declare KdpKdbgInit() only when KDBG is defined, move its definition
into kdio.c and remove the legacy wrappers/kdbg.c file.
And in KdbInitialize(), set KdpInitRoutine directly to the former,
instead of using the KdpKdbgInit indirection.
- Don't reset KdComPortInUse in KdpDebugLogInit().
- Minor refactorings: KdpSerialDebugPrint -> KdpSerialPrint and make it
static; argument name "Message" -> "String", "StringLength" -> "Length".
What we have:
- Maximum number of pagefiles: 16
- Minimum pagefile size: 256 pages (1 MB when page size = 4096 bytes)
- Maximum pagefile size:
* 32-bit platforms: (1024 * 1024 - 1) pages (~ 4095 MB)
* x86 with PAE support: same size as for AMD x64
* x64 platform: (4 * 1024 * 1024 * 1024 - 1) pages (~ 16 TB)
* IA64 platform: (8 * 1024 * 1024 * 1024 - 1) pages (~ 32 TB)
Those are the values as supported and verified by the NT kernel.
Now, user-mode programs (including SMSS.EXE) have different opinions
on these, namely, they consider estimates directly in MB, respectively:
4095 MB, (16 * 1024 * 1024) MB, and (32 * 1024 * 1024) MB
(verified on Win2k3 and Win7 32 and 64 bits).
Also here, the minimum pagefile size is set to 2 MB.
Starting Windows 8+ (and 10), those values change slightly, and are
still not fully synchronized between NTOS:MM and SMSS. Finally, while
(x86 PAE and) AMD64 and ARM64 seem to share the maximum pagefile
size limit, 32-bit ARMv7 appears to use different limits than regular
x86 (2 GB instead of 4).
Please keep those values as they are for NT compatibility!
See the following references:
https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/modwrite/create.htmhttps://techcommunity.microsoft.com/t5/ask-the-performance-team/what-is-the-page-file-for-anyway/ba-p/372608
+ Manual extraction of the values from different NT 6.2,6.3,10 builds.
[SMSS] Fill out in particular the x86-specific case for PAE.
[NTOS:MM] Some cleanup in the NtCreatePagingFile() code, namely:
- Clarify some comments;
- Validate the lower and upper bounds of the Minimum and Maximum sizes
(based on Windows behaviour as explained by Geoff + manual tests).
- Open the pagefile in case-insensitive;
- Simplify the loop that finds an existing matching pagefile;
- Simplify some failure exit paths;
- Add a "Missing validation steps TODO" comment block explaining the
existing code-hole.
The "failed to grant access rights" message isn't enough to understand what kind of access rights haven't been granted and why. Dumping information of the captured security descriptor, the ACL and its ACEs with mask rights and token SIDs should be enough to understand the reason of the failure in question.
debug.c will serve as a centralized facility for security debugging routines and everything related to that. This file will be expanded with further debug functions for the Security subsystem if needed.
Return TRUE instead of NTSTATUS code which has a value of FALSE and may confuse caller.
Fixes sporadic 0x7B bugcheck when booting from corrupted NTFS volume using WinXP ntfs.sys.
PsIdleProcess and PsInitialSystemProcess share the same handle table. This
leads ObGetProcessHandleCount() to report the same number of handles
when called on those system processes, when being enumerated by
NtQuerySystemInformation(SystemProcessInformation).
Instead, just return 0 for the handle count of the Idle process in SystemProcessInformation.
This is not done in ObGetProcessHandleCount(), since a separate
NtQueryInformationProcess(ProcessHandleCount) for the idle process should return
a non-zero value.
CORE-16577
Not only primary group assignation was broken but new dynamic length calculation is also broken. The length of the captured SID is not taken into account so the new dynamic length gets only the size of the default ACL present in an access token.
Therefore, the condition is always FALSE and the code never jumps to the STATUS_ALLOTTED_SPACE_EXCEEDED branch because the length will always be small than the charged dynamic length.
Addendum to 86bde3c.
With current master, what happens is that when someone wants to assign a new primary group SID for an access token, it results in an instant page fault because the primary group variable doesn't get assigned the dynamic part's address.
So the primary group variable gets an address which is basically a representation of the ACL size, hence the said address is bogus and it's where the page fault kicks in.
CORE-18249
Since we were charging the pool quota after the VAD insertion,
if the quota charge failed, the VAD would still have been inserted.
This commit attempts to resolve this issue by charging quota
before inserting the VAD thus allowing the quota charge to fail early.
Addendum to 884356a0. CORE-18028
On current master, ReactOS faces these problems:
- ObCreateObject charges both paged and non paged pool a size of TOKEN structure, not the actual dynamic contents of WHAT IS inside a token. For paged pool charge the size is that of the dynamic area (primary group + default DACL if any). This is basically what DynamicCharged is for.
For the non paged pool charge, the actual charge is that of TOKEN structure upon creation. On duplication and filtering however, the paged pool charge size is that of the inherited dynamic charged space from an existing token whereas the non paged pool size is that of the calculated token body
length for the new duplicated/filtered token. On current master, we're literally cheating the kernel by charging the wrong amount of quota not taking into account the dynamic contents which they come from UM.
- Both DynamicCharged and DynamicAvailable are not fully handled (DynamicAvailable is pretty much poorly handled with some cases still to be taking into account). DynamicCharged is barely handled, like at all.
- As a result of these two points above, NtSetInformationToken doesn't check when the caller wants to set up a new default token DACL or primary group if the newly DACL or the said group exceeds the dynamic charged boundary. So what happens is that I'm going to act like a smug bastard fat politician and whack
the primary group and DACL of an token however I want to, because why in the hell not? In reality no, the kernel has to punish whoever attempts to do that, although we currently don't.
- The dynamic area (aka DynamicPart) only picks up the default DACL but not the primary group as well. Generally the dynamic part is composed of primary group and default DACL, if provided.
In addition to that, we aren't returning the dynamic charged and available area in token statistics. SepComputeAvailableDynamicSpace helper is here to accommodate that. Apparently Windows is calculating the dynamic available area rather than just querying the DynamicAvailable field directly from the token.
My theory regarding this is like the following: on Windows both TokenDefaultDacl and TokenPrimaryGroup classes are barely used by the system components during startup (LSASS provides both a DACL and primary group when calling NtCreateToken anyway). In fact DynamicAvailable is 0 during token creation, duplication and filtering when inspecting a token with WinDBG. So
if an application wants to query token statistics that application will face a dynamic available space of 0.
On ObpChargeQuotaForObject function, the kernel will either charge the default object type charges or the specified information charges obtained from ObCreateObject API call. What happens is that if a paged pool charge is specified on ObCreateObject call the kernel will charge that
but when an object is about to be de-allocated, the amount of quota to return back to the system is the amounting of the paged pool charge specified previously by the ObCreateObject call plus the amounting of the security descriptor charge (see oblife.c / line 98).
This will result in a fatal crash with a bugcheck of QUOTA_UNDERFLOW because we are returning quota with bits of it that was never charged and that's SecurityDescriptorCharge. A QUOTA_UNDERFLOW bugcheck occurs in two following scenarios:
-- When installing Virtualbox Guest Additions and prompting the installer to reboot the system for you
-- When logging off and on back to the system and then you restart the system normally
This bug has been discovered whilst working on #4555 PR.
TokenGroupsAndPrivileges is the younger sister of two TokenGroups and TokenPrivileges classes. In its purpose there's no huge substantial differences apart that this class comes with its own structure, TOKEN_GROUPS_AND_PRIVILEGES, and that this structure comes with extra information.
In NtQueryInformationToken function, remove the useless and redundant NULL check for two primary reasons. First, DefaultQueryInfoBufferCheck already does the necessary probing validation checks and second, ReturnLength must NEVER be NULL!
If the caller does not respect the calling rules of NtQueryInformationToken, the caller is expected to be miserably punished.
NtQueryInformationToken is by far the only system call in NT where ReturnLength simply cannot be optional. On Windows this parameter is always probed and an argument to NULL directly leads to an access violation exception.
This is due to the fact of how tokens work, as its information contents (token user, owner, primary group, et al) are dynamic and can vary throughout over time in memory.
What happens on current ReactOS master however is that ReturnLength is only probed if the parameter is not NULL. On a NULL case scenario the probing checks succeed and NtQueryInformationToken fails later. For this, just get rid of CompleteProbing
parameter and opt in for a bit mask flag based approach, with ICIF_FORCE_RETURN_LENGTH_PROBE being set on DefaultQueryInfoBufferCheck which NtQueryInformationToken calls it to do sanity checks.
In addition to that...
- Document the ICIF probe helpers
- Annotate the ICIF prope helpers with SAL
- With the riddance of CompleteProbing and adoption of flags based approach, add ICIF_PROBE_READ_WRITE and ICIF_PROBE_READ flags alongside with ICIF_FORCE_RETURN_LENGTH_PROBE
The current state of Security manager's code is kind of a mess. Mainly, there's code scattered around places where they shouldn't belong and token implementation (token.c) is already of a bloat in itself as it is. The file has over 6k lines and it's subject to grow exponentially with improvements, features, whatever that is.
With that being said, the token implementation code in the kernel will be split accordingly and rest of the code moved to appropriate places. The new layout will look as follows (excluding the already existing files):
- client.c (Client security implementation code)
- objtype.c (Object type list implementation code -- more code related to object types will be put here when I'm going to implement object type access checks in the future)
- subject.c (Subject security context support)
The token implementation in the kernel will be split in 4 distinct files as shown:
- token.c (Base token support routines)
- tokenlif.c (Life management of a token object -- that is Duplication, Creation and Filtering)
- tokencls.c (Token Query/Set Information Classes support)
- tokenadj.c (Token privileges/groups adjusting support)
In addition to that, tidy up the internal header and reorganize it as well.
Fix MiInsertSharedUserPageVad to not charge the system process pool quota.
Even though PsChargeProcessNonPagedPoolQuota itself checks if the process specified is the system process, this doesn't work here as we're too early into boot for the kernel to know what the system process is.
This commit fully implements the inner logic of KeSaveFloatingPointState and KeRestoreFloatingPointState routines. On ReactOS we're currently simply doing a FNSAVE operation whereas on Windows it is a lot more than that.
On Windows Server 2003 the logic more or less goes like this. In order to save the FPU state the NPX state of the current thread has to be checked first, that is, if NPX is loaded and currently charged for the current thread then the system will acquire the NPX registers actively present. From that point it performs either a FNSAVE or FXSAVE
if FX is actually supported. Otherwise the control word and MXCsr registers are obtained.
FXSAVE/FNSAVE operation is done solely if the FX save area is held up in a pool allocation. Pool allocation occurs if it's been found out that the NPX IRQL of the thread is not the same as the current thread which from where it determines if the interrupt level is APC then allocate some pool memory and hold the save area there, otherwise
the save area in question is grabbed from the current processor control region. If NPX is not loaded for the current thread then the FPU state is obtained from the NPX frame.
In our case we'll be doing something way simpler. Only do a FXSAVE/FNSAVE directly of the FPU state registers, in this way we are simplifying the code and the actual logic of Save/Restore mechanism.
This is needed to store FPU state information when saving or restoring the floating point state of a system due to a call to KeSaveFloatingPointState or KeRestoreFloatingPointState.
- Add a check for correct PDO before doing anything
- Process the request only for started devices
- Send the request synchronously during the start sequence
This makes Windows' i8042prt.sys work on ReactOS.
Addendum to cf0bc1c132
The problem is that EndMem is changed to point to the DynamicPart of
the token, but the code after that expects it to still point into the
VariablePart instead.
Problem fixed by moving the insertion of RestrictedSids much sooner
(where the original ones are also being copied).
Shared locking must be used on the source token as it is accessed for
reading only. This fixes in particular the kmtest SeTokenFiltering that
would hang otherwise on a (wrong) exclusive locking.
- SepPerformTokenFiltering(): Always shared-lock the source token.
Its callers (NtFilterToken and SeFilterToken) just need to sanitize and
put the parameters in correct form before calling this helper function.
- Sync comments in NtFilterToken() with SeFilterToken().
This function is either called inter-kernel (in which case, all
parameters must be valid, and if not, we have to bugcheck), or, it
is called with **captured** parameters (from NtFilterToken) and those
latter ones are now expected to be valid and reside in kernel-mode.
Finally, data copied between token structures reside in kernel-mode
only and again are expected to be valid (if not, we bugcheck).
- The ACL is however not validated when the function is called within
kernel mode and no capture is actually being done.
- Simplify aspects of the function (returning early when possible).
If inserting the allocated VAD fails, MiMapViewOfDataSection will make no attempt to free the allocated VAD. Nor will it call MiDereferenceControlArea(ControlArea); like other failure return paths. This commit fixes this behavior.
Co-authored-by: Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito@reactos.org>
We are allocating blocks of pool memory for a security descriptor with its own specific tag, TAG_SEC_QUERY, so just use it when freeing when releasing the descriptor as well (aka freeing the said pool).
There are two fundamental problems when it comes to access checks in ReactOS. First, the internal function SepAccessCheck which is the heart and brain of the whole access checks logic of the kernel warrants access to the calling thread of a process to an object even though access could not be given.
This can potentially leave security issues as we literally leave objects to be touched indiscriminately by anyone regardless of their ACEs in the DACL of a security descriptor. Second, the current access check code doesn't take into account the fact that an access token can have restricted SIDs. In such scenario we must perform additional access checks by iterating over the restricted SIDs of the primary token by comparing the SID equality and see if the group can be granted certain rights based on the ACE policy that represents the same SID.
Part of SepAccessCheck's code logic will be split for a separate private kernel routine, SepAnalyzeAcesFromDacl. The reasons for this are primarily two -- such code is subject to grow eventually as we'll support different type ACEs and handle them accordingly -- and we avoid further code duplicates. On Windows Server 2003 there are 5 different type of ACEs that are supported for access checks:
- ACCESS_DENIED_ACE_TYPE (supported by ReactOS)
- ACCESS_ALLOWED_ACE_TYPE (supported by ReactOS)
- ACCESS_DENIED_OBJECT_ACE_TYPE
- ACCESS_ALLOWED_OBJECT_ACE_TYPE
- ACCESS_ALLOWED_COMPOUND_ACE_TYPE
This gives the opportunity for us to have a semi serious kernel where security of objects are are taken into account, rather than giving access to everyone.
CORE-9174
CORE-9175
CORE-9184
CORE-14520
SepSidInTokenEx function already provides the necessary mechanism to handle scenario where a token has restricted SIDs or a principal SID is given to the call. There's no reason to have these redundant ASSERTs anymore.
In addition to that make sure if the SID is not a restricted and if that SID is the first element on the array and it's enabled, this is the primary user.
This function will be used to retrieve a security identifier from a valid access control entry in the kernel. Mostly and exclusively used within access checks related code and such.
Implement the correct start-stop sequence for resource rebalancing
without the actual rebalancing. Also move IoInvalidateDeviceState
processing into the enumeration thread as it should be.
CORE-17519
Implement initial token debug code. For now debug information that is being tracked are: process image file name, process and thread client IDs and token creation method. More specific debug code can be added later only if needed.
As for the token creation method, this follows the same principle as on Windows where the creation method is defined by a value denoting the first letter of the said method of creation. That is, 0xC is for token creation, 0xD is for token duplication and 0xF is for token filtering. The debug field names are taken from Windows PDB symbols for WinDBG debug extension support purposes. The names must not be changed!
- Add a new cmboot.h header to isolate the boot-support definitions
shared with the NT/ReactOS bootloader.
- Move CmpFreeDriverList() to cmboot.c so that we can use it for
cleanup paths in the NT/ReactOS bootloader.
- CmpFindControlSet(): Directly build the control set name in UNICODE,
instead of doing an ANSI->UNICODE conversion.
- Directly assign the CurrentControlSet\Services constant string,
instead of going the route of init-empty-string + append-string.
This is possible since that string is not modified later.
- Remove ASSERT(FALSE), replacing them with correct failure handling.
- Add cleanup paths in CmpAddDriverToList().
- Simplify and fix CmpFreeDriverList(): it's the full DriverNode
that needs to be freed; not the LIST_ENTRY pointer.
- Add other validity checks:
* Registry value types and data sizes;
* For multi-strings, verify that they are NULL-terminated.
* For (multi-)strings, check whether they are NULL-terminated before
optionally removing their trailing NULL character from the count.
Check also whether they are of zero-length and take appropriate
action where necessary.
- Add CmpIsDriverInList() for future usage in CMBOOT compiled in
bootloader mode.
- Add SAL annotations and Doxygen documentation.
- Add debug traces.
- Formatting / code style fixes.
** TODO: Fix SafeBoot support **