Skip to content

Commit 0464dd8

Browse files
[Tools] Manual Validation - Fix Credential Manager native memory handling (#401901)
1 parent a60ec93 commit 0464dd8

2 files changed

Lines changed: 42 additions & 32 deletions

File tree

Tools/ManualValidation/ManualValidationPipeline.cs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3979,24 +3979,24 @@ public void AddValidationData(string PackageIdentifier,string gitHubUserName = "
39793979
//Extend/reference CredWriteW as CredWrite.
39803980

39813981
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredFree", CharSet = CharSet.Unicode)]
3982-
private static extern bool CredRelease([In] IntPtr credentialPtr);
3983-
//Extend/reference CredFree as CredWrite.
3982+
private static extern void CredRelease([In] IntPtr credentialPtr);
3983+
//Extend/reference CredFree as CredRelease.
39843984

39853985
public static Credential GetUserCredential(string target) {
39863986
CredentialMem credMem;
39873987
IntPtr credPtr;
39883988
if (CredRead(target, 1, 0, out credPtr)) { //If found, returns true and adds to credPtr, else false and error.
3989-
credMem = Marshal.PtrToStructure<CredentialMem>(credPtr);
3990-
//"Marshals data from an unmanaged block of memory to a newly allocated managed object of the type specified by a generic type parameter."
3991-
byte[] passwordBytes = new byte[credMem.credentialBlobSize]; //Make a new byte array passwordBytes of size credentialBlobSize.
3992-
Marshal.Copy(credMem.credentialBlob, passwordBytes, 0, credMem.credentialBlobSize);
3993-
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
3994-
//
3995-
Credential cred = new Credential(credMem.targetName, credMem.userName, Encoding.Unicode.GetString(passwordBytes)); //Make a new Credential object cred.
3996-
//Original example doesn't include these:
3997-
CredRelease(credPtr);
3998-
CredRelease(credMem.credentialBlob);
3999-
return cred;
3989+
try {
3990+
credMem = Marshal.PtrToStructure<CredentialMem>(credPtr);
3991+
//"Marshals data from an unmanaged block of memory to a newly allocated managed object of the type specified by a generic type parameter."
3992+
byte[] passwordBytes = new byte[credMem.credentialBlobSize]; //Make a new byte array passwordBytes of size credentialBlobSize.
3993+
Marshal.Copy(credMem.credentialBlob, passwordBytes, 0, credMem.credentialBlobSize);
3994+
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
3995+
return new Credential(credMem.targetName, credMem.userName, Encoding.Unicode.GetString(passwordBytes)); //Make a new Credential object cred.
3996+
} finally {
3997+
//credentialBlob is an interior pointer into credPtr; free the buffer once via CredFree.
3998+
if (credPtr != IntPtr.Zero) { CredRelease(credPtr); }
3999+
}
40004000
} else {
40014001
throw new Exception("Failed to retrieve credentials");
40024002
}
@@ -4014,11 +4014,14 @@ public static void SetUserCredential(string target, string userName, string pass
40144014
userCredential.credentialBlobSize = (int)bpassword.Length;
40154015
userCredential.credentialBlob = Marshal.StringToCoTaskMemUni(password);
40164016
//If write fails, emit last error.
4017-
if (!CredWrite(ref userCredential, 0)) {
4018-
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
4017+
try {
4018+
if (!CredWrite(ref userCredential, 0)) {
4019+
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
4020+
}
4021+
} finally {
4022+
//Match StringToCoTaskMemUni allocation with FreeCoTaskMem.
4023+
Marshal.FreeCoTaskMem(userCredential.credentialBlob);
40194024
}
4020-
//Original example doesn't include this. Was going to use FreeCoTaskMem as recommended, but this gave an error.
4021-
Marshal.FreeCoTaskMem(userCredential.credentialBlob);
40224025
}
40234026

40244027
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

Tools/ManualValidation/ManualValidationPipeline.ps1

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7431,21 +7431,25 @@ namespace CredManager {
74317431
private static extern bool CredRead(string target, int type, int reservedFlag, out IntPtr credentialPtr);
74327432
//Extend/reference CredReadW as CredRead.
74337433
7434+
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredFree", CharSet = CharSet.Unicode)]
7435+
private static extern void CredRelease([In] IntPtr credentialPtr);
7436+
//Extend/reference CredFree as CredRelease.
7437+
74347438
public static Credential GetUserCredential(string target) {
74357439
CredentialMem credMem;
74367440
IntPtr credPtr;
74377441
if (CredRead(target, 1, 0, out credPtr)) { //If found, returns true and adds to credPtr, else false and error.
7438-
credMem = Marshal.PtrToStructure<CredentialMem>(credPtr);
7439-
//"Marshals data from an unmanaged block of memory to a newly allocated managed object of the type specified by a generic type parameter."
7440-
byte[] passwordBytes = new byte[credMem.credentialBlobSize]; //Make a new byte array passwordBytes of size credentialBlobSize.
7441-
Marshal.Copy(credMem.credentialBlob, passwordBytes, 0, credMem.credentialBlobSize);
7442-
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
7443-
//
7444-
Credential cred = new Credential(credMem.targetName, credMem.userName, Encoding.Unicode.GetString(passwordBytes)); //Make a new Credential object cred.
7445-
//Original example doesn't include these:
7446-
Marshal.FreeHGlobal(credPtr);
7447-
Marshal.FreeHGlobal(credMem.credentialBlob);
7448-
return cred;
7442+
try {
7443+
credMem = Marshal.PtrToStructure<CredentialMem>(credPtr);
7444+
//"Marshals data from an unmanaged block of memory to a newly allocated managed object of the type specified by a generic type parameter."
7445+
byte[] passwordBytes = new byte[credMem.credentialBlobSize]; //Make a new byte array passwordBytes of size credentialBlobSize.
7446+
Marshal.Copy(credMem.credentialBlob, passwordBytes, 0, credMem.credentialBlobSize);
7447+
//Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.
7448+
return new Credential(credMem.targetName, credMem.userName, Encoding.Unicode.GetString(passwordBytes)); //Make a new Credential object cred.
7449+
} finally {
7450+
//credentialBlob is an interior pointer into credPtr; free the buffer once via CredFree.
7451+
if (credPtr != IntPtr.Zero) { CredRelease(credPtr); }
7452+
}
74497453
} else {
74507454
throw new Exception("Failed to retrieve credentials");
74517455
}
@@ -7467,11 +7471,14 @@ namespace CredManager {
74677471
userCredential.credentialBlobSize = (int)bpassword.Length;
74687472
userCredential.credentialBlob = Marshal.StringToCoTaskMemUni(password);
74697473
//If write fails, emit last error.
7470-
if (!CredWrite(ref userCredential, 0)) {
7471-
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
7474+
try {
7475+
if (!CredWrite(ref userCredential, 0)) {
7476+
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
7477+
}
7478+
} finally {
7479+
//Match StringToCoTaskMemUni allocation with FreeCoTaskMem.
7480+
Marshal.FreeCoTaskMem(userCredential.credentialBlob);
74727481
}
7473-
//Original example doesn't include this. Was going to use FreeCoTaskMem as recommended, but this gave an error.
7474-
Marshal.FreeHGlobal(userCredential.credentialBlob);
74757482
}
74767483
}
74777484
}

0 commit comments

Comments
 (0)