From a9950ddf1541542dc0183af49a9036207bff3880 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Tue, 14 Jul 2026 15:28:07 -0700 Subject: [PATCH 01/23] updating version requirements --- .gitignore | 1 + README.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 18d42f2..7a3456c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ **/obj/ **/.vs/ **/.vscode/ +**/.github/ \ No newline at end of file diff --git a/README.md b/README.md index b0cb78c..6890b4a 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ The tool respects P4CONFIG file usage, as well as supports typical configuration # Build and Test ### Build Requirments: -1. Visual Studio 2022 version 17.5.0 or later -1. Windows SDK version 10.0.26100.1742 -1. Windows WDK version 10.0.26100.2454 +1. Visual Studio 2022 version 17.14.0 or later +1. Windows SDK version 10.0.26100.7175 +1. Windows WDK version 10.0.26100.6584 Details for installing Visual Studio 2022, the Windows Software Development Kit (SDK), and the Windows Driver Kit (WDK) can be found here: > [Download the Windows Driver Kit](https://learn.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) From 1a1a668123b3f06a36213b9bb08fc7f02615a8b3 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Fri, 17 Jul 2026 22:03:42 -0700 Subject: [PATCH 02/23] security wip --- .../P4VFS.CodeSign/Source/DevCenterClient.cs | 2 +- source/P4VFS.Console/P4VFS.Notes.txt | 3 +++ source/P4VFS.Core/Tests/TestDriver.cpp | 1 + source/P4VFS.Driver/Include/DriverVersion.h | 6 ++--- source/P4VFS.Driver/P4VFS.Driver.vcxproj | 9 +++++++ .../P4VFS.Driver/P4VFS.Driver.vcxproj.filters | 26 +++++++++++++++++++ source/P4VFS.Driver/Scripts/TestPush.bat | 15 +++++++++++ source/P4VFS.Driver/Scripts/TestSetup.bat | 7 +++++ source/P4VFS.Driver/Source/DriverCore.c | 3 ++- source/P4VFS.Driver/Source/DriverFilter.c | 15 +++++++++++ source/P4VFS.Driver/p4vfsflt.inf | 10 +++---- 11 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 source/P4VFS.Driver/Scripts/TestPush.bat diff --git a/source/P4VFS.CodeSign/Source/DevCenterClient.cs b/source/P4VFS.CodeSign/Source/DevCenterClient.cs index 473b5d9..85a3593 100644 --- a/source/P4VFS.CodeSign/Source/DevCenterClient.cs +++ b/source/P4VFS.CodeSign/Source/DevCenterClient.cs @@ -25,7 +25,7 @@ namespace Microsoft.P4VFS.CodeSign { public class DevCenterClient : ICodeSignClient { - private static readonly string HdcVersion = "1.0"; + private static readonly string HdcVersion = "2.0"; private static readonly string HdcTenant = "my"; private static readonly string HdcRootUri = $"/v{HdcVersion}/{HdcTenant}/hardware"; diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 2219383..84d93a2 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -1,5 +1,8 @@ Microsoft P4VFS Release Notes +Version [1.30.0.0] +* Security updates addressing MSRC-126533, MSRC-121148, MSRC-121146, MSRC-121144 + Version [1.29.4.0] * Updating to latest P4API 25.2 * Migrating to OpenSSL 3.0 LTS as recommended by P4API 25.2 with latest 3.0.18 diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index 7391e73..c3791e7 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -263,6 +263,7 @@ typedef enum _POOL_TYPE { #define FLT_FILE_NAME_ALLOW_QUERY_ON_REPARSE 0x04000000 #define OBJ_CASE_INSENSITIVE 0x00000040L #define OBJ_KERNEL_HANDLE 0x00000200L +#define OBJ_FORCE_ACCESS_CHECK 0x00000400L #define POOL_FLAG_NON_PAGED 0x0000000000000040UI64 #define FileBasicInformation 4 #define FileStandardInformation 5 diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index 91f0f03..33de7b3 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -3,9 +3,9 @@ #pragma once #define P4VFS_VER_MAJOR 1 // Increment this number almost never -#define P4VFS_VER_MINOR 29 // Increment this number whenever the driver changes -#define P4VFS_VER_BUILD 4 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 0 // Increment this number when we rebuild with any change +#define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes +#define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made +#define P4VFS_VER_REVISION 2 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) diff --git a/source/P4VFS.Driver/P4VFS.Driver.vcxproj b/source/P4VFS.Driver/P4VFS.Driver.vcxproj index f64dafe..afca440 100644 --- a/source/P4VFS.Driver/P4VFS.Driver.vcxproj +++ b/source/P4VFS.Driver/P4VFS.Driver.vcxproj @@ -142,6 +142,15 @@ + + + + + + + + + diff --git a/source/P4VFS.Driver/P4VFS.Driver.vcxproj.filters b/source/P4VFS.Driver/P4VFS.Driver.vcxproj.filters index 1cf80f5..cd49e9d 100644 --- a/source/P4VFS.Driver/P4VFS.Driver.vcxproj.filters +++ b/source/P4VFS.Driver/P4VFS.Driver.vcxproj.filters @@ -12,6 +12,9 @@ {7d8d6eb4-5bd6-4f2d-aa81-fe952550cb96} + + {f2284f34-e352-45ea-be40-841a882442ef} + @@ -46,4 +49,27 @@ + + + Scripts + + + Scripts + + + Scripts + + + Scripts + + + Scripts + + + Scripts + + + Scripts + + \ No newline at end of file diff --git a/source/P4VFS.Driver/Scripts/TestPush.bat b/source/P4VFS.Driver/Scripts/TestPush.bat new file mode 100644 index 0000000..e099778 --- /dev/null +++ b/source/P4VFS.Driver/Scripts/TestPush.bat @@ -0,0 +1,15 @@ +@ECHO OFF +SETLOCAL ENABLEDELAYEDEXPANSION + +SET SCRIPT_FOLDER=%~dp0 +SET SCRIPT_FOLDER=%SCRIPT_FOLDER:~,-1% +SET REPO_FOLDER=%SCRIPT_FOLDER%\..\..\.. +SET DEPLOY_FOLDER=\\jessk-lt1-w11\C$\P4VFS + +SET ROBOCOPY_COMMON_OPTIONS=/XD .* lib include /MT /S + +robocopy.exe %REPO_FOLDER%\source %DEPLOY_FOLDER%\source /XD .* /MT /S +robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Setup %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Setup %ROBOCOPY_COMMON_OPTIONS% +robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Driver %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Driver %ROBOCOPY_COMMON_OPTIONS% +robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Console %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Console %ROBOCOPY_COMMON_OPTIONS% +robocopy.exe %REPO_FOLDER%\external\P4API %DEPLOY_FOLDER%\external\P4API %ROBOCOPY_COMMON_OPTIONS% diff --git a/source/P4VFS.Driver/Scripts/TestSetup.bat b/source/P4VFS.Driver/Scripts/TestSetup.bat index 8a3f376..55c6c09 100644 --- a/source/P4VFS.Driver/Scripts/TestSetup.bat +++ b/source/P4VFS.Driver/Scripts/TestSetup.bat @@ -27,5 +27,12 @@ IF NOT "%ERRORLEVEL%"=="0" ( EXIT /B 1 ) +:: Enable Windows Developer Mode (allows unprivileged symbolic link creation) +reg.exe add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock /v AllowDevelopmentWithoutDevLicense /t REG_DWORD /d 1 /f +IF NOT "%ERRORLEVEL%"=="0" ( + ECHO Failed to enable Windows Developer Mode + EXIT /B 1 +) + ECHO Successfully setup machine for test drivers. Reboot is probably required. EXIT /B 0 diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index b69da4b..ef4ae5e 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -1141,11 +1141,12 @@ P4vfsOpenReparsePoint( createContext.SiloContext = PsGetHostSilo(); // We use the fileIdPath in place of the pFileName path for FILE_OPEN_BY_FILE_ID. + // Use OBJ_FORCE_ACCESS_CHECK to verify that the application has the necessary access. // The IO_IGNORE_SHARE_ACCESS_CHECK is used to avoid existing share conflicts InitializeObjectAttributes(&objectAttributes, &fileIdPath, - OBJ_CASE_INSENSITIVE, + OBJ_CASE_INSENSITIVE | OBJ_FORCE_ACCESS_CHECK, NULL, NULL); diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index 8b033b2..6c2fa42 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -341,6 +341,7 @@ DriverEntry( goto CLEANUP; } + // Grant all access to the control port as each command will enforce elevated permission as necessary RtlSetDaclSecurityDescriptor(controlSecurityDescriptor, TRUE, NULL, FALSE); if (!NT_SUCCESS(status)) { @@ -802,6 +803,13 @@ P4vfsControlPortMessage( } case P4VFS_OPERATION_OPEN_REPARSE_POINT: { + if (P4vfsIsCurrentProcessElevated() == FALSE) + { + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: P4VFS_OPERATION_OPEN_REPARSE_POINT elevation required"); + status = STATUS_ELEVATION_REQUIRED; + break; + } + UNICODE_STRING unicodeFilePath = {0}; status = P4vfsToUnicodeString(&input->data.OPEN_REPARSE_POINT.filePath, &unicodeFilePath); if (!NT_SUCCESS(status)) @@ -825,6 +833,13 @@ P4vfsControlPortMessage( } case P4VFS_OPERATION_CLOSE_REPARSE_POINT: { + if (P4vfsIsCurrentProcessElevated() == FALSE) + { + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: P4VFS_OPERATION_CLOSE_REPARSE_POINT elevation required"); + status = STATUS_ELEVATION_REQUIRED; + break; + } + status = P4vfsCloseReparsePoint(input->data.CLOSE_REPARSE_POINT.handle.fileHandle, input->data.CLOSE_REPARSE_POINT.handle.fileObject); diff --git a/source/P4VFS.Driver/p4vfsflt.inf b/source/P4VFS.Driver/p4vfsflt.inf index c994fc8..a58703e 100644 --- a/source/P4VFS.Driver/p4vfsflt.inf +++ b/source/P4VFS.Driver/p4vfsflt.inf @@ -57,11 +57,11 @@ AddReg = P4VFSFLT.AddRegistry ;; [P4VFSFLT.AddRegistry] -HKR,,"DebugFlags",0x00010001 ,0x0 -HKR,,"SupportedFeatures",0x00010001,0x3 -HKR,"Instances","DefaultInstance",0x00000000,%DefaultInstance% -HKR,"Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% -HKR,"Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% +HKR,"Parameters","DebugFlags",0x00010001,0x0 +HKR,"Parameters","SupportedFeatures",0x00010001,0x3 +HKR,"Parameters\Instances","DefaultInstance",0x00000000,%DefaultInstance% +HKR,"Parameters\Instances\"%Instance1.Name%,"Altitude",0x00000000,%Instance1.Altitude% +HKR,"Parameters\Instances\"%Instance1.Name%,"Flags",0x00010001,%Instance1.Flags% ;; ;; Copy Files Section From 2ff5c66f2c03c7c30c0a4704feb3cab840cb17ec Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 18 Jul 2026 13:09:12 -0700 Subject: [PATCH 03/23] fix P4vfsIsCurrentProcessElevated for impersonation --- source/P4VFS.Driver/Include/DriverVersion.h | 2 +- source/P4VFS.Driver/Source/DriverCore.c | 29 +++++++++++++-------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index 33de7b3..37c9ff7 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 2 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 4 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index ef4ae5e..3ecaaee 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -1169,11 +1169,11 @@ P4vfsOpenReparsePoint( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsReopenFile: FltCreateFileEx2 failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); goto CLEANUP; } - P4vfsTraceInfo(Core, L"P4vfsReopenFile: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); *pTargetHandle = hLocalFile; hLocalFile = NULL; @@ -1233,7 +1233,7 @@ P4vfsIsCurrentProcessElevated( NTSTATUS status = STATUS_SUCCESS; PEPROCESS pProcessObject = NULL; PACCESS_TOKEN pAccessToken = NULL; - PTOKEN_ELEVATION_TYPE pElevationType = NULL; + PTOKEN_ELEVATION pElevation = NULL; PAGED_CODE(); @@ -1244,6 +1244,9 @@ P4vfsIsCurrentProcessElevated( goto CLEANUP; } + // Reference the process primary token explicitly. This is unaffected by any impersonation + // that may be active on the current thread. + pAccessToken = PsReferencePrimaryToken(pProcessObject); if (pAccessToken == NULL) { @@ -1251,27 +1254,31 @@ P4vfsIsCurrentProcessElevated( goto CLEANUP; } + // Use TokenElevation (TOKEN_ELEVATION::TokenIsElevated) rather than TokenElevationType. + // TokenElevationType may not report TokenElevationTypeFull type while impersonating, whereas + // TokenIsElevated reflects the true elevation state of the primary token. + status = SeQueryInformationToken(pAccessToken, - TokenElevationType, - &pElevationType); + TokenElevation, + &pElevation); - if (!NT_SUCCESS(status) || pElevationType == NULL) + if (!NT_SUCCESS(status) || pElevation == NULL) { - P4vfsTraceError(Core, L"P4vfsIsCurrentProcessElevated: Failed to query TokenElevationType"); + P4vfsTraceError(Core, L"P4vfsIsCurrentProcessElevated: Failed to query TokenElevation"); goto CLEANUP; } - if (*pElevationType == TokenElevationTypeFull) + if (pElevation->TokenIsElevated != 0) { result = TRUE; } - P4vfsTraceInfo(Core, L"P4vfsIsCurrentProcessElevated: Result [%d] pElevationType [%d]", (LONG)result, (LONG)(*pElevationType)); + P4vfsTraceInfo(Core, L"P4vfsIsCurrentProcessElevated: Result [%d] TokenIsElevated [%d]", (LONG)result, (LONG)(pElevation->TokenIsElevated)); CLEANUP: - if (pElevationType != NULL) + if (pElevation != NULL) { - ExFreePool(pElevationType); + ExFreePool(pElevation); } if (pAccessToken != NULL) From 44aaed3dd915cc148903d2f6bf872c8260188bb8 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 18 Jul 2026 14:54:09 -0700 Subject: [PATCH 04/23] file object wip --- source/P4VFS.Driver/Include/DriverCore.h | 6 +- source/P4VFS.Driver/Include/DriverData.h | 7 +- source/P4VFS.Driver/Include/DriverFilter.h | 14 +++- source/P4VFS.Driver/Source/DriverCore.c | 86 +++++++++++++++++++--- source/P4VFS.Driver/Source/DriverFilter.c | 6 +- 5 files changed, 96 insertions(+), 23 deletions(-) diff --git a/source/P4VFS.Driver/Include/DriverCore.h b/source/P4VFS.Driver/Include/DriverCore.h index e0e6244..c44ec39 100644 --- a/source/P4VFS.Driver/Include/DriverCore.h +++ b/source/P4VFS.Driver/Include/DriverCore.h @@ -130,14 +130,12 @@ NTSTATUS P4vfsOpenReparsePoint( _In_ PUNICODE_STRING pFileName, _In_ ACCESS_MASK desiredAccess, - _Out_ PHANDLE pTargetHandle, - _Outptr_ PFILE_OBJECT* ppTargetFileObject + _Out_ P4VFS_FLT_FILE_HANDLE* pFileHandle ); NTSTATUS P4vfsCloseReparsePoint( - _In_ HANDLE hHandle, - _In_ PFILE_OBJECT pFileObject + _In_ P4VFS_FLT_FILE_HANDLE* pFileHandle ); BOOLEAN diff --git a/source/P4VFS.Driver/Include/DriverData.h b/source/P4VFS.Driver/Include/DriverData.h index 8861f19..da95baa 100644 --- a/source/P4VFS.Driver/Include/DriverData.h +++ b/source/P4VFS.Driver/Include/DriverData.h @@ -135,10 +135,15 @@ typedef struct _P4VFS_SERVICE_REPLY typedef struct _FILE_OBJECT* PFILE_OBJECT; #endif +typedef struct _P4VFS_FLT_FILE_ID +{ + ULONGLONG Id; +} P4VFS_FLT_FILE_ID; + typedef struct _P4VFS_FLT_FILE_HANDLE { HANDLE fileHandle; - PFILE_OBJECT fileObject; + P4VFS_FLT_FILE_ID fileId; } P4VFS_FLT_FILE_HANDLE; typedef struct _P4VFS_CONTROL_MSG diff --git a/source/P4VFS.Driver/Include/DriverFilter.h b/source/P4VFS.Driver/Include/DriverFilter.h index 84093ed..1fc7a6e 100644 --- a/source/P4VFS.Driver/Include/DriverFilter.h +++ b/source/P4VFS.Driver/Include/DriverFilter.h @@ -12,6 +12,14 @@ typedef struct _P4VFS_REPARSE_ACTION LONG nRefCount; } P4VFS_REPARSE_ACTION; +typedef struct _P4VFS_OPEN_FILE_OBJECT +{ + struct _P4VFS_OPEN_FILE_OBJECT* pNext; + P4VFS_FLT_FILE_ID fileId; + PFILE_OBJECT pFileObject; + HANDLE fileHandle; +} P4VFS_OPEN_FILE_OBJECT; + typedef struct _P4VFS_FLT_CONTEXT { DRIVER_OBJECT* pDriverObject; // The object that IDs the driver @@ -22,8 +30,10 @@ typedef struct _P4VFS_FLT_CONTEXT LONG nRequestCount; // Number of requests processed BOOLEAN bSanitizeAttributes; // Enable stripping reparse and sparse file attributes BOOLEAN bShareModeDuringHydration; // Force file handle creation during hydration have share mode (legacy requirement) - P4VFS_REPARSE_ACTION* pReparseActionList; - FAST_MUTEX hReparseActionLock; + P4VFS_REPARSE_ACTION* pReparseActionList; // Linked list of reparse actions in progress + FAST_MUTEX hReparseActionLock; // Mutex for exclusive access to pReparseActionList + P4VFS_OPEN_FILE_OBJECT* pOpenFileObjectList; // Linked list of open file objects from P4vfsOpenReparsePoint + FAST_MUTEX pOpenFileObjectLock; // Mutex for exclusive access to pOpenFileObjectList } P4VFS_FLT_CONTEXT; extern P4VFS_FLT_CONTEXT g_FltContext; diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index 3ecaaee..78b92de 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -1093,12 +1093,30 @@ P4vfsGetFileIdByFileName( return status; } +NTSTATUS +P4vfsPushOpenFileObject( + _In_ PFILE_OBJECT pFileObject, + _In_ HANDLE fileHandle, + _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject + ) +{ + return STATUS_INVALID_PARAMETER; +} + +NTSTATUS +P4vfsPopOpenFileObject( + _In_ P4VFS_FLT_FILE_ID* pFileId, + _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject + ) +{ + return STATUS_INVALID_PARAMETER; +} + NTSTATUS P4vfsOpenReparsePoint( _In_ PUNICODE_STRING pFileName, _In_ ACCESS_MASK desiredAccess, - _Out_ PHANDLE pTargetHandle, - _Outptr_ PFILE_OBJECT* ppTargetFileObject + _Out_ P4VFS_FLT_FILE_HANDLE* pFileHandle ) { NTSTATUS status = STATUS_SUCCESS; @@ -1109,9 +1127,17 @@ P4vfsOpenReparsePoint( PFILE_OBJECT pLocalFileObject = NULL; UNICODE_STRING fileIdPath = {0}; PFLT_INSTANCE pFltInstance = NULL; + P4VFS_OPEN_FILE_OBJECT openFileObject = {0}; PAGED_CODE(); + if (pFileHandle == NULL) + { + status = STATUS_INVALID_PARAMETER; + P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: pFileHandle is NULL"); + goto CLEANUP; + } + // We wish to open an existing reparse point file by using FILE_OPEN_BY_FILE_ID so as to avoid // directory notifications. We take this opportunity to query our PFLT_INSTANCE for the volume // which holds this file, which will be optimal for future filter operations @@ -1173,11 +1199,25 @@ P4vfsOpenReparsePoint( goto CLEANUP; } - P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + // The kernel PFILE_OBJECT will remain private. We will only expose the user mode HANDLE along with an opaque P4VFS_FLT_FILE_ID. + // Future operations on this PFILE_OBJECT will restricted behind the P4VFS_FLT_FILE_ID and our internal P4VFS_OPEN_FILE_OBJECT. + + status = P4vfsPushOpenFileObject(pLocalFileObject, + hLocalFile, + &openFileObject); + + if (!NT_SUCCESS(status)) + { + P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: P4vfsPushOpenFileObject failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + goto CLEANUP; + } + + pFileHandle->fileHandle = openFileObject.fileHandle; + pFileHandle->fileId = openFileObject.fileId; + + P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] Id [0x%I64x] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fileId.Id, status); - *pTargetHandle = hLocalFile; hLocalFile = NULL; - *ppTargetFileObject = pLocalFileObject; pLocalFileObject = NULL; CLEANUP: @@ -1206,23 +1246,45 @@ P4vfsOpenReparsePoint( NTSTATUS P4vfsCloseReparsePoint( - _In_ HANDLE hFile, - _In_ PFILE_OBJECT pFileObject + _In_ P4VFS_FLT_FILE_HANDLE* pFileHandle ) { + NTSTATUS status = STATUS_SUCCESS; + P4VFS_OPEN_FILE_OBJECT openFileObject = {0}; + PAGED_CODE(); - if (hFile != NULL) + if (pFileHandle == NULL) { - FltClose(hFile); + status = STATUS_INVALID_PARAMETER; + P4vfsTraceError(Core, L"P4vfsCloseReparsePoint: pFileHandle is NULL"); + goto CLEANUP; } - if (pFileObject != NULL) + // We do not trust the HANDLE passed in from P4VFS_FLT_FILE_HANDLE. Instead we'll look for the P4VFS_OPEN_FILE_OBJECT by Id + // and confirm the HANDLE and ownership is what we expect. + + status = P4vfsPopOpenFileObject(&pFileHandle->fileId, + &openFileObject); + + if (!NT_SUCCESS(status)) { - ObDereferenceObject(pFileObject); + P4vfsTraceError(Core, L"P4vfsCloseReparsePoint: P4vfsPopOpenFileObject failed with fileId [0x%I64x] [%!STATUS!]", pFileHandle->fileId.Id, status); + goto CLEANUP; } - return STATUS_SUCCESS; + if (openFileObject.fileHandle != NULL) + { + FltClose(openFileObject.fileHandle); + } + + if (openFileObject.pFileObject != NULL) + { + ObDereferenceObject(openFileObject.pFileObject); + } + +CLEANUP: + return status; } BOOLEAN diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index 6c2fa42..039b8d4 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -825,8 +825,7 @@ P4vfsControlPortMessage( status = P4vfsOpenReparsePoint(&unicodeFilePath, desiredAccess, - &output->data.OPEN_REPARSE_POINT.handle.fileHandle, - &output->data.OPEN_REPARSE_POINT.handle.fileObject); + &output->data.OPEN_REPARSE_POINT.handle); output->data.OPEN_REPARSE_POINT.ntstatus = status; break; @@ -840,8 +839,7 @@ P4vfsControlPortMessage( break; } - status = P4vfsCloseReparsePoint(input->data.CLOSE_REPARSE_POINT.handle.fileHandle, - input->data.CLOSE_REPARSE_POINT.handle.fileObject); + status = P4vfsCloseReparsePoint(&input->data.CLOSE_REPARSE_POINT.handle); output->data.CLOSE_REPARSE_POINT.ntstatus = status; break; From 830b32641515f900535fecd18e882a6732ae3ddf Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 18 Jul 2026 21:24:09 -0700 Subject: [PATCH 05/23] file object wip 2 --- source/P4VFS.Driver/Include/DriverCore.h | 14 +++ source/P4VFS.Driver/Include/DriverData.h | 2 +- source/P4VFS.Driver/Include/DriverFilter.h | 5 +- source/P4VFS.Driver/Source/DriverCore.c | 129 ++++++++++++++++++++- source/P4VFS.Driver/Source/DriverFilter.c | 3 +- 5 files changed, 145 insertions(+), 8 deletions(-) diff --git a/source/P4VFS.Driver/Include/DriverCore.h b/source/P4VFS.Driver/Include/DriverCore.h index c44ec39..2e7ea81 100644 --- a/source/P4VFS.Driver/Include/DriverCore.h +++ b/source/P4VFS.Driver/Include/DriverCore.h @@ -13,6 +13,7 @@ #define P4VFS_FILE_NAME_ALLOC_TAG 'NsvP' #define P4VFS_SERVICE_PORT_HANDLE_ALLOC_TAG 'SsvP' #define P4VFS_CONTROL_PORT_HANDLE_ALLOC_TAG 'CsvP' +#define P4VFS_OPEN_FILE_OBJECT_ALLOC_TAG 'FsvP' NTSTATUS P4vfsUserModeExecuteDrvRequest( @@ -126,6 +127,19 @@ P4vfsGetFileIdByFileName( _Outptr_opt_ PFLT_INSTANCE* ppFltInstance ); +NTSTATUS +P4vfsPushOpenFileObject( + _In_ PFILE_OBJECT pFileObject, + _In_ HANDLE fileHandle, + _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject + ); + +NTSTATUS +P4vfsPopOpenFileObject( + _In_ P4VFS_FLT_FILE_ID* pFileId, + _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject + ); + NTSTATUS P4vfsOpenReparsePoint( _In_ PUNICODE_STRING pFileName, diff --git a/source/P4VFS.Driver/Include/DriverData.h b/source/P4VFS.Driver/Include/DriverData.h index da95baa..9b7e3a1 100644 --- a/source/P4VFS.Driver/Include/DriverData.h +++ b/source/P4VFS.Driver/Include/DriverData.h @@ -137,7 +137,7 @@ typedef struct _FILE_OBJECT* PFILE_OBJECT; typedef struct _P4VFS_FLT_FILE_ID { - ULONGLONG Id; + ULONGLONG data; } P4VFS_FLT_FILE_ID; typedef struct _P4VFS_FLT_FILE_HANDLE diff --git a/source/P4VFS.Driver/Include/DriverFilter.h b/source/P4VFS.Driver/Include/DriverFilter.h index 1fc7a6e..9553abb 100644 --- a/source/P4VFS.Driver/Include/DriverFilter.h +++ b/source/P4VFS.Driver/Include/DriverFilter.h @@ -27,13 +27,14 @@ typedef struct _P4VFS_FLT_CONTEXT PFLT_PORT pServiceServerPort; // Filter service listening port PFLT_PORT pServiceClientPort; // Latest active service port PFLT_PORT pControlServerPort; // Filter control listening port - LONG nRequestCount; // Number of requests processed + LONG nRequestCount; // Monotonic increasing number of requests processed + LONG nFileIdCount; // Monotonic increasing number of P4VFS_FLT_FILE_ID's created BOOLEAN bSanitizeAttributes; // Enable stripping reparse and sparse file attributes BOOLEAN bShareModeDuringHydration; // Force file handle creation during hydration have share mode (legacy requirement) P4VFS_REPARSE_ACTION* pReparseActionList; // Linked list of reparse actions in progress FAST_MUTEX hReparseActionLock; // Mutex for exclusive access to pReparseActionList P4VFS_OPEN_FILE_OBJECT* pOpenFileObjectList; // Linked list of open file objects from P4vfsOpenReparsePoint - FAST_MUTEX pOpenFileObjectLock; // Mutex for exclusive access to pOpenFileObjectList + FAST_MUTEX hOpenFileObjectLock; // Mutex for exclusive access to pOpenFileObjectList } P4VFS_FLT_CONTEXT; extern P4VFS_FLT_CONTEXT g_FltContext; diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index 78b92de..a1c84df 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -460,7 +460,9 @@ P4vfsIsEqualActionFileKey( if (fileKey0->Length == fileKey1->Length && fileKey0->Buffer != NULL && fileKey1->Buffer != NULL) { if (RtlCompareMemory(fileKey0->Buffer, fileKey1->Buffer, fileKey0->Length) == fileKey0->Length) + { return TRUE; + } } return FALSE; @@ -476,7 +478,9 @@ P4vfsQueryAnyReparseActionInProgress( ExAcquireFastMutex(&g_FltContext.hReparseActionLock); { if (g_FltContext.pReparseActionList != NULL) + { result = TRUE; + } } ExReleaseFastMutex(&g_FltContext.hReparseActionLock); return result; @@ -1100,7 +1104,60 @@ P4vfsPushOpenFileObject( _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject ) { - return STATUS_INVALID_PARAMETER; + NTSTATUS status = STATUS_SUCCESS; + P4VFS_OPEN_FILE_OBJECT* pLinkedFileObject = NULL; + + PAGED_CODE(); + + if (pOpenFileObject == NULL) + { + status = STATUS_INVALID_PARAMETER; + P4vfsTraceError(Core, L"P4vfsPushOpenFileObject: pOpenFileObject is NULL"); + goto CLEANUP; + } + + pLinkedFileObject = (P4VFS_OPEN_FILE_OBJECT*)ExAllocatePoolZero( + NonPagedPoolNx, + sizeof(P4VFS_OPEN_FILE_OBJECT), + P4VFS_OPEN_FILE_OBJECT_ALLOC_TAG); + + if (pLinkedFileObject == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + P4vfsTraceError(Core, L"P4vfsPushOpenFileObject: Failed to Allocate P4VFS_OPEN_FILE_OBJECT size [%d]", sizeof(P4VFS_OPEN_FILE_OBJECT)); + goto CLEANUP; + } + + ULONGLONG nextUniqueId = (ULONGLONG)(ULONG)InterlockedIncrement(&g_FltContext.nFileIdCount); + ULONGLONG processId = (ULONGLONG)(ULONG_PTR)PsGetCurrentProcessId(); + ULONGLONG threadId = (ULONGLONG)(ULONG_PTR)PsGetCurrentThreadId(); + + #define MIX_U64_TO_U16(a) (((a)>>48)&0xFFFF) ^ (((a)>>32)&0xFFFF) ^ (((a)>>16)&0xFFFF) ^ ((a)&0xFFFF) + processId = MIX_U64_TO_U16(processId); + threadId = MIX_U64_TO_U16(threadId); + #undef MIX_U64_TO_U16 + + // The interal pLinkedFileObject will reside in the linked list of P4VFS_OPEN_FILE_OBJECT + // The fileId is a unique and obfuscated 64bit value which will serve well to track this handle in user mode. + // We can also use the fileHandle to cross-reference with the fileId if needed for additional verification + pLinkedFileObject->fileId.data = (nextUniqueId<<32) | (processId<<16) | threadId; + pLinkedFileObject->pFileObject = pFileObject; + pLinkedFileObject->fileHandle = fileHandle; + + // Insert the pLinkedFileObject into the linked list + // Assign a copy of the pLinkedFileObject which we are now tracking + ExAcquireFastMutex(&g_FltContext.hOpenFileObjectLock); + { + pLinkedFileObject->pNext = g_FltContext.pOpenFileObjectList; + g_FltContext.pOpenFileObjectList = pLinkedFileObject; + + *pOpenFileObject = *pLinkedFileObject; + pOpenFileObject->pNext = NULL; + } + ExReleaseFastMutex(&g_FltContext.hOpenFileObjectLock); + +CLEANUP: + return status; } NTSTATUS @@ -1109,7 +1166,71 @@ P4vfsPopOpenFileObject( _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject ) { - return STATUS_INVALID_PARAMETER; + NTSTATUS status = STATUS_SUCCESS; + P4VFS_OPEN_FILE_OBJECT* pObject = NULL; + P4VFS_OPEN_FILE_OBJECT* pPrevObject = NULL; + P4VFS_OPEN_FILE_OBJECT* pFreeObject = NULL; + + PAGED_CODE(); + + if (pOpenFileObject == NULL) + { + status = STATUS_INVALID_PARAMETER; + P4vfsTraceError(Core, L"P4vfsPopOpenFileObject: pOpenFileObject is NULL"); + goto CLEANUP; + } + + if (pFileId == NULL) + { + status = STATUS_INVALID_PARAMETER; + P4vfsTraceError(Core, L"P4vfsPopOpenFileObject: pFileId is NULL"); + goto CLEANUP; + } + + // Search our linked list of known file objects for one with this pFileId + ExAcquireFastMutex(&g_FltContext.hOpenFileObjectLock); + { + for (pObject = g_FltContext.pOpenFileObjectList; pObject != NULL; pObject = pObject->pNext) + { + if (pObject->fileId.data == pFileId->data) + { + break; + } + + pPrevObject = pObject; + } + + if (pObject != NULL) + { + // Remove the pObject that we found from the linked list + if (g_FltContext.pOpenFileObjectList == pObject) + { + g_FltContext.pOpenFileObjectList = pObject->pNext; + } + else if (pPrevObject != NULL) + { + pPrevObject->pNext = pObject->pNext; + } + + // Return a copy of the object that we've removed and free it + *pOpenFileObject = *pObject; + pOpenFileObject->pNext = NULL; + pFreeObject = pObject; + } + else + { + status = STATUS_NOT_FOUND; + } + } + ExReleaseFastMutex(&g_FltContext.hOpenFileObjectLock); + +CLEANUP: + if (pFreeObject != NULL) + { + ExFreePoolWithTag(pFreeObject, P4VFS_OPEN_FILE_OBJECT_ALLOC_TAG); + } + + return status; } NTSTATUS @@ -1215,7 +1336,7 @@ P4vfsOpenReparsePoint( pFileHandle->fileHandle = openFileObject.fileHandle; pFileHandle->fileId = openFileObject.fileId; - P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] Id [0x%I64x] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fileId.Id, status); + P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] Id [0x%I64x] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fileId.data, status); hLocalFile = NULL; pLocalFileObject = NULL; @@ -1269,7 +1390,7 @@ P4vfsCloseReparsePoint( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsCloseReparsePoint: P4vfsPopOpenFileObject failed with fileId [0x%I64x] [%!STATUS!]", pFileHandle->fileId.Id, status); + P4vfsTraceError(Core, L"P4vfsCloseReparsePoint: P4vfsPopOpenFileObject failed with fileId [0x%I64x] [%!STATUS!]", pFileHandle->fileId.data, status); goto CLEANUP; } diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index 039b8d4..abc61ed 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -375,8 +375,9 @@ DriverEntry( goto CLEANUP; } - // Initialize the reparse action mutex + // Initialize required mutexes ExInitializeFastMutex(&g_FltContext.hReparseActionLock); + ExInitializeFastMutex(&g_FltContext.hOpenFileObjectLock); // After we have created everything we needed, actually start filtering status = FltStartFiltering(g_FltContext.pFilter); From ceb0c55e887648e4e848f3f9320208256086eb47 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 18 Jul 2026 22:04:52 -0700 Subject: [PATCH 06/23] file object wip 3 --- source/P4VFS.Core/Tests/TestDriver.cpp | 1 + source/P4VFS.Core/Tests/TestFileOperations.cpp | 4 ++-- source/P4VFS.Driver/Include/DriverVersion.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index c3791e7..ae35de2 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -255,6 +255,7 @@ typedef enum _POOL_TYPE { #define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L) #define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS)0x00000106L) #define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS)0xC0000206L) +#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define IO_IGNORE_SHARE_ACCESS_CHECK 0x0800 #define FILE_SHARE_VALID_FLAGS 0x00000007 diff --git a/source/P4VFS.Core/Tests/TestFileOperations.cpp b/source/P4VFS.Core/Tests/TestFileOperations.cpp index 0fb31c9..8af0cd3 100644 --- a/source/P4VFS.Core/Tests/TestFileOperations.cpp +++ b/source/P4VFS.Core/Tests/TestFileOperations.cpp @@ -65,7 +65,7 @@ void TestFileOperationsOpenReparsePointFile(const TestContext& context) // Open the file for write as a reparse point excluding share access checks P4VFS_FLT_FILE_HANDLE fltFile = FileOperations::OpenReparsePointFile(reparseFilePath.c_str(), GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE); - Assert(fltFile.fileHandle != NULL && fltFile.fileHandle != INVALID_HANDLE_VALUE && fltFile.fileObject != nullptr); + Assert(fltFile.fileHandle != NULL && fltFile.fileHandle != INVALID_HANDLE_VALUE && fltFile.fileId.data != 0); Array localFileWriteBytes; for (uint32_t i = 1; i <= 5693; ++i) @@ -153,7 +153,7 @@ void AssertFileOperationsAccessInternal(const TestContext& context, bool isEleva const String adminFilePath = FileOperations::GetExpandedEnvironmentStrings(TEXT("%ProgramFiles%\\P4VFS\\P4VFS.Notes.txt")); Assert(FileInfo::IsRegular(adminFilePath.c_str())); P4VFS_FLT_FILE_HANDLE adminFileHandle = FileOperations::OpenReparsePointFile(adminFilePath.c_str(), FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0); - Assert(adminFileHandle.fileHandle != NULL && adminFileHandle.fileHandle != INVALID_HANDLE_VALUE && adminFileHandle.fileObject != nullptr); + Assert(adminFileHandle.fileHandle != NULL && adminFileHandle.fileHandle != INVALID_HANDLE_VALUE && adminFileHandle.fileId.data != 0); Assert(SUCCEEDED(FileOperations::CloseReparsePointFile(adminFileHandle))); // Attempt to set a driver control message which should be elevated only (this should be a proper set and restore) diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index 37c9ff7..b170844 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 4 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 5 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) From 3ca7f3a2f6b60540bc79c8af627ef519996056d5 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sun, 19 Jul 2026 21:23:44 -0700 Subject: [PATCH 07/23] testing with TestDriverOpenFileObjectList --- source/P4VFS.Core/Tests/TestDriver.cpp | 56 +++++++++++++++++++++++- source/P4VFS.Core/Tests/TestRegistry.cpp | 1 + source/P4VFS.Driver/Include/DriverCore.h | 2 +- source/P4VFS.Driver/Source/DriverCore.c | 7 +-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index ae35de2..6072b62 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -419,6 +419,13 @@ NTSTATUS SeQueryInformationToken(PACCESS_TOKEN token, TOKEN_INFORMATION_CLASS to return STATUS_UNSUCCESSFUL; } +LARGE_INTEGER KeQueryPerformanceCounter(PLARGE_INTEGER*) +{ + LARGE_INTEGER counter = {0}; + QueryPerformanceCounter(&counter); + return counter; +} + #include "DriverCore.h" #include "DriverCore.c" @@ -453,7 +460,7 @@ static void InternalTestDriverReset(const TestContext& context) FltGetVolumeFromFileObject = P4VFS_DEFAULT_FUNCTION_NTSTATUS(); FltGetVolumeInstanceFromName = P4VFS_DEFAULT_FUNCTION_NTSTATUS(); - ExAllocatePoolZero = [](POOL_TYPE, SIZE_T s, ULONG) -> PVOID { return GAlloc(s); }; + ExAllocatePoolZero = [](POOL_TYPE, SIZE_T s, ULONG) -> PVOID { void* p = GAlloc(s); Assert(p); ZeroMemory(p, s); return p; }; ExFreePoolWithTag = [](PVOID p, ULONG) -> VOID { GFree(p); }; ExAcquireFastMutex = P4VFS_DEFAULT_ACTION(); ExReleaseFastMutex = P4VFS_DEFAULT_ACTION(); @@ -546,3 +553,50 @@ void TestDriverUnicodeString(const TestContext& context) AssertUserModeResolveFile(veryLongFilePath, L"C:\\"); } +void TestDriverOpenFileObjectList(const TestContext& context) +{ + auto MakeFileObject = [](const PVOID fileObject, const PVOID fileHandle) -> P4VFS_OPEN_FILE_OBJECT + { + P4VFS_OPEN_FILE_OBJECT obj = {0}; + obj.pFileObject = (PFILE_OBJECT)fileObject; + obj.fileHandle = fileHandle; + return obj; + }; + + InternalTestDriverReset(context); + Assert(g_FltContext.pOpenFileObjectList == NULL); + + const P4VFS_OPEN_FILE_OBJECT src0 = MakeFileObject(L"fo0", L"fh0"); + P4VFS_OPEN_FILE_OBJECT push0 = {0}; + Assert(P4vfsPushOpenFileObject(src0.pFileObject, src0.fileHandle, &push0) == STATUS_SUCCESS); + Assert(src0.pFileObject == push0.pFileObject && src0.fileHandle == push0.fileHandle && push0.fileId.data != 0 && push0.pNext == NULL); + + Assert(g_FltContext.pOpenFileObjectList != NULL); + + const P4VFS_OPEN_FILE_OBJECT src1 = MakeFileObject(L"fo1", L"fh1"); + P4VFS_OPEN_FILE_OBJECT push1 = {0}; + Assert(P4vfsPushOpenFileObject(src1.pFileObject, src1.fileHandle, &push1) == STATUS_SUCCESS); + Assert(src1.pFileObject == push1.pFileObject && src1.fileHandle == push1.fileHandle && push1.fileId.data != 0 && push1.pNext == NULL); + Assert(push1.fileId.data != push0.fileId.data); + + P4VFS_OPEN_FILE_OBJECT pop1 = {0}; + Assert(P4vfsPopOpenFileObject(&push1.fileId, &pop1) == STATUS_SUCCESS); + Assert(src1.pFileObject == pop1.pFileObject && src1.fileHandle == pop1.fileHandle && push1.fileId.data == pop1.fileId.data && pop1.pNext == NULL); + + P4VFS_OPEN_FILE_OBJECT push2 = {0}; + Assert(P4vfsPushOpenFileObject(src0.pFileObject, src0.fileHandle, &push2) == STATUS_SUCCESS); + Assert(src0.pFileObject == push2.pFileObject && src0.fileHandle == push2.fileHandle && push2.fileId.data != 0 && push2.pNext == NULL); + + P4VFS_OPEN_FILE_OBJECT pop0 = {0}; + Assert(P4vfsPopOpenFileObject(&push0.fileId, &pop0) == STATUS_SUCCESS); + Assert(pop0.pFileObject == push0.pFileObject && pop0.fileHandle == push0.fileHandle && pop0.fileId.data == push0.fileId.data && pop0.pNext == NULL); + + P4VFS_OPEN_FILE_OBJECT pop3 = {0}; + Assert(P4vfsPopOpenFileObject(&push0.fileId, &pop3) == STATUS_NOT_FOUND); + + P4VFS_OPEN_FILE_OBJECT pop2 = {0}; + Assert(P4vfsPopOpenFileObject(&push2.fileId, &pop2) == STATUS_SUCCESS); + Assert(pop2.pFileObject == push2.pFileObject && pop2.fileHandle == push2.fileHandle && pop2.fileId.data != 0 && pop2.pNext == NULL); + + Assert(g_FltContext.pOpenFileObjectList == NULL); +} diff --git a/source/P4VFS.Core/Tests/TestRegistry.cpp b/source/P4VFS.Core/Tests/TestRegistry.cpp index 87b14eb..43553ab 100644 --- a/source/P4VFS.Core/Tests/TestRegistry.cpp +++ b/source/P4VFS.Core/Tests/TestRegistry.cpp @@ -55,6 +55,7 @@ P4VFS_REGISTER_TEST( TestThreadPool, 10800 ) // TestDriver P4VFS_REGISTER_TEST( TestDriverUnicodeString, 10900 ) +P4VFS_REGISTER_TEST( TestDriverOpenFileObjectList, 10901 ) // TestFileOperations P4VFS_REGISTER_TEST( TestFileOperationsUnicodeString, 11000 ) diff --git a/source/P4VFS.Driver/Include/DriverCore.h b/source/P4VFS.Driver/Include/DriverCore.h index 2e7ea81..ad794bb 100644 --- a/source/P4VFS.Driver/Include/DriverCore.h +++ b/source/P4VFS.Driver/Include/DriverCore.h @@ -136,7 +136,7 @@ P4vfsPushOpenFileObject( NTSTATUS P4vfsPopOpenFileObject( - _In_ P4VFS_FLT_FILE_ID* pFileId, + _In_ const P4VFS_FLT_FILE_ID* pFileId, _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject ); diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index a1c84df..47e159a 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -1128,9 +1128,10 @@ P4vfsPushOpenFileObject( goto CLEANUP; } + ULONGLONG seed = KeQueryPerformanceCounter(NULL).QuadPart; ULONGLONG nextUniqueId = (ULONGLONG)(ULONG)InterlockedIncrement(&g_FltContext.nFileIdCount); - ULONGLONG processId = (ULONGLONG)(ULONG_PTR)PsGetCurrentProcessId(); - ULONGLONG threadId = (ULONGLONG)(ULONG_PTR)PsGetCurrentThreadId(); + ULONGLONG processId = seed * (ULONGLONG)(ULONG_PTR)PsGetCurrentProcessId(); + ULONGLONG threadId = seed * (ULONGLONG)(ULONG_PTR)PsGetCurrentThreadId(); #define MIX_U64_TO_U16(a) (((a)>>48)&0xFFFF) ^ (((a)>>32)&0xFFFF) ^ (((a)>>16)&0xFFFF) ^ ((a)&0xFFFF) processId = MIX_U64_TO_U16(processId); @@ -1162,7 +1163,7 @@ P4vfsPushOpenFileObject( NTSTATUS P4vfsPopOpenFileObject( - _In_ P4VFS_FLT_FILE_ID* pFileId, + _In_ const P4VFS_FLT_FILE_ID* pFileId, _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject ) { From e8382a94119be1da6ba8c383e3b88aa406178c6a Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sun, 19 Jul 2026 21:28:42 -0700 Subject: [PATCH 08/23] fixing VM name in TestPush.bat --- source/P4VFS.Driver/Scripts/TestPush.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/P4VFS.Driver/Scripts/TestPush.bat b/source/P4VFS.Driver/Scripts/TestPush.bat index e099778..f210da9 100644 --- a/source/P4VFS.Driver/Scripts/TestPush.bat +++ b/source/P4VFS.Driver/Scripts/TestPush.bat @@ -4,7 +4,7 @@ SETLOCAL ENABLEDELAYEDEXPANSION SET SCRIPT_FOLDER=%~dp0 SET SCRIPT_FOLDER=%SCRIPT_FOLDER:~,-1% SET REPO_FOLDER=%SCRIPT_FOLDER%\..\..\.. -SET DEPLOY_FOLDER=\\jessk-lt1-w11\C$\P4VFS +SET DEPLOY_FOLDER=\\%COMPUTERNAME%-w11\C$\P4VFS SET ROBOCOPY_COMMON_OPTIONS=/XD .* lib include /MT /S From 8f8a7534de80048ef72763cedadb22620cfbe65d Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Wed, 22 Jul 2026 22:23:36 -0700 Subject: [PATCH 09/23] TestPush script fix for hostname --- source/P4VFS.Driver/Scripts/TestPush.bat | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/P4VFS.Driver/Scripts/TestPush.bat b/source/P4VFS.Driver/Scripts/TestPush.bat index f210da9..eb023c1 100644 --- a/source/P4VFS.Driver/Scripts/TestPush.bat +++ b/source/P4VFS.Driver/Scripts/TestPush.bat @@ -4,8 +4,13 @@ SETLOCAL ENABLEDELAYEDEXPANSION SET SCRIPT_FOLDER=%~dp0 SET SCRIPT_FOLDER=%SCRIPT_FOLDER:~,-1% SET REPO_FOLDER=%SCRIPT_FOLDER%\..\..\.. -SET DEPLOY_FOLDER=\\%COMPUTERNAME%-w11\C$\P4VFS +SET DEPLOY_COMPUTERNAME=%COMPUTERNAME%-w11 +IF NOT "%1" == "" ( + SET DEPLOY_COMPUTERNAME=%1 +) + +SET DEPLOY_FOLDER=\\%DEPLOY_COMPUTERNAME%\C$\P4VFS SET ROBOCOPY_COMMON_OPTIONS=/XD .* lib include /MT /S robocopy.exe %REPO_FOLDER%\source %DEPLOY_FOLDER%\source /XD .* /MT /S From 98aa0d925bcb1cec362aa016eda3dace92782199 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Thu, 23 Jul 2026 21:04:45 -0700 Subject: [PATCH 10/23] wpp trace fix for file_id string --- source/P4VFS.CoreInterop/Source/Pch.h | 1 + source/P4VFS.Driver/Include/DriverTrace.h | 38 +++++++++++++++++++++ source/P4VFS.Driver/Include/DriverVersion.h | 2 +- source/P4VFS.Driver/Source/DriverCore.c | 18 +++++----- source/P4VFS.Driver/Source/DriverFilter.c | 1 + 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/source/P4VFS.CoreInterop/Source/Pch.h b/source/P4VFS.CoreInterop/Source/Pch.h index bff2b3a..aec0a0c 100644 --- a/source/P4VFS.CoreInterop/Source/Pch.h +++ b/source/P4VFS.CoreInterop/Source/Pch.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/source/P4VFS.Driver/Include/DriverTrace.h b/source/P4VFS.Driver/Include/DriverTrace.h index 4579479..d5167b8 100644 --- a/source/P4VFS.Driver/Include/DriverTrace.h +++ b/source/P4VFS.Driver/Include/DriverTrace.h @@ -20,6 +20,41 @@ #define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) +#define WPP_LOGFILEIDPATH(x) \ + WPP_LOGPAIR(sizeof(USHORT), &(x)->Length) \ + WPP_LOGPAIR((x)->Length, (x)->Buffer) + +#define WPP_LOGHEXDUMP(x) \ + WPP_LOGPAIR(sizeof(USHORT), &(x).Length) \ + WPP_LOGPAIR((x).Length, (x).Buffer) + +typedef struct _WPP_HEXDUMP +{ + USHORT Length; + __field_bcount(Length) CONST PVOID Buffer; +} WPP_HEXDUMP, *PWPP_HEXDUMP; + +__inline +WPP_HEXDUMP +P4vfsCreateWppHexDump( + __in ULONG length, + __in_bcount(length) CONST PVOID buffer + ) +{ + WPP_HEXDUMP WppHexDump; + if (length > USHORT_MAX) + { + length = USHORT_MAX; + } + WppHexDump.Length = (USHORT)length; + WppHexDump.Buffer = buffer; + return WppHexDump; +} + +#define LOG_HEXDUMP(length, buffer) \ + P4vfsCreateWppHexDump(length, buffer) + + // begin_wpp config // FUNC P4vfsTraceError{ LEVEL=TRACE_LEVEL_ERROR }(FLAGS, MSG, ...); @@ -31,4 +66,7 @@ // FUNC P4vfsTraceInfo{ LEVEL=TRACE_LEVEL_INFORMATION }(FLAGS, MSG, ...); // USEPREFIX(P4vfsTraceInfo, "%!STDPREFIX! [%!FILE! @ %!LINE!] INFO:%!SPACE!"); +// DEFINE_CPLX_TYPE(HEXDUMP, WPP_LOGHEXDUMP, WPP_HEXDUMP, ItemHEXDump, "s", _HEX_, 0, 2); +// DEFINE_CPLX_TYPE(FILEIDPATH, WPP_LOGFILEIDPATH, PUNICODE_STRING, ItemHexBytes, "s", _HEX_, 0, 2); + // end_wpp diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index b170844..69e6012 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 5 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 10 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index 47e159a..f37368c 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -859,7 +859,7 @@ P4vfsSetFileWritable( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsSetFileWritable: FltCreateFileEx2 failed fileIdPath [%wZ] [%!STATUS!]", pFileIdPath, status); + P4vfsTraceError(Core, L"P4vfsSetFileWritable: FltCreateFileEx2 failed fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileIdPath, status); goto CLEANUP; } @@ -872,7 +872,7 @@ P4vfsSetFileWritable( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsSetFileWritable: FltQueryInformationFile FileBasicInformation fileIdPath [%wZ] [%!STATUS!]", pFileIdPath, status); + P4vfsTraceError(Core, L"P4vfsSetFileWritable: FltQueryInformationFile FileBasicInformation fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileIdPath, status); goto CLEANUP; } @@ -888,7 +888,7 @@ P4vfsSetFileWritable( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsSetFileWritable: FltSetInformationFile FileBasicInformation fileIdPath [%wZ] [%!STATUS!]", pFileIdPath, status); + P4vfsTraceError(Core, L"P4vfsSetFileWritable: FltSetInformationFile FileBasicInformation fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileIdPath, status); goto CLEANUP; } } @@ -1042,7 +1042,7 @@ P4vfsGetFileIdByFileName( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsGetFileIdByFileName: RtlAppendUnicodeToString separator failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + P4vfsTraceError(Core, L"P4vfsGetFileIdByFileName: RtlAppendUnicodeToString separator failed [%wZ] fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileName, &fileIdPath, status); goto CLEANUP; } @@ -1055,7 +1055,7 @@ P4vfsGetFileIdByFileName( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsGetFileIdByFileName: RtlAppendUnicodeToString fileIdString failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + P4vfsTraceError(Core, L"P4vfsGetFileIdByFileName: RtlAppendUnicodeToString fileIdString failed [%wZ] fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileName, &fileIdPath, status); goto CLEANUP; } @@ -1317,7 +1317,7 @@ P4vfsOpenReparsePoint( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 failed [%wZ] fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileName, &fileIdPath, status); goto CLEANUP; } @@ -1330,14 +1330,14 @@ P4vfsOpenReparsePoint( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: P4vfsPushOpenFileObject failed [%wZ] fileIdPath [%wZ] [%!STATUS!]", pFileName, &fileIdPath, status); + P4vfsTraceError(Core, L"P4vfsOpenReparsePoint: P4vfsPushOpenFileObject failed [%wZ] fileIdPath [%!FILEIDPATH!] [%!STATUS!]", pFileName, &fileIdPath, status); goto CLEANUP; } pFileHandle->fileHandle = openFileObject.fileHandle; pFileHandle->fileId = openFileObject.fileId; - P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%wZ] Id [0x%I64x] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fileId.data, status); + P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%!FILEIDPATH!] Id [0x%016I64X] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fileId.data, status); hLocalFile = NULL; pLocalFileObject = NULL; @@ -1391,7 +1391,7 @@ P4vfsCloseReparsePoint( if (!NT_SUCCESS(status)) { - P4vfsTraceError(Core, L"P4vfsCloseReparsePoint: P4vfsPopOpenFileObject failed with fileId [0x%I64x] [%!STATUS!]", pFileHandle->fileId.data, status); + P4vfsTraceError(Core, L"P4vfsCloseReparsePoint: P4vfsPopOpenFileObject failed with fileId [0x%016I64X] [%!STATUS!]", pFileHandle->fileId.data, status); goto CLEANUP; } diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index abc61ed..1817de2 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -13,6 +13,7 @@ Module Name: --*/ #include +#include #include "DriverCore.h" #include "DriverFilter.h" #include "DriverFilter.tmh" From d47d2f719cd58a91c0478cd433aeca7609b4afce Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Thu, 23 Jul 2026 21:09:11 -0700 Subject: [PATCH 11/23] fixing PVOID --- source/P4VFS.Driver/Include/DriverTrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/P4VFS.Driver/Include/DriverTrace.h b/source/P4VFS.Driver/Include/DriverTrace.h index d5167b8..f1d8da1 100644 --- a/source/P4VFS.Driver/Include/DriverTrace.h +++ b/source/P4VFS.Driver/Include/DriverTrace.h @@ -31,7 +31,7 @@ typedef struct _WPP_HEXDUMP { USHORT Length; - __field_bcount(Length) CONST PVOID Buffer; + __field_bcount(Length) CONST VOID* Buffer; } WPP_HEXDUMP, *PWPP_HEXDUMP; __inline From 994e3addc9db2ac104f1cf046c77a8a9eb3194dc Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Fri, 24 Jul 2026 22:43:16 -0700 Subject: [PATCH 12/23] unit test fixes for elevation --- source/P4VFS.Core/Tests/TestFileOperations.cpp | 6 +++--- source/P4VFS.Driver/Scripts/TestPush.bat | 4 ++-- source/P4VFS.Driver/Scripts/TestSetup.bat | 7 +++++++ source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs | 1 + 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/source/P4VFS.Core/Tests/TestFileOperations.cpp b/source/P4VFS.Core/Tests/TestFileOperations.cpp index 8af0cd3..1e96494 100644 --- a/source/P4VFS.Core/Tests/TestFileOperations.cpp +++ b/source/P4VFS.Core/Tests/TestFileOperations.cpp @@ -136,12 +136,12 @@ void TestFileOperationsAccess(const TestContext& context) AssertTypeLogFile(testElevatedOutputFile, TEXT("[AccessElevated] ")); Assert(testElevatedResult == 0); - // Confirm successfull run of p4vfs test TestFileOperationsAccessUnelevated + // Confirm unsuccessfull run of p4vfs test TestFileOperationsAccessUnelevated const String testUnelevatedOutputFile = FileInfo::FullPath(StringInfo::Format(TEXT("%s\\test-unelevated.txt"), localRootFolder.c_str()).c_str()); int32_t testUnelevatedPriority = P4VFS_FIND_TEST(TestFileOperationsAccessUnelevated).m_Priority; int32_t testUnelevatedResult = TestUtilities::ExecuteWait(context, StringInfo::Format(TEXT("cmd.exe /s /c %s test -e %d > \"%s\" 2>&1"), p4vfsExe.c_str(), testUnelevatedPriority, testUnelevatedOutputFile.c_str()), nullptr, Process::ExecuteFlags::Unelevated); AssertTypeLogFile(testUnelevatedOutputFile, TEXT("[AccessUnelevated] ")); - Assert(testUnelevatedResult == 0); + Assert(testUnelevatedResult == 1); } void AssertFileOperationsAccessInternal(const TestContext& context, bool isElevated) @@ -149,7 +149,7 @@ void AssertFileOperationsAccessInternal(const TestContext& context, bool isEleva // Confirm successfull run of elevated process if expected Assert((TestUtilities::ExecuteWait(context, TEXT("fltmc.exe")) == 0) == isElevated); - // Attempt to open read/write an an existing file under an elevation protected folder. In the future we may want to restrict this to isElevated only + // Attempt to open read/write an an existing file under an elevation protected folder. This is restricted to elevated only const String adminFilePath = FileOperations::GetExpandedEnvironmentStrings(TEXT("%ProgramFiles%\\P4VFS\\P4VFS.Notes.txt")); Assert(FileInfo::IsRegular(adminFilePath.c_str())); P4VFS_FLT_FILE_HANDLE adminFileHandle = FileOperations::OpenReparsePointFile(adminFilePath.c_str(), FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0); diff --git a/source/P4VFS.Driver/Scripts/TestPush.bat b/source/P4VFS.Driver/Scripts/TestPush.bat index eb023c1..658c25d 100644 --- a/source/P4VFS.Driver/Scripts/TestPush.bat +++ b/source/P4VFS.Driver/Scripts/TestPush.bat @@ -11,9 +11,9 @@ IF NOT "%1" == "" ( ) SET DEPLOY_FOLDER=\\%DEPLOY_COMPUTERNAME%\C$\P4VFS -SET ROBOCOPY_COMMON_OPTIONS=/XD .* lib include /MT /S +SET ROBOCOPY_COMMON_OPTIONS=/XD .* *.tlog lib include /MT /S /NJH /NJS /NP -robocopy.exe %REPO_FOLDER%\source %DEPLOY_FOLDER%\source /XD .* /MT /S +robocopy.exe %REPO_FOLDER%\source %DEPLOY_FOLDER%\source %ROBOCOPY_COMMON_OPTIONS% robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Setup %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Setup %ROBOCOPY_COMMON_OPTIONS% robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Driver %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Driver %ROBOCOPY_COMMON_OPTIONS% robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Console %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Console %ROBOCOPY_COMMON_OPTIONS% diff --git a/source/P4VFS.Driver/Scripts/TestSetup.bat b/source/P4VFS.Driver/Scripts/TestSetup.bat index 55c6c09..da27452 100644 --- a/source/P4VFS.Driver/Scripts/TestSetup.bat +++ b/source/P4VFS.Driver/Scripts/TestSetup.bat @@ -34,5 +34,12 @@ IF NOT "%ERRORLEVEL%"=="0" ( EXIT /B 1 ) +:: Ensure the VHD powershell module is installed +powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$ProgressPreference='SilentlyContinue'; if (-not (Get-Command New-VHD -ErrorAction SilentlyContinue)) { Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-Management-PowerShell -All -NoRestart }" +IF NOT "%ERRORLEVEL%"=="0" ( + ECHO Failed to install New-VHD module + EXIT /B 1 +) + ECHO Successfully setup machine for test drivers. Reboot is probably required. EXIT /B 0 diff --git a/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs b/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs index ede692e..5fe42e5 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs @@ -472,6 +472,7 @@ public void DevDriveSupportTest() "; ProcessInfo.ExecuteResultOutput mountOutput = executePowershellCommand(mountDevDriveScript); + VirtualFileSystemLog.Info(mountOutput.Text); Assert(mountOutput?.ExitCode == 0); Assert(File.Exists(devDriveVhd)); From fd2371b0949f8b622cc5512772aa35b4599140cc Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 25 Jul 2026 15:13:36 -0700 Subject: [PATCH 13/23] additional driver security fixes --- external/OpenSSL/OpenSSL.Module.cs | 2 +- external/P4API/P4API.Module.cs | 2 +- source/P4VFS.Console/P4VFS.Notes.txt | 16 +++- source/P4VFS.Core/Tests/TestDriver.cpp | 39 +++++---- source/P4VFS.Driver/Include/DriverCore.h | 20 ++++- source/P4VFS.Driver/Include/DriverFilter.h | 3 +- source/P4VFS.Driver/Include/DriverTrace.h | 2 +- source/P4VFS.Driver/Include/DriverVersion.h | 2 +- source/P4VFS.Driver/Source/DriverCore.c | 91 +++++++++++++++------ source/P4VFS.Driver/Source/DriverFilter.c | 75 +++++++++-------- 10 files changed, 170 insertions(+), 82 deletions(-) diff --git a/external/OpenSSL/OpenSSL.Module.cs b/external/OpenSSL/OpenSSL.Module.cs index d30a5e5..b0e5dcf 100644 --- a/external/OpenSSL/OpenSSL.Module.cs +++ b/external/OpenSSL/OpenSSL.Module.cs @@ -12,7 +12,7 @@ namespace Microsoft.P4VFS.External { public class OpensslModule : Module { - private const string OPENSSL_VERSION = "3.0.18"; + private const string OPENSSL_VERSION = "3.0.21"; private const string PERL_PACKAGE_NAME = "StrawberryPerl"; private const string PERL_VERSION = "5.28.0.1"; diff --git a/external/P4API/P4API.Module.cs b/external/P4API/P4API.Module.cs index 9c9df93..9c3204a 100644 --- a/external/P4API/P4API.Module.cs +++ b/external/P4API/P4API.Module.cs @@ -12,7 +12,7 @@ namespace Microsoft.P4VFS.External { public class P4apiModule : Module { - private const string P4API_VERSION = "r25.2"; + private const string P4API_VERSION = "r26.1"; private const string P4API_VISUAL_STUDIO_EDITION = "2022"; public override string Name diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 84d93a2..2fbff17 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -1,7 +1,21 @@ Microsoft P4VFS Release Notes Version [1.30.0.0] -* Security updates addressing MSRC-126533, MSRC-121148, MSRC-121146, MSRC-121144 +* Updating to P4API 26.1 +* Updating to OpenSSL 3.0.21 as recommended by P4API 26.1 +* Driver security updates to address Microsoft Security Response Center recommendations + MSRC-126533, MSRC-121148, MSRC-121146, MSRC-121144, MSRC-123527, MSRC-121150, MSRC-121128 +* Driver user mode control port messages now require process to have elevated access + for any file operations +* Driver P4VFS_FLT_FILE_HANDLE data shared now indirectly maps to kernel mode objects + to prevent possible tampering in user mode +* Driver user mode control port now uses structured exception handling prevent possible + access violation to user mode input and output buffer. This could be caused by a time-of-check + time-of-use (TOCTOU) race condition. This also prevents possible alignment failures + casting and using misaligned user-mode P4VFS_CONTROL_MSG & P4VFS_CONTROL_REPLY buffers. +* Driver now guarding against invalid offsets in P4VFS_UNICODE_STRING from user mode. +* Driver WPP tracing fixes for unicode file paths with FILE_ID_128 suffix +* Additional driver unit tests for internal P4VFS_FLT_FILE_HANDLE translation Version [1.29.4.0] * Updating to latest P4API 25.2 diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index 6072b62..6619b3a 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -280,7 +280,8 @@ typedef enum _POOL_TYPE { #define P4VFS_DEFAULT_ACTION() [](...) -> VOID { } #define P4VFS_DEFAULT_FUNCTION(retType, retValue) [](...) -> retType { return retValue; } -#define P4VFS_DEFAULT_FUNCTION_NTSTATUS() P4VFS_DEFAULT_FUNCTION(NTSTATUS, STATUS_UNSUCCESSFUL) +#define P4VFS_DEFAULT_FUNCTION_NTSTATUS() [](...) -> NTSTATUS { return STATUS_UNSUCCESSFUL; } +#define P4VFS_UNIMPLEMENTED_FUNCTION_NTSTATUS() [](...) -> NTSTATUS { Assert(false); return STATUS_UNSUCCESSFUL; } std::function FltGetFileNameInformation; std::function FltParseFileNameInformation; @@ -303,6 +304,8 @@ std::function ExAllocatePoolZero; std::function ExFreePoolWithTag; std::function ExAcquireFastMutex; std::function ExReleaseFastMutex; +std::function ProbeForRead; +std::function ProbeForWrite; NTSTATUS RtlAppendUnicodeToString(PUNICODE_STRING dst, PCWSTR src) { @@ -464,6 +467,8 @@ static void InternalTestDriverReset(const TestContext& context) ExFreePoolWithTag = [](PVOID p, ULONG) -> VOID { GFree(p); }; ExAcquireFastMutex = P4VFS_DEFAULT_ACTION(); ExReleaseFastMutex = P4VFS_DEFAULT_ACTION(); + ProbeForRead = P4VFS_UNIMPLEMENTED_FUNCTION_NTSTATUS(); + ProbeForWrite = P4VFS_UNIMPLEMENTED_FUNCTION_NTSTATUS(); }; static UNICODE_STRING CStrToUnicodeString(const WCHAR* text) @@ -555,11 +560,11 @@ void TestDriverUnicodeString(const TestContext& context) void TestDriverOpenFileObjectList(const TestContext& context) { - auto MakeFileObject = [](const PVOID fileObject, const PVOID fileHandle) -> P4VFS_OPEN_FILE_OBJECT + auto MakeFileObject = [](const VOID* fileObject, const VOID* fileHandle) -> P4VFS_OPEN_FILE_OBJECT { P4VFS_OPEN_FILE_OBJECT obj = {0}; obj.pFileObject = (PFILE_OBJECT)fileObject; - obj.fileHandle = fileHandle; + obj.fltFileHandle.fileHandle = (PVOID)fileHandle; return obj; }; @@ -568,35 +573,35 @@ void TestDriverOpenFileObjectList(const TestContext& context) const P4VFS_OPEN_FILE_OBJECT src0 = MakeFileObject(L"fo0", L"fh0"); P4VFS_OPEN_FILE_OBJECT push0 = {0}; - Assert(P4vfsPushOpenFileObject(src0.pFileObject, src0.fileHandle, &push0) == STATUS_SUCCESS); - Assert(src0.pFileObject == push0.pFileObject && src0.fileHandle == push0.fileHandle && push0.fileId.data != 0 && push0.pNext == NULL); + Assert(P4vfsPushOpenFileObject(src0.pFileObject, src0.fltFileHandle.fileHandle, &push0) == STATUS_SUCCESS); + Assert(src0.pFileObject == push0.pFileObject && src0.fltFileHandle.fileHandle == push0.fltFileHandle.fileHandle && push0.fltFileHandle.fileId.data != 0 && push0.pNext == NULL); Assert(g_FltContext.pOpenFileObjectList != NULL); const P4VFS_OPEN_FILE_OBJECT src1 = MakeFileObject(L"fo1", L"fh1"); P4VFS_OPEN_FILE_OBJECT push1 = {0}; - Assert(P4vfsPushOpenFileObject(src1.pFileObject, src1.fileHandle, &push1) == STATUS_SUCCESS); - Assert(src1.pFileObject == push1.pFileObject && src1.fileHandle == push1.fileHandle && push1.fileId.data != 0 && push1.pNext == NULL); - Assert(push1.fileId.data != push0.fileId.data); + Assert(P4vfsPushOpenFileObject(src1.pFileObject, src1.fltFileHandle.fileHandle, &push1) == STATUS_SUCCESS); + Assert(src1.pFileObject == push1.pFileObject && src1.fltFileHandle.fileHandle == push1.fltFileHandle.fileHandle && push1.fltFileHandle.fileId.data != 0 && push1.pNext == NULL); + Assert(push1.fltFileHandle.fileId.data != push0.fltFileHandle.fileId.data); P4VFS_OPEN_FILE_OBJECT pop1 = {0}; - Assert(P4vfsPopOpenFileObject(&push1.fileId, &pop1) == STATUS_SUCCESS); - Assert(src1.pFileObject == pop1.pFileObject && src1.fileHandle == pop1.fileHandle && push1.fileId.data == pop1.fileId.data && pop1.pNext == NULL); + Assert(P4vfsPopOpenFileObject(&push1.fltFileHandle, &pop1) == STATUS_SUCCESS); + Assert(src1.pFileObject == pop1.pFileObject && src1.fltFileHandle.fileHandle == pop1.fltFileHandle.fileHandle && push1.fltFileHandle.fileId.data == pop1.fltFileHandle.fileId.data && pop1.pNext == NULL); P4VFS_OPEN_FILE_OBJECT push2 = {0}; - Assert(P4vfsPushOpenFileObject(src0.pFileObject, src0.fileHandle, &push2) == STATUS_SUCCESS); - Assert(src0.pFileObject == push2.pFileObject && src0.fileHandle == push2.fileHandle && push2.fileId.data != 0 && push2.pNext == NULL); + Assert(P4vfsPushOpenFileObject(src0.pFileObject, src0.fltFileHandle.fileHandle, &push2) == STATUS_SUCCESS); + Assert(src0.pFileObject == push2.pFileObject && src0.fltFileHandle.fileHandle == push2.fltFileHandle.fileHandle && push2.fltFileHandle.fileId.data != 0 && push2.pNext == NULL); P4VFS_OPEN_FILE_OBJECT pop0 = {0}; - Assert(P4vfsPopOpenFileObject(&push0.fileId, &pop0) == STATUS_SUCCESS); - Assert(pop0.pFileObject == push0.pFileObject && pop0.fileHandle == push0.fileHandle && pop0.fileId.data == push0.fileId.data && pop0.pNext == NULL); + Assert(P4vfsPopOpenFileObject(&push0.fltFileHandle, &pop0) == STATUS_SUCCESS); + Assert(pop0.pFileObject == push0.pFileObject && pop0.fltFileHandle.fileHandle == push0.fltFileHandle.fileHandle && pop0.fltFileHandle.fileId.data == push0.fltFileHandle.fileId.data && pop0.pNext == NULL); P4VFS_OPEN_FILE_OBJECT pop3 = {0}; - Assert(P4vfsPopOpenFileObject(&push0.fileId, &pop3) == STATUS_NOT_FOUND); + Assert(P4vfsPopOpenFileObject(&push0.fltFileHandle, &pop3) == STATUS_NOT_FOUND); P4VFS_OPEN_FILE_OBJECT pop2 = {0}; - Assert(P4vfsPopOpenFileObject(&push2.fileId, &pop2) == STATUS_SUCCESS); - Assert(pop2.pFileObject == push2.pFileObject && pop2.fileHandle == push2.fileHandle && pop2.fileId.data != 0 && pop2.pNext == NULL); + Assert(P4vfsPopOpenFileObject(&push2.fltFileHandle, &pop2) == STATUS_SUCCESS); + Assert(pop2.pFileObject == push2.pFileObject && pop2.fltFileHandle.fileHandle == push2.fltFileHandle.fileHandle && pop2.fltFileHandle.fileId.data != 0 && pop2.pNext == NULL); Assert(g_FltContext.pOpenFileObjectList == NULL); } diff --git a/source/P4VFS.Driver/Include/DriverCore.h b/source/P4VFS.Driver/Include/DriverCore.h index ad794bb..adb8561 100644 --- a/source/P4VFS.Driver/Include/DriverCore.h +++ b/source/P4VFS.Driver/Include/DriverCore.h @@ -98,7 +98,7 @@ P4vfsCopyAssignUnicodeString( _In_ P4VFS_UNICODE_STRING* pTargetString, _In_ VOID* pTargetBuffer, _In_ ULONG targetBufferSize, - _In_ const VOID* pSourceBuffer, + _In_ CONST VOID* pSourceBuffer, _In_ ULONG sourceBufferSize ); @@ -110,7 +110,7 @@ P4vfsAllocateUnicodeString( NTSTATUS P4vfsToUnicodeString( - _In_ const P4VFS_UNICODE_STRING* pSrcString, + _In_ CONST P4VFS_UNICODE_STRING* pSrcString, _Out_ UNICODE_STRING* pDstString ); @@ -136,7 +136,7 @@ P4vfsPushOpenFileObject( NTSTATUS P4vfsPopOpenFileObject( - _In_ const P4VFS_FLT_FILE_ID* pFileId, + _In_ P4VFS_FLT_FILE_HANDLE* pFileHandle, _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject ); @@ -156,3 +156,17 @@ BOOLEAN P4vfsIsCurrentProcessElevated( ); +NTSTATUS +P4vfsReadUserMemory( + _Out_ PVOID pTargetBuffer, + _In_ CONST VOID* pUserSourceBuffer, + _In_ ULONG dwLength + ); + +NTSTATUS +P4vfsWriteUserMemory( + _Out_ PVOID pUserTargetBuffer, + _In_ CONST VOID* pSourceBuffer, + _In_ ULONG dwLength + ); + diff --git a/source/P4VFS.Driver/Include/DriverFilter.h b/source/P4VFS.Driver/Include/DriverFilter.h index 9553abb..b9e97d9 100644 --- a/source/P4VFS.Driver/Include/DriverFilter.h +++ b/source/P4VFS.Driver/Include/DriverFilter.h @@ -15,9 +15,8 @@ typedef struct _P4VFS_REPARSE_ACTION typedef struct _P4VFS_OPEN_FILE_OBJECT { struct _P4VFS_OPEN_FILE_OBJECT* pNext; - P4VFS_FLT_FILE_ID fileId; + P4VFS_FLT_FILE_HANDLE fltFileHandle; PFILE_OBJECT pFileObject; - HANDLE fileHandle; } P4VFS_OPEN_FILE_OBJECT; typedef struct _P4VFS_FLT_CONTEXT diff --git a/source/P4VFS.Driver/Include/DriverTrace.h b/source/P4VFS.Driver/Include/DriverTrace.h index f1d8da1..e3c33a1 100644 --- a/source/P4VFS.Driver/Include/DriverTrace.h +++ b/source/P4VFS.Driver/Include/DriverTrace.h @@ -38,7 +38,7 @@ __inline WPP_HEXDUMP P4vfsCreateWppHexDump( __in ULONG length, - __in_bcount(length) CONST PVOID buffer + __in_bcount(length) CONST VOID* buffer ) { WPP_HEXDUMP WppHexDump; diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index 69e6012..a351925 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 10 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 12 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index f37368c..ec98a0d 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -230,12 +230,12 @@ P4vfsUserModeResolveFile( ULONG requestMsgSize = sizeof(P4VFS_SERVICE_MSG); - const ULONG volumeNameSize = pFileNameInfo->Volume.Length + sizeof(WCHAR); - const ULONG volumeNameOffset = requestMsgSize; + CONST ULONG volumeNameSize = pFileNameInfo->Volume.Length + sizeof(WCHAR); + CONST ULONG volumeNameOffset = requestMsgSize; requestMsgSize += volumeNameSize; - const ULONG dataNameSize = pFileNameInfo->Name.Length + sizeof(WCHAR); - const ULONG dataNameOffset = requestMsgSize; + CONST ULONG dataNameSize = pFileNameInfo->Name.Length + sizeof(WCHAR); + CONST ULONG dataNameOffset = requestMsgSize; requestMsgSize += dataNameSize; pRequestMsg = (P4VFS_SERVICE_MSG*)ExAllocatePoolZero( @@ -692,7 +692,7 @@ P4vfsCopyAssignUnicodeString( _In_ P4VFS_UNICODE_STRING* pTargetString, _In_ VOID* pTargetBuffer, _In_ ULONG targetBufferSize, - _In_ const VOID* pSourceBuffer, + _In_ CONST VOID* pSourceBuffer, _In_ ULONG sourceBufferSize ) { @@ -721,8 +721,8 @@ P4vfsCopyAssignUnicodeString( goto CLEANUP; } - const INT64 ptrOffset = ((CHAR*)pTargetBuffer) - ((CHAR*)pTargetString); - const LONG longOffset = (LONG)ptrOffset; + CONST INT64 ptrOffset = ((CHAR*)pTargetBuffer) - ((CHAR*)pTargetString); + CONST LONG longOffset = (LONG)ptrOffset; if (((INT64)longOffset) != ptrOffset) { status = STATUS_INTEGER_OVERFLOW; @@ -774,7 +774,7 @@ P4vfsAllocateUnicodeString( NTSTATUS P4vfsToUnicodeString( - _In_ const P4VFS_UNICODE_STRING* pSrcString, + _In_ CONST P4VFS_UNICODE_STRING* pSrcString, _Out_ UNICODE_STRING* pDstString ) { @@ -1140,10 +1140,10 @@ P4vfsPushOpenFileObject( // The interal pLinkedFileObject will reside in the linked list of P4VFS_OPEN_FILE_OBJECT // The fileId is a unique and obfuscated 64bit value which will serve well to track this handle in user mode. - // We can also use the fileHandle to cross-reference with the fileId if needed for additional verification - pLinkedFileObject->fileId.data = (nextUniqueId<<32) | (processId<<16) | threadId; + // We can also use the fileHandle to cross-reference with the fileId for additional verification + pLinkedFileObject->fltFileHandle.fileId.data = (nextUniqueId<<32) | (processId<<16) | threadId; + pLinkedFileObject->fltFileHandle.fileHandle = fileHandle; pLinkedFileObject->pFileObject = pFileObject; - pLinkedFileObject->fileHandle = fileHandle; // Insert the pLinkedFileObject into the linked list // Assign a copy of the pLinkedFileObject which we are now tracking @@ -1163,7 +1163,7 @@ P4vfsPushOpenFileObject( NTSTATUS P4vfsPopOpenFileObject( - _In_ const P4VFS_FLT_FILE_ID* pFileId, + _In_ P4VFS_FLT_FILE_HANDLE* pFileHandle, _Out_ P4VFS_OPEN_FILE_OBJECT* pOpenFileObject ) { @@ -1181,19 +1181,19 @@ P4vfsPopOpenFileObject( goto CLEANUP; } - if (pFileId == NULL) + if (pFileHandle == NULL) { status = STATUS_INVALID_PARAMETER; - P4vfsTraceError(Core, L"P4vfsPopOpenFileObject: pFileId is NULL"); + P4vfsTraceError(Core, L"P4vfsPopOpenFileObject: pFileHandle is NULL"); goto CLEANUP; } - // Search our linked list of known file objects for one with this pFileId + // Search our linked list of known file objects for one with this pFileId and fileHandle ExAcquireFastMutex(&g_FltContext.hOpenFileObjectLock); { for (pObject = g_FltContext.pOpenFileObjectList; pObject != NULL; pObject = pObject->pNext) { - if (pObject->fileId.data == pFileId->data) + if (pObject->fltFileHandle.fileId.data == pFileHandle->fileId.data && pObject->fltFileHandle.fileHandle == pFileHandle->fileHandle) { break; } @@ -1334,10 +1334,8 @@ P4vfsOpenReparsePoint( goto CLEANUP; } - pFileHandle->fileHandle = openFileObject.fileHandle; - pFileHandle->fileId = openFileObject.fileId; - - P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%!FILEIDPATH!] Id [0x%016I64X] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fileId.data, status); + *pFileHandle = openFileObject.fltFileHandle; + P4vfsTraceInfo(Core, L"P4vfsOpenReparsePoint: FltCreateFileEx2 success [%wZ] fileIdPath [%!FILEIDPATH!] Id [0x%016I64X] [%!STATUS!]", pFileName, &fileIdPath, openFileObject.fltFileHandle.fileId.data, status); hLocalFile = NULL; pLocalFileObject = NULL; @@ -1386,7 +1384,7 @@ P4vfsCloseReparsePoint( // We do not trust the HANDLE passed in from P4VFS_FLT_FILE_HANDLE. Instead we'll look for the P4VFS_OPEN_FILE_OBJECT by Id // and confirm the HANDLE and ownership is what we expect. - status = P4vfsPopOpenFileObject(&pFileHandle->fileId, + status = P4vfsPopOpenFileObject(pFileHandle, &openFileObject); if (!NT_SUCCESS(status)) @@ -1395,9 +1393,9 @@ P4vfsCloseReparsePoint( goto CLEANUP; } - if (openFileObject.fileHandle != NULL) + if (openFileObject.fltFileHandle.fileHandle != NULL) { - FltClose(openFileObject.fileHandle); + FltClose(openFileObject.fltFileHandle.fileHandle); } if (openFileObject.pFileObject != NULL) @@ -1473,3 +1471,50 @@ P4vfsIsCurrentProcessElevated( return result; } +NTSTATUS +P4vfsReadUserMemory( + _Out_ PVOID pTargetBuffer, + _In_ CONST VOID* pUserSourceBuffer, + _In_ ULONG dwLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + __try + { + ProbeForRead((PVOID)pUserSourceBuffer, dwLength, sizeof(UCHAR)); + RtlCopyMemory(pTargetBuffer, pUserSourceBuffer, dwLength); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + return status; +} + +NTSTATUS +P4vfsWriteUserMemory( + _Out_ PVOID pUserTargetBuffer, + _In_ CONST VOID* pSourceBuffer, + _In_ ULONG dwLength + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + PAGED_CODE(); + + __try + { + ProbeForWrite(pUserTargetBuffer, dwLength, sizeof(UCHAR)); + RtlCopyMemory(pUserTargetBuffer, pSourceBuffer, dwLength); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + status = GetExceptionCode(); + } + + return status; +} diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index 1817de2..bb834c4 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -723,21 +723,21 @@ P4vfsControlPortMessage( ) { NTSTATUS status = STATUS_SUCCESS; - P4VFS_CONTROL_MSG* input = NULL; - P4VFS_CONTROL_REPLY* output = NULL; + P4VFS_CONTROL_MSG input = {0}; + P4VFS_CONTROL_REPLY output = {0}; UNREFERENCED_PARAMETER(pPortCookie); PAGED_CODE(); - if (pInputBuffer == NULL || dwInputBufferLength < sizeof(P4VFS_CONTROL_MSG)) + if (pInputBuffer == NULL || dwInputBufferLength < sizeof(input)) { status = STATUS_INVALID_PARAMETER; P4vfsTraceError(Filter, L"P4vfsControlPortMessage: pInputBuffer is invalid [%ld]", dwInputBufferLength); goto CLEANUP; } - if (pOutputBuffer == NULL || dwOutputBufferLength < sizeof(P4VFS_CONTROL_REPLY)) + if (pOutputBuffer == NULL || dwOutputBufferLength < sizeof(output)) { status = STATUS_INVALID_PARAMETER; P4vfsTraceError(Filter, L"P4vfsControlPortMessage: pOutputBuffer is invalid [%ld]", dwOutputBufferLength); @@ -751,27 +751,27 @@ P4vfsControlPortMessage( goto CLEANUP; } - input = (P4VFS_CONTROL_MSG*)pInputBuffer; - output = (P4VFS_CONTROL_REPLY*)pOutputBuffer; - - RtlZeroMemory(output, sizeof(P4VFS_CONTROL_REPLY)); - output->operation = input->operation; - *pReturnOutputBufferLength = sizeof(P4VFS_CONTROL_REPLY); + status = P4vfsReadUserMemory(&input, pInputBuffer, sizeof(input)); + if (!NT_SUCCESS(status)) + { + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to read user-mode pInputBuffer"); + goto CLEANUP; + } - switch (input->operation) + switch (input.operation) { case P4VFS_OPERATION_GET_IS_CONNECTED: { - output->data.GET_IS_CONNECTED.connected = g_FltContext.pServiceClientPort ? 1 : 0; - P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_GET_IS_CONNECTED [%d]", output->data.GET_IS_CONNECTED.connected); + output.data.GET_IS_CONNECTED.connected = g_FltContext.pServiceClientPort ? 1 : 0; + P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_GET_IS_CONNECTED [%d]", output.data.GET_IS_CONNECTED.connected); break; } case P4VFS_OPERATION_GET_VERSION: { - output->data.GET_VERSION.major = P4VFS_VER_MAJOR; - output->data.GET_VERSION.minor = P4VFS_VER_MINOR; - output->data.GET_VERSION.build = P4VFS_VER_BUILD; - output->data.GET_VERSION.revision = P4VFS_VER_REVISION; + output.data.GET_VERSION.major = P4VFS_VER_MAJOR; + output.data.GET_VERSION.minor = P4VFS_VER_MINOR; + output.data.GET_VERSION.build = P4VFS_VER_BUILD; + output.data.GET_VERSION.revision = P4VFS_VER_REVISION; P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_GET_VERSION [%ls]", P4VFS_VER_VERSION_STRING); break; } @@ -784,18 +784,18 @@ P4vfsControlPortMessage( break; } - const WCHAR strSanitizeAttributes[] = L"SanitizeAttributes"; - if (RtlCompareMemory(strSanitizeAttributes, input->data.SET_FLAG.name, sizeof(strSanitizeAttributes)) == sizeof(strSanitizeAttributes)) + CONST WCHAR strSanitizeAttributes[] = L"SanitizeAttributes"; + if (RtlCompareMemory(strSanitizeAttributes, input.data.SET_FLAG.name, sizeof(strSanitizeAttributes)) == sizeof(strSanitizeAttributes)) { - g_FltContext.bSanitizeAttributes = !!input->data.SET_FLAG.value; + g_FltContext.bSanitizeAttributes = !!input.data.SET_FLAG.value; P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_SET_FLAG SanitizeAttributes = [0x%08d]", (LONG)g_FltContext.bSanitizeAttributes); break; } - const WCHAR strShareModeDuringHydration[] = L"ShareModeDuringHydration"; - if (RtlCompareMemory(strShareModeDuringHydration, input->data.SET_FLAG.name, sizeof(strShareModeDuringHydration)) == sizeof(strShareModeDuringHydration)) + CONST WCHAR strShareModeDuringHydration[] = L"ShareModeDuringHydration"; + if (RtlCompareMemory(strShareModeDuringHydration, input.data.SET_FLAG.name, sizeof(strShareModeDuringHydration)) == sizeof(strShareModeDuringHydration)) { - g_FltContext.bShareModeDuringHydration = !!input->data.SET_FLAG.value; + g_FltContext.bShareModeDuringHydration = !!input.data.SET_FLAG.value; P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_SET_FLAG ShareModeDuringHydration = [0x%08d]", (LONG)g_FltContext.bShareModeDuringHydration); break; } @@ -813,7 +813,7 @@ P4vfsControlPortMessage( } UNICODE_STRING unicodeFilePath = {0}; - status = P4vfsToUnicodeString(&input->data.OPEN_REPARSE_POINT.filePath, &unicodeFilePath); + status = P4vfsToUnicodeString(&input.data.OPEN_REPARSE_POINT.filePath, &unicodeFilePath); if (!NT_SUCCESS(status)) { P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_OPERATION_OPEN_REPARSE_POINT P4vfsToUnicodeString failed [%!STATUS!]", status); @@ -821,15 +821,15 @@ P4vfsControlPortMessage( } ACCESS_MASK desiredAccess = 0; - desiredAccess |= input->data.OPEN_REPARSE_POINT.accessRead ? FILE_GENERIC_READ : 0; - desiredAccess |= input->data.OPEN_REPARSE_POINT.accessWrite ? FILE_GENERIC_WRITE : 0; - desiredAccess |= input->data.OPEN_REPARSE_POINT.accessDelete ? DELETE : 0; + desiredAccess |= input.data.OPEN_REPARSE_POINT.accessRead ? FILE_GENERIC_READ : 0; + desiredAccess |= input.data.OPEN_REPARSE_POINT.accessWrite ? FILE_GENERIC_WRITE : 0; + desiredAccess |= input.data.OPEN_REPARSE_POINT.accessDelete ? DELETE : 0; status = P4vfsOpenReparsePoint(&unicodeFilePath, desiredAccess, - &output->data.OPEN_REPARSE_POINT.handle); + &output.data.OPEN_REPARSE_POINT.handle); - output->data.OPEN_REPARSE_POINT.ntstatus = status; + output.data.OPEN_REPARSE_POINT.ntstatus = status; break; } case P4VFS_OPERATION_CLOSE_REPARSE_POINT: @@ -841,19 +841,30 @@ P4vfsControlPortMessage( break; } - status = P4vfsCloseReparsePoint(&input->data.CLOSE_REPARSE_POINT.handle); + status = P4vfsCloseReparsePoint(&input.data.CLOSE_REPARSE_POINT.handle); - output->data.CLOSE_REPARSE_POINT.ntstatus = status; + output.data.CLOSE_REPARSE_POINT.ntstatus = status; break; } default: { - P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: UNKNOWN 0x%08x", input->operation); + P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: UNKNOWN 0x%08x", input.operation); status = STATUS_INVALID_PARAMETER; break; } } + output.operation = input.operation; + NTSTATUS writeStatus = P4vfsWriteUserMemory(pOutputBuffer, &output, sizeof(output)); + if (!NT_SUCCESS(writeStatus)) + { + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to write user-mode pOutputBuffer overriding operation status [%!STATUS!] with status [%!STATUS!]", status, writeStatus); + status = writeStatus; + goto CLEANUP; + } + + *pReturnOutputBufferLength = sizeof(output); + CLEANUP: return status; } From 79d34da35e49066f7ac0a485cbe5c70d9edc058f Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 25 Jul 2026 22:25:23 -0700 Subject: [PATCH 14/23] fixing control input buffer extent --- source/P4VFS.Console/P4VFS.Notes.txt | 2 + source/P4VFS.Driver/Include/DriverCore.h | 2 + source/P4VFS.Driver/Include/DriverData.h | 1 + source/P4VFS.Driver/Source/DriverFilter.c | 104 ++++++++++++++++------ 4 files changed, 80 insertions(+), 29 deletions(-) diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 2fbff17..ce9d39e 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -16,6 +16,8 @@ Version [1.30.0.0] * Driver now guarding against invalid offsets in P4VFS_UNICODE_STRING from user mode. * Driver WPP tracing fixes for unicode file paths with FILE_ID_128 suffix * Additional driver unit tests for internal P4VFS_FLT_FILE_HANDLE translation +* Addition of TestPush.bat script to optionally use in a post-build deployment to a + virtual machine for full development driver and user-mode testing. Version [1.29.4.0] * Updating to latest P4API 25.2 diff --git a/source/P4VFS.Driver/Include/DriverCore.h b/source/P4VFS.Driver/Include/DriverCore.h index adb8561..7a3f875 100644 --- a/source/P4VFS.Driver/Include/DriverCore.h +++ b/source/P4VFS.Driver/Include/DriverCore.h @@ -14,6 +14,8 @@ #define P4VFS_SERVICE_PORT_HANDLE_ALLOC_TAG 'SsvP' #define P4VFS_CONTROL_PORT_HANDLE_ALLOC_TAG 'CsvP' #define P4VFS_OPEN_FILE_OBJECT_ALLOC_TAG 'FsvP' +#define P4VFS_CONTROL_MSG_ALLOC_TAG 'MsvP' +#define P4VFS_CONTROL_REPLY_ALLOC_TAG 'RsvP' NTSTATUS P4vfsUserModeExecuteDrvRequest( diff --git a/source/P4VFS.Driver/Include/DriverData.h b/source/P4VFS.Driver/Include/DriverData.h index 9b7e3a1..9446fa9 100644 --- a/source/P4VFS.Driver/Include/DriverData.h +++ b/source/P4VFS.Driver/Include/DriverData.h @@ -40,6 +40,7 @@ #define P4VFS_CONTROL_PORT_NAME L"\\P4VFS_CONTROL_PORT_NAME" #define P4VFS_CONTROL_FLAG_LENGTH 32 +#define P4VFS_CONTROL_MAX_BUFFER_LENGTH USHORT_MAX typedef struct _P4VFS_UNICODE_STRING { diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index bb834c4..5402e9c 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -723,21 +723,23 @@ P4vfsControlPortMessage( ) { NTSTATUS status = STATUS_SUCCESS; - P4VFS_CONTROL_MSG input = {0}; - P4VFS_CONTROL_REPLY output = {0}; + P4VFS_CONTROL_MSG* pInputMsg = NULL; + P4VFS_CONTROL_REPLY* pOutputReply = NULL; + ULONG dwInputMsgLength = 0; + ULONG dwOutputReplyLength = 0; UNREFERENCED_PARAMETER(pPortCookie); PAGED_CODE(); - if (pInputBuffer == NULL || dwInputBufferLength < sizeof(input)) + if (pInputBuffer == NULL || dwInputBufferLength < sizeof(P4VFS_CONTROL_MSG) || dwInputBufferLength > P4VFS_CONTROL_MAX_BUFFER_LENGTH) { status = STATUS_INVALID_PARAMETER; P4vfsTraceError(Filter, L"P4vfsControlPortMessage: pInputBuffer is invalid [%ld]", dwInputBufferLength); goto CLEANUP; } - if (pOutputBuffer == NULL || dwOutputBufferLength < sizeof(output)) + if (pOutputBuffer == NULL || dwOutputBufferLength < sizeof(P4VFS_CONTROL_REPLY) || dwOutputBufferLength > P4VFS_CONTROL_MAX_BUFFER_LENGTH) { status = STATUS_INVALID_PARAMETER; P4vfsTraceError(Filter, L"P4vfsControlPortMessage: pOutputBuffer is invalid [%ld]", dwOutputBufferLength); @@ -750,28 +752,60 @@ P4vfsControlPortMessage( P4vfsTraceError(Filter, L"P4vfsControlPortMessage: pReturnOutputBufferLength is NULL"); goto CLEANUP; } + + // Copy the user-mode pInputBuffer safely into kernel-mode memory to ensure proper access + // and exclusive use in this routine + + dwInputMsgLength = dwInputBufferLength; + pInputMsg = (P4VFS_CONTROL_MSG*)ExAllocatePoolZero( + NonPagedPoolNx, + dwInputMsgLength, + P4VFS_CONTROL_MSG_ALLOC_TAG); + + if (pInputMsg == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to allocate P4VFS_CONTROL_MSG size [%d]", dwInputMsgLength); + goto CLEANUP; + } - status = P4vfsReadUserMemory(&input, pInputBuffer, sizeof(input)); + status = P4vfsReadUserMemory(pInputMsg, pInputBuffer, dwInputMsgLength); if (!NT_SUCCESS(status)) { - P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to read user-mode pInputBuffer"); + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to read user-mode pInputBuffer size [%d]", dwInputMsgLength); + goto CLEANUP; + } + + // Create a reply buffer in kernel-mode memory for exclusive use in this routine, and then safely + // write to the user-mode pOutputBuffer at the end + + dwOutputReplyLength = sizeof(P4VFS_CONTROL_REPLY); + pOutputReply = (P4VFS_CONTROL_REPLY*)ExAllocatePoolZero( + NonPagedPoolNx, + dwOutputReplyLength, + P4VFS_CONTROL_REPLY_ALLOC_TAG); + + if (pOutputReply == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to allocate P4VFS_CONTROL_REPLY size [%d]", dwOutputReplyLength); goto CLEANUP; } - switch (input.operation) + switch (pInputMsg->operation) { case P4VFS_OPERATION_GET_IS_CONNECTED: { - output.data.GET_IS_CONNECTED.connected = g_FltContext.pServiceClientPort ? 1 : 0; - P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_GET_IS_CONNECTED [%d]", output.data.GET_IS_CONNECTED.connected); + pOutputReply->data.GET_IS_CONNECTED.connected = g_FltContext.pServiceClientPort ? 1 : 0; + P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_GET_IS_CONNECTED [%d]", pOutputReply->data.GET_IS_CONNECTED.connected); break; } case P4VFS_OPERATION_GET_VERSION: { - output.data.GET_VERSION.major = P4VFS_VER_MAJOR; - output.data.GET_VERSION.minor = P4VFS_VER_MINOR; - output.data.GET_VERSION.build = P4VFS_VER_BUILD; - output.data.GET_VERSION.revision = P4VFS_VER_REVISION; + pOutputReply->data.GET_VERSION.major = P4VFS_VER_MAJOR; + pOutputReply->data.GET_VERSION.minor = P4VFS_VER_MINOR; + pOutputReply->data.GET_VERSION.build = P4VFS_VER_BUILD; + pOutputReply->data.GET_VERSION.revision = P4VFS_VER_REVISION; P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_GET_VERSION [%ls]", P4VFS_VER_VERSION_STRING); break; } @@ -785,17 +819,17 @@ P4vfsControlPortMessage( } CONST WCHAR strSanitizeAttributes[] = L"SanitizeAttributes"; - if (RtlCompareMemory(strSanitizeAttributes, input.data.SET_FLAG.name, sizeof(strSanitizeAttributes)) == sizeof(strSanitizeAttributes)) + if (RtlCompareMemory(strSanitizeAttributes, pInputMsg->data.SET_FLAG.name, sizeof(strSanitizeAttributes)) == sizeof(strSanitizeAttributes)) { - g_FltContext.bSanitizeAttributes = !!input.data.SET_FLAG.value; + g_FltContext.bSanitizeAttributes = !!pInputMsg->data.SET_FLAG.value; P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_SET_FLAG SanitizeAttributes = [0x%08d]", (LONG)g_FltContext.bSanitizeAttributes); break; } CONST WCHAR strShareModeDuringHydration[] = L"ShareModeDuringHydration"; - if (RtlCompareMemory(strShareModeDuringHydration, input.data.SET_FLAG.name, sizeof(strShareModeDuringHydration)) == sizeof(strShareModeDuringHydration)) + if (RtlCompareMemory(strShareModeDuringHydration, pInputMsg->data.SET_FLAG.name, sizeof(strShareModeDuringHydration)) == sizeof(strShareModeDuringHydration)) { - g_FltContext.bShareModeDuringHydration = !!input.data.SET_FLAG.value; + g_FltContext.bShareModeDuringHydration = !!pInputMsg->data.SET_FLAG.value; P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_CONTROL_SET_FLAG ShareModeDuringHydration = [0x%08d]", (LONG)g_FltContext.bShareModeDuringHydration); break; } @@ -813,7 +847,7 @@ P4vfsControlPortMessage( } UNICODE_STRING unicodeFilePath = {0}; - status = P4vfsToUnicodeString(&input.data.OPEN_REPARSE_POINT.filePath, &unicodeFilePath); + status = P4vfsToUnicodeString(&pInputMsg->data.OPEN_REPARSE_POINT.filePath, &unicodeFilePath); if (!NT_SUCCESS(status)) { P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_OPERATION_OPEN_REPARSE_POINT P4vfsToUnicodeString failed [%!STATUS!]", status); @@ -821,15 +855,15 @@ P4vfsControlPortMessage( } ACCESS_MASK desiredAccess = 0; - desiredAccess |= input.data.OPEN_REPARSE_POINT.accessRead ? FILE_GENERIC_READ : 0; - desiredAccess |= input.data.OPEN_REPARSE_POINT.accessWrite ? FILE_GENERIC_WRITE : 0; - desiredAccess |= input.data.OPEN_REPARSE_POINT.accessDelete ? DELETE : 0; + desiredAccess |= pInputMsg->data.OPEN_REPARSE_POINT.accessRead ? FILE_GENERIC_READ : 0; + desiredAccess |= pInputMsg->data.OPEN_REPARSE_POINT.accessWrite ? FILE_GENERIC_WRITE : 0; + desiredAccess |= pInputMsg->data.OPEN_REPARSE_POINT.accessDelete ? DELETE : 0; status = P4vfsOpenReparsePoint(&unicodeFilePath, desiredAccess, - &output.data.OPEN_REPARSE_POINT.handle); + &pOutputReply->data.OPEN_REPARSE_POINT.handle); - output.data.OPEN_REPARSE_POINT.ntstatus = status; + pOutputReply->data.OPEN_REPARSE_POINT.ntstatus = status; break; } case P4VFS_OPERATION_CLOSE_REPARSE_POINT: @@ -841,21 +875,21 @@ P4vfsControlPortMessage( break; } - status = P4vfsCloseReparsePoint(&input.data.CLOSE_REPARSE_POINT.handle); + status = P4vfsCloseReparsePoint(&pInputMsg->data.CLOSE_REPARSE_POINT.handle); - output.data.CLOSE_REPARSE_POINT.ntstatus = status; + pOutputReply->data.CLOSE_REPARSE_POINT.ntstatus = status; break; } default: { - P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: UNKNOWN 0x%08x", input.operation); + P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: UNKNOWN 0x%08x", pInputMsg->operation); status = STATUS_INVALID_PARAMETER; break; } } - output.operation = input.operation; - NTSTATUS writeStatus = P4vfsWriteUserMemory(pOutputBuffer, &output, sizeof(output)); + pOutputReply->operation = pInputMsg->operation; + NTSTATUS writeStatus = P4vfsWriteUserMemory(pOutputBuffer, pOutputReply, dwOutputReplyLength); if (!NT_SUCCESS(writeStatus)) { P4vfsTraceError(Filter, L"P4vfsControlPortMessage: Failed to write user-mode pOutputBuffer overriding operation status [%!STATUS!] with status [%!STATUS!]", status, writeStatus); @@ -863,9 +897,21 @@ P4vfsControlPortMessage( goto CLEANUP; } - *pReturnOutputBufferLength = sizeof(output); + *pReturnOutputBufferLength = dwOutputReplyLength; CLEANUP: + if (pInputMsg) + { + ExFreePoolWithTag(pInputMsg, P4VFS_CONTROL_MSG_ALLOC_TAG); + pInputMsg = NULL; + } + + if (pOutputReply) + { + ExFreePoolWithTag(pOutputReply, P4VFS_CONTROL_REPLY_ALLOC_TAG); + pOutputReply = NULL; + } + return status; } From 17279d925d990e48a895ac9c81402c2083434e15 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 25 Jul 2026 23:28:08 -0700 Subject: [PATCH 15/23] adding range guards to P4vfsToUnicodeString --- source/P4VFS.Core/Tests/TestDriver.cpp | 28 ++++++++++++---------- source/P4VFS.Driver/Include/DriverCore.h | 4 +++- source/P4VFS.Driver/Source/DriverCore.c | 29 ++++++++++++++++++++--- source/P4VFS.Driver/Source/DriverFilter.c | 12 ++++++---- 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index 6619b3a..99a9d4f 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -243,19 +243,21 @@ typedef enum _POOL_TYPE { #define max(a,b) (((a) > (b)) ? (a) : (b)) #define RtlUShortAdd UShortAdd -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) -#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) -#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) -#define STATUS_PORT_DISCONNECTED ((NTSTATUS)0xC0000037L) -#define STATUS_DATA_ERROR ((NTSTATUS)0xC000003EL) -#define STATUS_INVALID_PORT_HANDLE ((NTSTATUS)0xC0000042L) -#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL) -#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L) -#define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS)0x00000106L) -#define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS)0xC0000206L) -#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) +#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) +#define STATUS_PORT_DISCONNECTED ((NTSTATUS)0xC0000037L) +#define STATUS_DATA_ERROR ((NTSTATUS)0xC000003EL) +#define STATUS_INVALID_PORT_HANDLE ((NTSTATUS)0xC0000042L) +#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL) +#define STATUS_MEMORY_NOT_ALLOCATED ((NTSTATUS)0xC00000A0L) +#define STATUS_NOT_ALL_ASSIGNED ((NTSTATUS)0x00000106L) +#define STATUS_INVALID_BUFFER_SIZE ((NTSTATUS)0xC0000206L) +#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) +#define STATUS_INVALID_ADDRESS ((NTSTATUS)0xC0000141L) +#define STATUS_INVALID_OFFSET_ALIGNMENT ((NTSTATUS)0xC0000474L) #define IO_IGNORE_SHARE_ACCESS_CHECK 0x0800 #define FILE_SHARE_VALID_FLAGS 0x00000007 diff --git a/source/P4VFS.Driver/Include/DriverCore.h b/source/P4VFS.Driver/Include/DriverCore.h index 7a3f875..80bb5a4 100644 --- a/source/P4VFS.Driver/Include/DriverCore.h +++ b/source/P4VFS.Driver/Include/DriverCore.h @@ -113,7 +113,9 @@ P4vfsAllocateUnicodeString( NTSTATUS P4vfsToUnicodeString( _In_ CONST P4VFS_UNICODE_STRING* pSrcString, - _Out_ UNICODE_STRING* pDstString + _Out_ UNICODE_STRING* pDstString, + _In_ CONST VOID* pSrcBuffer, + _In_ CONST ULONG dwSrcBufferLength ); NTSTATUS diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index ec98a0d..afc9114 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -775,7 +775,9 @@ P4vfsAllocateUnicodeString( NTSTATUS P4vfsToUnicodeString( _In_ CONST P4VFS_UNICODE_STRING* pSrcString, - _Out_ UNICODE_STRING* pDstString + _Out_ UNICODE_STRING* pDstString, + _In_ CONST VOID* pSrcBuffer, + _In_ CONST ULONG dwSrcBufferLength ) { PAGED_CODE(); @@ -785,16 +787,37 @@ P4vfsToUnicodeString( return STATUS_INVALID_PARAMETER; } + // The P4VFS_UNICODE_STRING must have a valid string length if (pSrcString->sizeBytes >= UNICODE_STRING_MAX_BYTES) { return STATUS_BUFFER_OVERFLOW; } - if (pSrcString->sizeBytes % sizeof(WCHAR)) + // The P4VFS_UNICODE_STRING must have a WCHAR string length + if (pSrcString->sizeBytes % sizeof(WCHAR) || pSrcString->offsetBytes % sizeof(WCHAR)) { return STATUS_DATATYPE_MISALIGNMENT; } - + + // The P4VFS_UNICODE_STRING header must lie within the pSrcBuffer up to dwSrcBufferLength + CONST BYTE* pSrcBufferBegin = (CONST BYTE*)pSrcBuffer; + CONST BYTE* pSrcBufferEnd = pSrcBufferBegin + dwSrcBufferLength; + CONST BYTE* pSrcStr = (CONST BYTE*)pSrcString; + if (pSrcStr < pSrcBufferBegin || (pSrcStr + sizeof(P4VFS_UNICODE_STRING)) > pSrcBufferEnd) + { + return STATUS_INVALID_ADDRESS; + } + + // The P4VFS_UNICODE_STRING payload must lie within the buffer. Compute as + // offsets relative to the buffer base so that it's overflow-safe LONGLONG arithmetic + CONST LONGLONG headerOffset = (LONGLONG)(pSrcStr - pSrcBufferBegin); + CONST LONGLONG dataBegin = headerOffset + (LONGLONG)pSrcString->offsetBytes; + CONST LONGLONG dataEnd = dataBegin + (LONGLONG)pSrcString->sizeBytes; + if (dataBegin < 0 || dataEnd < dataBegin || dataEnd > (LONGLONG)dwSrcBufferLength) + { + return STATUS_INVALID_OFFSET_ALIGNMENT; + } + pDstString->Length = (USHORT)(pSrcString->sizeBytes >= sizeof(WCHAR) ? pSrcString->sizeBytes-sizeof(WCHAR) : 0); pDstString->MaximumLength = (USHORT)pSrcString->sizeBytes; pDstString->Buffer = (WCHAR*)P4VFS_UNICODE_STRING_CSTR(*pSrcString); diff --git a/source/P4VFS.Driver/Source/DriverFilter.c b/source/P4VFS.Driver/Source/DriverFilter.c index 5402e9c..4acda82 100644 --- a/source/P4VFS.Driver/Source/DriverFilter.c +++ b/source/P4VFS.Driver/Source/DriverFilter.c @@ -781,9 +781,9 @@ P4vfsControlPortMessage( dwOutputReplyLength = sizeof(P4VFS_CONTROL_REPLY); pOutputReply = (P4VFS_CONTROL_REPLY*)ExAllocatePoolZero( - NonPagedPoolNx, - dwOutputReplyLength, - P4VFS_CONTROL_REPLY_ALLOC_TAG); + NonPagedPoolNx, + dwOutputReplyLength, + P4VFS_CONTROL_REPLY_ALLOC_TAG); if (pOutputReply == NULL) { @@ -847,7 +847,11 @@ P4vfsControlPortMessage( } UNICODE_STRING unicodeFilePath = {0}; - status = P4vfsToUnicodeString(&pInputMsg->data.OPEN_REPARSE_POINT.filePath, &unicodeFilePath); + status = P4vfsToUnicodeString(&pInputMsg->data.OPEN_REPARSE_POINT.filePath, + &unicodeFilePath, + pInputMsg, + dwInputMsgLength); + if (!NT_SUCCESS(status)) { P4vfsTraceInfo(Filter, L"P4vfsControlPortMessage: P4VFS_OPERATION_OPEN_REPARSE_POINT P4vfsToUnicodeString failed [%!STATUS!]", status); From 8f85263cc3280e604e3cc540ae95def3502cd579 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 25 Jul 2026 23:43:51 -0700 Subject: [PATCH 16/23] updating solution external package versions --- source/P4VFS.Core/P4VFS.Core.vcxproj | 4 ++-- source/P4VFS.CoreInterop/P4VFS.CoreInterop.vcxproj | 4 ++-- source/P4VFS.props | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/P4VFS.Core/P4VFS.Core.vcxproj b/source/P4VFS.Core/P4VFS.Core.vcxproj index 1b6e362..1daaeef 100644 --- a/source/P4VFS.Core/P4VFS.Core.vcxproj +++ b/source/P4VFS.Core/P4VFS.Core.vcxproj @@ -149,7 +149,7 @@ DebugFull - advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles);%(AdditionalDependencies) + advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;iphlpapi.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles);%(AdditionalDependencies) Default $(PerforceApiLibDir);$(OpenSSLApiLibDir);%(AdditionalLibraryDirectories) @@ -177,7 +177,7 @@ DebugFull - advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles);%(AdditionalDependencies) + advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;iphlpapi.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles);%(AdditionalDependencies) UseLinkTimeCodeGeneration $(PerforceApiLibDir);$(OpenSSLApiLibDir);%(AdditionalLibraryDirectories) diff --git a/source/P4VFS.CoreInterop/P4VFS.CoreInterop.vcxproj b/source/P4VFS.CoreInterop/P4VFS.CoreInterop.vcxproj index 4c0a90a..4147484 100644 --- a/source/P4VFS.CoreInterop/P4VFS.CoreInterop.vcxproj +++ b/source/P4VFS.CoreInterop/P4VFS.CoreInterop.vcxproj @@ -113,7 +113,7 @@ DebugFull - $(P4VFSCoreName).lib;advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles) + $(P4VFSCoreName).lib;advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;iphlpapi.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles) Default $(P4VFSCoreBuildDir)\$(Configuration);$(PerforceApiLibDir);$(OpenSSLApiLibDir);%(AdditionalLibraryDirectories) @@ -141,7 +141,7 @@ DebugFull - $(P4VFSCoreName).lib;advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles) + $(P4VFSCoreName).lib;advapi32.lib;bcrypt.lib;ws2_32.lib;wtsapi32.lib;shlwapi.lib;shell32.lib;fltlib.lib;userenv.lib;crypt32.lib;iphlpapi.lib;$(PerforceApiLibFiles);$(OpenSSLApiLibFiles) Default $(P4VFSCoreBuildDir)\$(Configuration);$(PerforceApiLibDir);$(OpenSSLApiLibDir);%(AdditionalLibraryDirectories) diff --git a/source/P4VFS.props b/source/P4VFS.props index 791de78..9a0d430 100644 --- a/source/P4VFS.props +++ b/source/P4VFS.props @@ -65,15 +65,15 @@ $(P4VFSBuildDir)/$(P4VFSUnitTestName) - 2025.2 + 2026.1 $(P4VFSConfiguration) $(P4VFSExternalDir)/P4API/$(PerforceApiVersion) $(PerforceApiDir)/include $(PerforceApiDir)/lib/x64.vs$(P4VFSVisualStudioEdition).dyn.$(PerforceApiConfiguration) - libclient.lib;libp4api.lib;libp4script.lib;libp4script_c.lib;libp4script_curl.lib;libp4script_sqlite.lib;librpc.lib;libsupp.lib + libclient.lib;libp4api.lib;libp4script.lib;libp4script_c.lib;libp4script_cstub.lib;libp4script_curl.lib;libp4script_sqlite.lib;librpc.lib;libsupp.lib - 3.0.18 + 3.0.21 $(P4VFSConfiguration) $(P4VFSExternalDir)/OpenSSL/$(OpenSSLApiVersion) $(OpenSSLApiDir)/include From 6ad282eaac7ac49ab0e3c3c20f9f9a9fd46e5b5a Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sun, 26 Jul 2026 10:51:31 -0700 Subject: [PATCH 17/23] fixing unit test server creation --- source/P4VFS.Console/P4VFS.Notes.txt | 1 + source/P4VFS.UnitTest/Source/UnitTestServer.cs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index ce9d39e..ac80428 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -18,6 +18,7 @@ Version [1.30.0.0] * Additional driver unit tests for internal P4VFS_FLT_FILE_HANDLE translation * Addition of TestPush.bat script to optionally use in a post-build deployment to a virtual machine for full development driver and user-mode testing. +* Updating unit test server support for p4d 2026.1 with default configurable security=4 Version [1.29.4.0] * Updating to latest P4API 25.2 diff --git a/source/P4VFS.UnitTest/Source/UnitTestServer.cs b/source/P4VFS.UnitTest/Source/UnitTestServer.cs index ad28ede..066cd34 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestServer.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestServer.cs @@ -53,6 +53,10 @@ public void StartupLocalPerforceServerTest() Assert(!serverProcess.HasExited, "Server process quit prematuraly"); AssertRetry(() => ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} info", _P4Port)) == 0); + // Set the password for the default generated admin for this new database. This user will be deleted afterwards + Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} passwd", _P4Port), echo:true, stdin:String.Format("{0}\n{0}\n", DefaultP4Passwd)) == 0); + Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} login", _P4Port), echo:true, stdin:String.Format("{0}\n", DefaultP4Passwd)) == 0); + string[] serverConfigVariables = new[] { "auth.sso.allow.passwd=1", "db.peeking=3", @@ -90,10 +94,8 @@ public void StartupLocalPerforceServerTest() Assert(defaultUsers.Length == 1); string defaultUser = defaultUsers[0]; - // Set a password for the admin, then restart the server for the SSO login options to take effect, then change the admin password as required - Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} -u {1} passwd -P {2}{2}", _P4Port, defaultUser, DefaultP4Passwd), echo:true) == 0); + // Restart the server for the SSO login options to take effect, then change the admin password as required Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} admin restart", _P4Port), echo:true) == 0); - Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} -u {1} passwd", _P4Port, defaultUser), echo:true, stdin:String.Format("{0}{0}\n{0}\n{0}\n", DefaultP4Passwd)) == 0); Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("-p {0} -u {1} set P4PASSWD=", _P4Port, defaultUser), echo:true) == 0); // Perform initial administrator operations using the default generated admin user (typically current session user name). From a905ba5f3e96ab3530b1dd8802489e36c8913bde Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Mon, 27 Jul 2026 19:06:03 -0700 Subject: [PATCH 18/23] unicode string unit testing fixes --- source/P4VFS.Console/P4VFS.Notes.txt | 3 +- source/P4VFS.Core/Tests/TestDriver.cpp | 48 ++++++++++++++++++- source/P4VFS.Driver/Scripts/TestPush.bat | 6 ++- source/P4VFS.Driver/Source/DriverCore.c | 2 +- .../P4VFS.UnitTest/Source/UnitTestCommon.cs | 7 ++- .../P4VFS.UnitTest/Source/UnitTestServer.cs | 7 ++- 6 files changed, 64 insertions(+), 9 deletions(-) diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index ac80428..6987402 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -18,7 +18,8 @@ Version [1.30.0.0] * Additional driver unit tests for internal P4VFS_FLT_FILE_HANDLE translation * Addition of TestPush.bat script to optionally use in a post-build deployment to a virtual machine for full development driver and user-mode testing. -* Updating unit test server support for p4d 2026.1 with default configurable security=4 +* Updating unit test server to support p4d 2026.1 with default configurable security=4 and + enable database journal to avoid bug with p4d crash when deleting auth extension Version [1.29.4.0] * Updating to latest P4API 25.2 diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index 99a9d4f..16ab9e6 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -487,15 +487,17 @@ static UNICODE_STRING CStrToUnicodeString(const WCHAR* text) void TestDriverUnicodeString(const TestContext& context) { - const UCHAR sentinalByte = 0xCD; const WCHAR* shortFilePath = L"C:\\memory.dmp"; const WCHAR* typicalFilePath = L"C:\\depot\\tools\\dev\\source\\Hammer\\Hammer.Interfaces\\BaseClasses\\Launcher\\ApplicationDescription.cs"; const WCHAR* veryLongFilePath = L"C:\\depot\\tools\\dev\\external\\packages\\thirdparty\\microsoft\\Windows Kits\\10\\References\\10.0.17134.0\\Windows.ApplicationModel.Activation.WebUISearchActivatedEventsContract\\1.0.0.0\\zh-hans\\2019\\Enterprise\\VSSDK\\VisualStudioIntegration\\Common\\Source\\CPP\\VSL\\VSLArcitecture_files\\Microsoft Azure Tools\\Visual Studio 16.0\\2.9\\RemoteDebuggerConnector\\Connector\\MSBuild\\Microsoft\\Microsoft.NET.Build.Extensions\\net471\\Windows.ApplicationModel.Activation.WebUISearchActivatedEventsContract\\References\\Windows.ApplicationModel.CommunicationBlocking.CommunicationBlockingContract\\2.0.0.0\\Windows.ApplicationModel.CommunicationBlocking.CommunicationBlockingContract.WinMD\\Windows.ApplicationModel.Background.BackgroundAlarmApplicationContract.xml"; - auto AssertCopyAssignUnicodeString = [&](const WCHAR* srcText, const ULONG dstPadding = 0, const LONG srcLength = -1) -> void + auto AssertCopyAssignUnicodeString = [&context](const WCHAR* srcText, const ULONG dstPadding = 0, const LONG srcLength = -1) -> void { InternalTestDriverReset(context); + + const UCHAR sentinalByte = 0xCD; const LONG srcTextLength = srcLength < 0 ? LONG(StringInfo::Strlen(srcText)) : srcLength; + const ULONG largeTextOffset = max(srcTextLength * 8, 1024); const ULONG srcTextSizeBytes = ULONG(srcTextLength*sizeof(WCHAR)); const ULONG dstTextSizeBytes = srcTextSizeBytes+sizeof(WCHAR); Array dstBuffer(sizeof(P4VFS_UNICODE_STRING)+dstPadding+dstTextSizeBytes+sizeof(sentinalByte), 0); @@ -509,6 +511,48 @@ void TestDriverUnicodeString(const TestContext& context) Assert(dstString->c_str()[srcTextLength] == L'\0'); Assert(dstString->sizeBytes == dstTextSizeBytes); Assert(dstBuffer[dstBuffer.size()-1] == sentinalByte); + const P4VFS_UNICODE_STRING dstValidString = *dstString; + + auto AssertToUnicodeString = [srcText, srcTextLength](const P4VFS_UNICODE_STRING* dataString, const void* dataBuffer, size_t dataBufferLength, NTSTATUS expectStatus) -> void + { + UNICODE_STRING kmString = {0}; + Assert(P4vfsToUnicodeString(dataString, &kmString, dataBuffer, (ULONG)dataBufferLength) == expectStatus); + if (expectStatus == STATUS_SUCCESS) + { + Assert(kmString.Length == srcTextLength*sizeof(WCHAR)); + Assert(kmString.Buffer && memcmp(kmString.Buffer, srcText, srcTextLength*sizeof(WCHAR)) == 0); + } + }; + + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_SUCCESS); + AssertToUnicodeString(dstString, dstBuffer.data()-largeTextOffset, dstBuffer.size(), STATUS_INVALID_ADDRESS); + AssertToUnicodeString(dstString, dstBuffer.data()-largeTextOffset, dstBuffer.size()+largeTextOffset, STATUS_SUCCESS); + AssertToUnicodeString(dstString, dstBuffer.data()+largeTextOffset, dstBuffer.size(), STATUS_INVALID_ADDRESS); + + P4VFS_UNICODE_STRING dstStackString = *dstString; + AssertToUnicodeString(&dstStackString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_ADDRESS); + + dstString->sizeBytes = largeTextOffset; + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); + *dstString = dstValidString; + + dstString->sizeBytes += 1; + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_DATATYPE_MISALIGNMENT); + *dstString = dstValidString; + + dstString->offsetBytes = largeTextOffset; + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); + *dstString = dstValidString; + + dstString->offsetBytes = largeTextOffset; + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); + *dstString = dstValidString; + + dstString->offsetBytes = LONG_MAX; + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); + *dstString = dstValidString; + + AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_SUCCESS); }; AssertCopyAssignUnicodeString(typicalFilePath); diff --git a/source/P4VFS.Driver/Scripts/TestPush.bat b/source/P4VFS.Driver/Scripts/TestPush.bat index 658c25d..ac86251 100644 --- a/source/P4VFS.Driver/Scripts/TestPush.bat +++ b/source/P4VFS.Driver/Scripts/TestPush.bat @@ -11,10 +11,12 @@ IF NOT "%1" == "" ( ) SET DEPLOY_FOLDER=\\%DEPLOY_COMPUTERNAME%\C$\P4VFS -SET ROBOCOPY_COMMON_OPTIONS=/XD .* *.tlog lib include /MT /S /NJH /NJS /NP +SET ROBOCOPY_COMMON_OPTIONS=/XD .* *.tlog lib include /MT /E /NJH /NJS /NP robocopy.exe %REPO_FOLDER%\source %DEPLOY_FOLDER%\source %ROBOCOPY_COMMON_OPTIONS% robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Setup %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Setup %ROBOCOPY_COMMON_OPTIONS% robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Driver %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Driver %ROBOCOPY_COMMON_OPTIONS% robocopy.exe %REPO_FOLDER%\intermediate\builds\P4VFS.Console %DEPLOY_FOLDER%\intermediate\builds\P4VFS.Console %ROBOCOPY_COMMON_OPTIONS% -robocopy.exe %REPO_FOLDER%\external\P4API %DEPLOY_FOLDER%\external\P4API %ROBOCOPY_COMMON_OPTIONS% +robocopy.exe %REPO_FOLDER%\external\P4API %DEPLOY_FOLDER%\external\P4API %ROBOCOPY_COMMON_OPTIONS% /PURGE + +EXIT /B 0 diff --git a/source/P4VFS.Driver/Source/DriverCore.c b/source/P4VFS.Driver/Source/DriverCore.c index afc9114..c6089fa 100644 --- a/source/P4VFS.Driver/Source/DriverCore.c +++ b/source/P4VFS.Driver/Source/DriverCore.c @@ -794,7 +794,7 @@ P4vfsToUnicodeString( } // The P4VFS_UNICODE_STRING must have a WCHAR string length - if (pSrcString->sizeBytes % sizeof(WCHAR) || pSrcString->offsetBytes % sizeof(WCHAR)) + if (pSrcString->sizeBytes % sizeof(WCHAR)) { return STATUS_DATATYPE_MISALIGNMENT; } diff --git a/source/P4VFS.UnitTest/Source/UnitTestCommon.cs b/source/P4VFS.UnitTest/Source/UnitTestCommon.cs index 381acbf..06569e0 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestCommon.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestCommon.cs @@ -1611,6 +1611,7 @@ public void ConnectInvalidConfigTest() WorkspaceReset(); using (DepotClient depotClient = new DepotClient()) { + depotClient.Unattended = true; DepotConfig config = new DepotConfig{ Port=_P4Port, Client="__invalid_p4vfs_client__", User=_P4User }; Assert(depotClient.Connect(config)); DepotResultInfo.Node info = depotClient.Info(); @@ -1623,6 +1624,7 @@ public void ConnectInvalidConfigTest() } using (DepotClient depotClient = new DepotClient()) { + depotClient.Unattended = true; DepotConfig config = new DepotConfig{ Port=_P4Port, Client="__invalid_p4vfs_client__", User="__invalid_user__" }; Assert(depotClient.Connect(config)); DepotResultInfo.Node info = depotClient.Info(); @@ -1635,6 +1637,7 @@ public void ConnectInvalidConfigTest() } using (DepotClient depotClient = new DepotClient()) { + depotClient.Unattended = true; DepotConfig config = new DepotConfig{ Port=_P4Port, Client="__invalid_p4vfs_client__" }; Assert(depotClient.Connect(config)); DepotResultInfo.Node info = depotClient.Info(); @@ -1647,11 +1650,13 @@ public void ConnectInvalidConfigTest() } using (DepotClient depotClient = new DepotClient()) { + depotClient.Unattended = true; DepotConfig config = new DepotConfig{ Port=_P4Port, User="__invalid_user__" }; Assert(depotClient.Connect(config) == false); } using (DepotClient depotClient = new DepotClient()) { + depotClient.Unattended = true; DepotConfig config = new DepotConfig{ Port=_P4Port, Client=_P4Client }; Assert(depotClient.Connect(config)); DepotResultInfo.Node info = depotClient.Info(); @@ -1664,6 +1669,7 @@ public void ConnectInvalidConfigTest() } using (DepotClient depotClient = new DepotClient()) { + depotClient.Unattended = true; DepotConfig config = new DepotConfig{ Port=_P4Port, User=_P4User }; Assert(depotClient.Connect(config)); DepotResultInfo.Node info = depotClient.Info(); @@ -2137,7 +2143,6 @@ public void ShellLoginTimeoutTest() HttpListener loginListener = new HttpListener(); loginListener.Prefixes.Add(loginEndpoint); loginListener.Start(); - Thread loginListenerThread = new Thread(new ThreadStart(() => { diff --git a/source/P4VFS.UnitTest/Source/UnitTestServer.cs b/source/P4VFS.UnitTest/Source/UnitTestServer.cs index 066cd34..c8eef38 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestServer.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestServer.cs @@ -37,8 +37,9 @@ public void StartupLocalPerforceServerTest() string serverDatabaseFolder = String.Format("{0}\\db", serverRootFolder); FileUtilities.CreateDirectory(serverDatabaseFolder); string serverLogFile = String.Format("{0}\\p4d.log", serverRootFolder); + string serverJnlFile = String.Format("{0}\\journal", serverDatabaseFolder); string serverDescription = GetServerDescription(_P4Port); - string serverArgs = String.Format("-L \"{0}\" -r \"{1}\" -p {2} -Id {3} -J off", serverLogFile, serverDatabaseFolder, serverPortNumber, serverDescription); + string serverArgs = String.Format("-L \"{0}\" -r \"{1}\" -p {2} -Id {3} -J {4}", serverLogFile, serverDatabaseFolder, serverPortNumber, serverDescription, serverJnlFile); ProcessStartInfo serverStartInfo = new ProcessStartInfo{ FileName = serverP4dExe, @@ -66,6 +67,7 @@ public void StartupLocalPerforceServerTest() "monitor=10", "net.parallel.submit.threads=8", "net.parallel.max=8", + "security=4", "server=2", "submit.unlocklocked=1", }; @@ -623,6 +625,7 @@ public static void CreateDuplicatePerforceServer(string targetPort) string targetRootFolder = GetServerRootFolder(targetPort); string targetDatabaseFolder = String.Format("{0}\\db", targetRootFolder); string targetServerLogFile = String.Format("{0}\\p4d.log", targetRootFolder); + string targetServerJnlFile = String.Format("{0}\\journal", targetDatabaseFolder); string targetServerDescription = GetServerDescription(targetPort); Action copyFileToTarget = (string sourceFilePath) => @@ -649,7 +652,7 @@ public static void CreateDuplicatePerforceServer(string targetPort) string targetP4dExe = String.Format("{0}\\{1}", targetRootFolder, Path.GetFileName(GetServerP4dExe())); Assert(File.Exists(targetP4dExe)); - string targetServerArgs = String.Format("-L \"{0}\" -r \"{1}\" -p {2} -Id {3} -J off", targetServerLogFile, targetDatabaseFolder, targetServerPort, targetServerDescription); + string targetServerArgs = String.Format("-L \"{0}\" -r \"{1}\" -p {2} -Id {3} -J {4}", targetServerLogFile, targetDatabaseFolder, targetServerPort, targetServerDescription, targetServerJnlFile); Assert(ProcessInfo.ExecuteWait(targetP4dExe, String.Format("{0} -jr {1}", targetServerArgs, checkpointFile), echo: true) == 0); Dictionary targetEnvironment = new Dictionary(); From f5614f2a5e82043d4d2eeac4b5af2269c488080b Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Tue, 28 Jul 2026 22:35:17 -0700 Subject: [PATCH 19/23] additional exclude processes --- deploy/pipelines/templates/p4vfs-ci.yml | 8 ++++---- external/OpenSSL/OpenSSL.Module.cs | 2 +- source/P4VFS.Console/P4VFS.Notes.txt | 2 ++ source/P4VFS.Core/Include/SettingManager.h | 2 +- source/P4VFS.Driver/Include/DriverVersion.h | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/deploy/pipelines/templates/p4vfs-ci.yml b/deploy/pipelines/templates/p4vfs-ci.yml index d6a713f..92655e6 100644 --- a/deploy/pipelines/templates/p4vfs-ci.yml +++ b/deploy/pipelines/templates/p4vfs-ci.yml @@ -38,14 +38,14 @@ jobs: inputs: restoreSolution: '${{parameters.solutionFile}}' - - task: VSBuild@1 + - task: MSBuild@1 displayName: 'Build' timeoutInMinutes: 180 inputs: - vsVersion: '17.0' solution: '${{parameters.solutionFile}}' - platform: '${{parameters.platform}}' - configuration: '${{parameters.configuration}}' + msbuildVersion: '17.0' + msbuildArchitecture: 'x64' + configuration: ${{parameters.configuration}} msbuildArgs: '${{parameters.additionalBuildArguments}}' - task: PublishSymbols@2 diff --git a/external/OpenSSL/OpenSSL.Module.cs b/external/OpenSSL/OpenSSL.Module.cs index b0e5dcf..8de8689 100644 --- a/external/OpenSSL/OpenSSL.Module.cs +++ b/external/OpenSSL/OpenSSL.Module.cs @@ -75,7 +75,7 @@ private void BuildOpensslLibrary(string opensslArchiveFolder, string opensslTarg $"@ECHO ON", $"CALL \"{vcvarsScriptPath}\"", $"CD /D \"{opensslArchiveFolder}\"", - $"\"{perlExe}\" Configure VC-WIN64A no-asm no-tests \"--prefix={opensslConfigurationFolder}\" \"--openssldir={opensslConfigurationFolder}-ssl\" --{configuration}", + $"\"{perlExe}\" Configure VC-WIN64A no-asm no-tests no-makedepend \"--prefix={opensslConfigurationFolder}\" \"--openssldir={opensslConfigurationFolder}-ssl\" --{configuration}", $"IF %ERRORLEVEL% NEQ 0 EXIT /B 1", $"nmake clean", $"IF %ERRORLEVEL% NEQ 0 EXIT /B 1", diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 6987402..9254b29 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -20,6 +20,8 @@ Version [1.30.0.0] virtual machine for full development driver and user-mode testing. * Updating unit test server to support p4d 2026.1 with default configurable security=4 and enable database journal to avoid bug with p4d crash when deleting auth extension +* Addition to default ExcludedProcessNames of SenseNdr.exe, SenseDlpProcessor.exe +* OpenSSL configure now includes no-makedepend for a slight reduction in build time Version [1.29.4.0] * Updating to latest P4API 25.2 diff --git a/source/P4VFS.Core/Include/SettingManager.h b/source/P4VFS.Core/Include/SettingManager.h index fa2beee..ec6a252 100644 --- a/source/P4VFS.Core/Include/SettingManager.h +++ b/source/P4VFS.Core/Include/SettingManager.h @@ -25,7 +25,7 @@ namespace FileCore { _N( String, SyncResidentPattern, L"" ) \ _N( bool, Unattended, false ) \ _N( String, Verbosity, FileCore::LogChannel::ToString(FileCore::LogChannel::Info).c_str() ) \ - _N( String, ExcludedProcessNames, L"MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe;MpDlpService.exe" ) \ + _N( String, ExcludedProcessNames, L"MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe;MpDlpService.exe;SenseNdr.exe;SenseDlpProcessor.exe" ) \ _N( int32_t, CreateFileRetryCount, 8 ) \ _N( int32_t, CreateFileRetryWaitMs, 250 ) \ _N( int32_t, PoolDefaultNumberOfThreads, 8 ) \ diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index a351925..b19a79c 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 12 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 14 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) From edb53ea0c336c91d1306f158b6fe5cab92c46ea7 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Thu, 30 Jul 2026 16:25:01 -0700 Subject: [PATCH 20/23] fixing info output for resolve after sync --- source/P4VFS.Console/P4VFS.Notes.txt | 1 + source/P4VFS.Core/Source/DepotSyncAction.cpp | 3 ++- source/P4VFS.Driver/Include/DriverVersion.h | 2 +- source/P4VFS.UnitTest/Source/UnitTestBase.cs | 15 ++++++++++----- source/P4VFS.UnitTest/Source/UnitTestCommon.cs | 2 +- source/P4VFS.UnitTest/Source/UnitTestServer.cs | 2 ++ 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 9254b29..0bf459a 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -22,6 +22,7 @@ Version [1.30.0.0] enable database journal to avoid bug with p4d crash when deleting auth extension * Addition to default ExcludedProcessNames of SenseNdr.exe, SenseDlpProcessor.exe * OpenSSL configure now includes no-makedepend for a slight reduction in build time +* Fixing info output log when syncing to files that must resolve before submitting Version [1.29.4.0] * Updating to latest P4API 25.2 diff --git a/source/P4VFS.Core/Source/DepotSyncAction.cpp b/source/P4VFS.Core/Source/DepotSyncAction.cpp index cbb6e5f..8581bce 100644 --- a/source/P4VFS.Core/Source/DepotSyncAction.cpp +++ b/source/P4VFS.Core/Source/DepotSyncAction.cpp @@ -220,7 +220,8 @@ DepotSyncActionInfo FDepotSyncActionInfo::FromInfoOutput(const DepotString& info info->m_ClientFile = match[1]; } } - else if (std::regex_search(infoText.c_str(), match, rx.m_ActionNeedsResolve)) + + if (std::regex_search(infoText.c_str(), match, rx.m_ActionNeedsResolve)) { info->m_DepotFile = match[2]; info->m_Revision = FDepotRevision::FromString(match[4]); diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index b19a79c..7f91e2f 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 14 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 15 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v) diff --git a/source/P4VFS.UnitTest/Source/UnitTestBase.cs b/source/P4VFS.UnitTest/Source/UnitTestBase.cs index 8f7cb35..7a200aa 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestBase.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestBase.cs @@ -254,11 +254,7 @@ public void WorkspaceReset(DepotConfig config = null) Assert(File.Exists(settingsPath) == false, String.Format("User settings file not allowed for unit tests: {0}", settingsPath)); } - foreach (string name in new[]{DepotConstants.P4PORT, DepotConstants.P4USER, DepotConstants.P4CLIENT, DepotConstants.P4CONFIG, DepotConstants.P4TRUST, DepotConstants.P4TICKETS}) - { - Environment.SetEnvironmentVariable(name, ""); - Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("set {0}=", name)) == 0); - } + WorkspaceEnvironmentReset(); AssertRetry(() => VirtualFileSystem.IsDriverLoaded(), message:"IsDriverLoaded"); AssertRetry(() => VirtualFileSystem.IsDriverReady(), message:"IsDriverReady"); @@ -334,6 +330,15 @@ public void WorkspaceReset(DepotConfig config = null) Assert(service.GetServiceSetting(nameof(SettingManager.ExcludedProcessNames)).ToString() == SettingManager.Default.ExcludedProcessNames); } + public static void WorkspaceEnvironmentReset() + { + foreach (string name in new[]{DepotConstants.P4PORT, DepotConstants.P4USER, DepotConstants.P4CLIENT, DepotConstants.P4CONFIG, DepotConstants.P4TRUST, DepotConstants.P4TICKETS}) + { + Environment.SetEnvironmentVariable(name, ""); + Assert(ProcessInfo.ExecuteWait(P4Exe, String.Format("set {0}=", name)) == 0); + } + } + public void ServiceRestart() { VirtualFileSystemLog.Info("Stopping service {0} ...", VirtualFileSystem.ServiceTitle); diff --git a/source/P4VFS.UnitTest/Source/UnitTestCommon.cs b/source/P4VFS.UnitTest/Source/UnitTestCommon.cs index 06569e0..5a8c7a0 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestCommon.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestCommon.cs @@ -1042,7 +1042,7 @@ public void DepotServerConfigTest() // Simple test of expected values of SettingManager.Default Assert(SettingManager.ExcludedProcessNames == SettingManager.Default.ExcludedProcessNames); - Assert(SettingManager.ExcludedProcessNames == "MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe;MpDlpService.exe"); + Assert(SettingManager.ExcludedProcessNames == "MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe;MpDlpService.exe;SenseNdr.exe;SenseDlpProcessor.exe"); Assert(SettingManager.PopulateMethod == SettingManager.Default.PopulateMethod); Assert(SettingManager.PopulateMethod == "Stream"); diff --git a/source/P4VFS.UnitTest/Source/UnitTestServer.cs b/source/P4VFS.UnitTest/Source/UnitTestServer.cs index c8eef38..900c48a 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestServer.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestServer.cs @@ -833,6 +833,8 @@ private static void ServerWorkspaceReset(string p4Port = null) AssertRetry(() => { try { FileUtilities.DeleteDirectoryAndFiles(serverRootFolder); return true; } catch {} return false; }); AssertRetry(() => Directory.Exists(serverRootFolder) == false, String.Format("directory exists {0}", serverRootFolder)); + + WorkspaceEnvironmentReset(); } public static string GetServerPortIPAddress(string p4Port = null) From a2c01ad8d125e71b2e9c177419a53349ae9534f2 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Thu, 30 Jul 2026 20:48:40 -0700 Subject: [PATCH 21/23] addition of codesign token creation command --- source/P4VFS.CodeSign/Source/CodeSign.cs | 8 ++ .../P4VFS.CodeSign/Source/DevCenterClient.cs | 12 +++ source/P4VFS.CodeSign/Source/EsrpClient.cs | 5 ++ source/P4VFS.CodeSign/Source/Program.cs | 84 ++++++++++++++++++- source/P4VFS.Core/Tests/TestDriver.cpp | 14 +++- 5 files changed, 121 insertions(+), 2 deletions(-) diff --git a/source/P4VFS.CodeSign/Source/CodeSign.cs b/source/P4VFS.CodeSign/Source/CodeSign.cs index e8c76ce..9078599 100644 --- a/source/P4VFS.CodeSign/Source/CodeSign.cs +++ b/source/P4VFS.CodeSign/Source/CodeSign.cs @@ -15,8 +15,15 @@ namespace Microsoft.P4VFS.CodeSign public interface ICodeSignClient : IDisposable { bool Submit(CodeSignJob job); + CodeSignToken[] CreateTokens(bool interactive); } + public class CodeSignToken + { + public string Name { get; set; } + public string Value { get; set; } + }; + public static class CodeSignInterop { [DllImport("cabapi.dll", EntryPoint = "Cab_Extract", CallingConvention = CallingConvention.Cdecl, SetLastError = true, CharSet = CharSet.Auto)] @@ -32,6 +39,7 @@ public static class CodeSignResources public const string HardwareLabPlaylistXml = "HardwareLabPlaylist.xml"; public const string SignAuthJson = "SignAuth.json"; public const string SignInputSetupJson = "SignInputSetup.json"; + public const string SignInputAttestation = "SignInputAttestation.json"; public const string SignPolicyJson = "SignPolicy.json"; }; diff --git a/source/P4VFS.CodeSign/Source/DevCenterClient.cs b/source/P4VFS.CodeSign/Source/DevCenterClient.cs index 85a3593..8392417 100644 --- a/source/P4VFS.CodeSign/Source/DevCenterClient.cs +++ b/source/P4VFS.CodeSign/Source/DevCenterClient.cs @@ -109,6 +109,18 @@ public bool Submit(CodeSignJob job) return true; } + public CodeSignToken[] CreateTokens(bool interactive) + { + JToken hdcSignInput = JObject.Parse(CodeSignUtilities.ExtractResourceToString(CodeSignResources.SignInputAttestation)); + JToken hdcTokenManifest = hdcSignInput.SelectTokens("$.SignBatches[*].SignRequestFiles[*].Manifest").FirstOrDefault(); + CodeSignJob job = null; + + InitializeJob(job, hdcTokenManifest); + CodeSignToken accessToken = new CodeSignToken{ Name = "AccessToken", Value = GetCachedAccessTokenAsync().Result }; + + return new CodeSignToken[]{ accessToken }; + } + public static bool PreprocessSignRequestFile(CodeSignJob job, JToken tokenSignRequestFile, string signFilePath) { JToken tokenManifest = tokenSignRequestFile.SelectToken("$.Manifest"); diff --git a/source/P4VFS.CodeSign/Source/EsrpClient.cs b/source/P4VFS.CodeSign/Source/EsrpClient.cs index 30cf7e0..a910bd9 100644 --- a/source/P4VFS.CodeSign/Source/EsrpClient.cs +++ b/source/P4VFS.CodeSign/Source/EsrpClient.cs @@ -95,6 +95,11 @@ public bool Submit(CodeSignJob job) return esrpClientExitCode == 0; } + public CodeSignToken[] CreateTokens(bool interactive) + { + return null; + } + private string CreateJobSignAuthFile(CodeSignJob job) { return CodeSignUtilities.ExtractResourceToFile(job.TargetFolder, CodeSignResources.SignAuthJson); diff --git a/source/P4VFS.CodeSign/Source/Program.cs b/source/P4VFS.CodeSign/Source/Program.cs index 59ba360..da4cd9b 100644 --- a/source/P4VFS.CodeSign/Source/Program.cs +++ b/source/P4VFS.CodeSign/Source/Program.cs @@ -6,6 +6,7 @@ using System.IO; using System.Security; using System.Reflection; +using System.Text.RegularExpressions; using Microsoft.P4VFS.Extensions; using Microsoft.P4VFS.Extensions.Linq; using Microsoft.P4VFS.CoreInterop; @@ -46,7 +47,19 @@ wait indefinatly until signed binaries arrive. -i Name of input json file resource for signing -c Optional path to the folder containing EsrpClient.exe tool -n Optional path to the folder containing nuget.exe tool if EsrpClient - folder is not specified. This may be an interactive login. + folder is not specified. This may be an interactive login + + + tokens Request and display short-lived access tokens for debugging + service REST APIs + + P4VFS.CodeSign.exe tokens -q -a -i [-s ] + + -q Query available codesign services + -a Request and display tokens for all services + -i Use public interative authentication for codesign service + instead of default SignAuth service principal + -s Request and disply tokens for service by name "; public static int Main(string[] args) @@ -115,6 +128,9 @@ public static int Main(string[] args) case "submit": status = CommandSubmit(cmdArgs); break; + case "tokens": + status = CommandTokens(cmdArgs); + break; default: VirtualFileSystemLog.Error("P4VFS.CodeSign Unknown Command {0}", args[argIndex]); status = false; @@ -197,13 +213,79 @@ private static bool CommandSubmit(string[] args) return true; } + private static bool CommandTokens(string[] args) + { + List serviceNames = new List(); + bool query = false; + bool interactive = false; + + int argIndex = 0; + for (; argIndex < args.Length; ++argIndex) + { + if (String.Compare(args[argIndex], "-s") == 0 && argIndex+1 < args.Length) + serviceNames.Add(args[++argIndex]); + else if (String.Compare(args[argIndex], "-a") == 0) + serviceNames.Add(null); + else if (String.Compare(args[argIndex], "-i") == 0) + interactive = true; + else if (String.Compare(args[argIndex], "-q") == 0) + query = true; + else + break; + } + + if (argIndex < args.Length) + { + VirtualFileSystemLog.Error("Unexpected argument: {0}", args[argIndex]); + return false; + } + + Type[] clientTypes = Assembly.GetExecutingAssembly() + .GetTypes() + .Where(type => type.IsAbstract == false && type.IsClass && typeof(ICodeSignClient).IsAssignableFrom(type)) + .OrderBy(type => type.Name) + .ToArray(); + + Func getServiceName = (Type clientType) => + Regex.Replace(clientType.Name, @"Client$", "", RegexOptions.IgnoreCase); + + if (query) + { + VirtualFileSystemLog.Info("Available codesign services: {0}", String.Join(", ", clientTypes.Select(t => getServiceName(t)))); + return true; + } + + foreach (Type clientType in clientTypes) + { + string serviceName = getServiceName(clientType); + if (serviceNames.Any(name => name == null || String.Equals(name, serviceName, StringComparison.InvariantCultureIgnoreCase))) + { + foreach (CodeSignToken token in CreateCodeSignTokens(clientType, interactive)) + { + VirtualFileSystemLog.Info($"{serviceName}.{token.Name} = {token.Value}\n"); + } + } + } + return true; + } + private static bool SubmitCodeSignJob(CodeSignJob job) where ClientType : ICodeSignClient, new() { + VirtualFileSystemLog.Info("Submitting codesign job for service {0}", typeof(ClientType).Name); using (ICodeSignClient client = new ClientType()) { return client.Submit(job); } } + + private static CodeSignToken[] CreateCodeSignTokens(Type clientType, bool interactive) + { + VirtualFileSystemLog.Info("Creating codesign tokens for service {0}", clientType.Name); + using (ICodeSignClient client = Activator.CreateInstance(clientType) as ICodeSignClient) + { + return client.CreateTokens(interactive) ?? Array.Empty(); + } + } } } diff --git a/source/P4VFS.Core/Tests/TestDriver.cpp b/source/P4VFS.Core/Tests/TestDriver.cpp index 16ab9e6..bc6a048 100644 --- a/source/P4VFS.Core/Tests/TestDriver.cpp +++ b/source/P4VFS.Core/Tests/TestDriver.cpp @@ -500,12 +500,15 @@ void TestDriverUnicodeString(const TestContext& context) const ULONG largeTextOffset = max(srcTextLength * 8, 1024); const ULONG srcTextSizeBytes = ULONG(srcTextLength*sizeof(WCHAR)); const ULONG dstTextSizeBytes = srcTextSizeBytes+sizeof(WCHAR); + Array dstBuffer(sizeof(P4VFS_UNICODE_STRING)+dstPadding+dstTextSizeBytes+sizeof(sentinalByte), 0); dstBuffer[dstBuffer.size()-sizeof(sentinalByte)] = sentinalByte; P4VFS_UNICODE_STRING* dstString = (P4VFS_UNICODE_STRING*)dstBuffer.data(); + // Serialize the srcText into a variable length P4VFS_UNICODE_STRING starting dstPadding past the header Assert(P4vfsCopyAssignUnicodeString(dstString, dstBuffer.data()+sizeof(P4VFS_UNICODE_STRING)+dstPadding, dstTextSizeBytes, srcText, srcTextSizeBytes) == STATUS_SUCCESS); + // Verify the P4VFS_UNICODE_STRING reflects the srcText we expect Assert(StringInfo::Strncmp(dstString->c_str(), srcText, srcTextLength) == 0); Assert(StringInfo::Strlen(dstString->c_str()) == srcTextLength); Assert(dstString->c_str()[srcTextLength] == L'\0'); @@ -513,6 +516,7 @@ void TestDriverUnicodeString(const TestContext& context) Assert(dstBuffer[dstBuffer.size()-1] == sentinalByte); const P4VFS_UNICODE_STRING dstValidString = *dstString; + // Test reading a P4VFS_UNICODE_STRING from an arbitrary buffer for goodness or badness! auto AssertToUnicodeString = [srcText, srcTextLength](const P4VFS_UNICODE_STRING* dataString, const void* dataBuffer, size_t dataBufferLength, NTSTATUS expectStatus) -> void { UNICODE_STRING kmString = {0}; @@ -524,34 +528,42 @@ void TestDriverUnicodeString(const TestContext& context) } }; + // Test reading a P4VFS_UNICODE_STRING with good or bad buffer ranges AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_SUCCESS); AssertToUnicodeString(dstString, dstBuffer.data()-largeTextOffset, dstBuffer.size(), STATUS_INVALID_ADDRESS); AssertToUnicodeString(dstString, dstBuffer.data()-largeTextOffset, dstBuffer.size()+largeTextOffset, STATUS_SUCCESS); AssertToUnicodeString(dstString, dstBuffer.data()+largeTextOffset, dstBuffer.size(), STATUS_INVALID_ADDRESS); + // Testing reading when header buffer is outside data buffer P4VFS_UNICODE_STRING dstStackString = *dstString; AssertToUnicodeString(&dstStackString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_ADDRESS); + // Test when string size overflows the data buffer dstString->sizeBytes = largeTextOffset; AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); *dstString = dstValidString; + // Test when string data size is not 2 byte aligned, as expected for WCHAR string dstString->sizeBytes += 1; AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_DATATYPE_MISALIGNMENT); *dstString = dstValidString; + // Test when offset to string payload overflows the data buffer dstString->offsetBytes = largeTextOffset; AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); *dstString = dstValidString; - dstString->offsetBytes = largeTextOffset; + // Test when offset to string payload underflows the data buffer + dstString->offsetBytes = -1 * LONG(largeTextOffset); AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); *dstString = dstValidString; + // Test when offset to string payload overflows the data buffer by maximum possible, confirming pointer arithmatic safety dstString->offsetBytes = LONG_MAX; AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_INVALID_OFFSET_ALIGNMENT); *dstString = dstValidString; + // Test again to confirm our previous tests didn't fail for some other reason AssertToUnicodeString(dstString, dstBuffer.data(), dstBuffer.size(), STATUS_SUCCESS); }; From 278e6e6887bf41cc8b6396b2f1592cd38b8edfa2 Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Sat, 1 Aug 2026 11:13:29 -0700 Subject: [PATCH 22/23] update release notes --- source/P4VFS.Console/P4VFS.Notes.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 0bf459a..142f850 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -23,6 +23,8 @@ Version [1.30.0.0] * Addition to default ExcludedProcessNames of SenseNdr.exe, SenseDlpProcessor.exe * OpenSSL configure now includes no-makedepend for a slight reduction in build time * Fixing info output log when syncing to files that must resolve before submitting +* Addition of CodeSign 'tokens' command to request and display short-lived access tokens + for debugging service REST APIs. Version [1.29.4.0] * Updating to latest P4API 25.2 From d3e1edc47d989aa7031e3df455ff8d4322edb2bc Mon Sep 17 00:00:00 2001 From: Jess Kube Date: Mon, 3 Aug 2026 16:06:44 -0700 Subject: [PATCH 23/23] formatting for release --- source/P4VFS.Driver/Include/DriverTrace.h | 52 ++++++++++----------- source/P4VFS.Driver/Include/DriverVersion.h | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/source/P4VFS.Driver/Include/DriverTrace.h b/source/P4VFS.Driver/Include/DriverTrace.h index e3c33a1..18abb4f 100644 --- a/source/P4VFS.Driver/Include/DriverTrace.h +++ b/source/P4VFS.Driver/Include/DriverTrace.h @@ -5,50 +5,50 @@ #define P4VFS_WPP_CONTROL_GUID \ 082E7434-2FF0-4DE7-8470-1BBBD2E48237 -#define WPP_CONTROL_GUIDS \ - WPP_DEFINE_CONTROL_GUID( \ - P4VFS_DRIVER_TRACE_GUID, (082E7434, 2FF0, 4DE7, 8470, 1BBBD2E48237), \ - WPP_DEFINE_BIT(Init) \ - WPP_DEFINE_BIT(Shutdown) \ - WPP_DEFINE_BIT(Filter) \ - WPP_DEFINE_BIT(Core) \ - ) +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + P4VFS_DRIVER_TRACE_GUID, (082E7434, 2FF0, 4DE7, 8470, 1BBBD2E48237), \ + WPP_DEFINE_BIT(Init) \ + WPP_DEFINE_BIT(Shutdown) \ + WPP_DEFINE_BIT(Filter) \ + WPP_DEFINE_BIT(Core) \ + ) #define WPP_LEVEL_FLAGS_LOGGER(lvl, flags) \ - WPP_LEVEL_LOGGER(flags) + WPP_LEVEL_LOGGER(flags) #define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ - (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) #define WPP_LOGFILEIDPATH(x) \ - WPP_LOGPAIR(sizeof(USHORT), &(x)->Length) \ - WPP_LOGPAIR((x)->Length, (x)->Buffer) + WPP_LOGPAIR(sizeof(USHORT), &(x)->Length) \ + WPP_LOGPAIR((x)->Length, (x)->Buffer) #define WPP_LOGHEXDUMP(x) \ - WPP_LOGPAIR(sizeof(USHORT), &(x).Length) \ - WPP_LOGPAIR((x).Length, (x).Buffer) + WPP_LOGPAIR(sizeof(USHORT), &(x).Length) \ + WPP_LOGPAIR((x).Length, (x).Buffer) typedef struct _WPP_HEXDUMP { - USHORT Length; - __field_bcount(Length) CONST VOID* Buffer; + USHORT Length; + __field_bcount(Length) CONST VOID* Buffer; } WPP_HEXDUMP, *PWPP_HEXDUMP; __inline WPP_HEXDUMP P4vfsCreateWppHexDump( - __in ULONG length, - __in_bcount(length) CONST VOID* buffer - ) + __in ULONG length, + __in_bcount(length) CONST VOID* buffer + ) { - WPP_HEXDUMP WppHexDump; - if (length > USHORT_MAX) + WPP_HEXDUMP WppHexDump; + if (length > USHORT_MAX) { - length = USHORT_MAX; - } - WppHexDump.Length = (USHORT)length; - WppHexDump.Buffer = buffer; - return WppHexDump; + length = USHORT_MAX; + } + WppHexDump.Length = (USHORT)length; + WppHexDump.Buffer = buffer; + return WppHexDump; } #define LOG_HEXDUMP(length, buffer) \ diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index 7f91e2f..32f6a49 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -5,7 +5,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 30 // Increment this number whenever the driver changes #define P4VFS_VER_BUILD 0 // Increment this number when a major user mode change has been made -#define P4VFS_VER_REVISION 15 // Increment this number when we rebuild with any change +#define P4VFS_VER_REVISION 16 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v #define P4VFS_VER_STRINGIZE(v) P4VFS_VER_STRINGIZE_EX(v)