diff --git a/src/Common/PCSCException.cpp b/src/Common/PCSCException.cpp index dc5eead3..f768c3fc 100644 --- a/src/Common/PCSCException.cpp +++ b/src/Common/PCSCException.cpp @@ -136,18 +136,18 @@ namespace VeraCrypt #ifdef TC_HEADER_Platform_Exception - void PCSCException::Deserialize(shared_ptr stream) + void PCSCException::DeserializeData(shared_ptr stream) { - Exception::Deserialize(stream); + Exception::DeserializeData(stream); Serializer sr(stream); int64 v; sr.Deserialize("ErrorCode", v); ErrorCode = (LONG_PCSC)v; } - void PCSCException::Serialize(shared_ptr stream) const + void PCSCException::SerializeData(shared_ptr stream) const { - Exception::Serialize(stream); + Exception::SerializeData(stream); Serializer sr(stream); int64 v = (int64)ErrorCode; sr.Serialize("ErrorCode", v); @@ -176,17 +176,17 @@ namespace VeraCrypt #ifdef TC_HEADER_Platform_Exception - void CommandAPDUNotValid::Deserialize(shared_ptr stream) + void CommandAPDUNotValid::DeserializeData(shared_ptr stream) { - Exception::Deserialize(stream); + Exception::DeserializeData(stream); Serializer sr(stream); sr.Deserialize("SrcPos", SrcPos); sr.Deserialize("ErrorStr", ErrorStr); } - void CommandAPDUNotValid::Serialize(shared_ptr stream) const + void CommandAPDUNotValid::SerializeData(shared_ptr stream) const { - Exception::Serialize(stream); + Exception::SerializeData(stream); Serializer sr(stream); sr.Serialize("SrcPos", SrcPos); sr.Serialize("ErrorStr", ErrorStr); diff --git a/src/Common/SecurityToken.cpp b/src/Common/SecurityToken.cpp index 604d4960..6e7c2e29 100644 --- a/src/Common/SecurityToken.cpp +++ b/src/Common/SecurityToken.cpp @@ -753,9 +753,9 @@ namespace VeraCrypt #ifdef TC_HEADER_Platform_Exception - void Pkcs11Exception::Deserialize(shared_ptr stream) + void Pkcs11Exception::DeserializeData(shared_ptr stream) { - Exception::Deserialize(stream); + Exception::DeserializeData(stream); Serializer sr(stream); uint64 code; sr.Deserialize("ErrorCode", code); @@ -764,9 +764,9 @@ namespace VeraCrypt ErrorCode = (CK_RV)code; } - void Pkcs11Exception::Serialize(shared_ptr stream) const + void Pkcs11Exception::SerializeData(shared_ptr stream) const { - Exception::Serialize(stream); + Exception::SerializeData(stream); Serializer sr(stream); sr.Serialize("ErrorCode", (uint64)ErrorCode); sr.Serialize("SubjectErrorCodeValid", SubjectErrorCodeValid); diff --git a/src/Core/Core.make b/src/Core/Core.make index def349d8..6ed9bb7e 100644 --- a/src/Core/Core.make +++ b/src/Core/Core.make @@ -13,6 +13,7 @@ OBJS := OBJS += CoreBase.o OBJS += CoreException.o +OBJS += CoreTest.o OBJS += FatFormatter.o OBJS += HostDevice.o OBJS += MountOptions.o diff --git a/src/Core/CoreException.cpp b/src/Core/CoreException.cpp index 9563b5ac..cff52ebb 100644 --- a/src/Core/CoreException.cpp +++ b/src/Core/CoreException.cpp @@ -15,24 +15,24 @@ namespace VeraCrypt { - void ElevationFailed::Deserialize (shared_ptr stream) + void ElevationFailed::DeserializeData (shared_ptr stream) { - ExecutedProcessFailed::Deserialize (stream); + ExecutedProcessFailed::DeserializeData (stream); } - void ElevationFailed::Serialize (shared_ptr stream) const + void ElevationFailed::SerializeData (shared_ptr stream) const { - ExecutedProcessFailed::Serialize (stream); + ExecutedProcessFailed::SerializeData (stream); } - void FilesystemDismountFailed::Deserialize (shared_ptr stream) + void FilesystemDismountFailed::DeserializeData (shared_ptr stream) { - ExecutedProcessFailed::Deserialize (stream); + ExecutedProcessFailed::DeserializeData (stream); } - void FilesystemDismountFailed::Serialize (shared_ptr stream) const + void FilesystemDismountFailed::SerializeData (shared_ptr stream) const { - ExecutedProcessFailed::Serialize (stream); + ExecutedProcessFailed::SerializeData (stream); } #define TC_EXCEPTION(TYPE) TC_SERIALIZER_FACTORY_ADD(TYPE) diff --git a/src/Core/CoreTest.cpp b/src/Core/CoreTest.cpp new file mode 100644 index 00000000..ca4b0017 --- /dev/null +++ b/src/Core/CoreTest.cpp @@ -0,0 +1,126 @@ +/* + Copyright (c) 2026 AM Crypto. All rights reserved. + + Governed by the Apache License 2.0, the full text of which is contained in + the file License.txt included in VeraCrypt binary and source distributions. +*/ + +#include "CoreTest.h" +#include "HostDevice.h" +#include "Platform/MemoryStream.h" +#include "Unix/CoreServiceRequest.h" +#include "Volume/VolumePassword.h" + +namespace VeraCrypt +{ + void CoreTest::HostDeviceTest () + { + HostDevice device; + device.Name = L"Device"; + device.SystemNumber = 0; + shared_ptr partition (new HostDevice); + partition->Name = L"Partition"; + partition->SystemNumber = 1; + device.Partitions.push_back (partition); + + // Exercise real parent/child serialization at the nesting boundary using a + // shallow tree and reserved scopes, without constructing a deep input tree. + for (int attempt = 0; attempt < 2; ++attempt) + { + shared_ptr stream (new MemoryStream); + vector < shared_ptr > scopes; + if (attempt != 0) + { + for (unsigned int i = 0; i < Serializer::MaxNestingDepth - 2; ++i) + scopes.push_back (shared_ptr (new SerializationScope (stream))); + } + device.Serialize (stream); + shared_ptr result = Serializable::DeserializeNew (stream); + if (result->Name != device.Name || result->Partitions.size() != 1 + || result->Partitions.front()->Name != partition->Name + || result->Partitions.front()->SystemNumber != partition->SystemNumber) + throw TestFailed (SRC_POS); + } + + for (int direction = 0; direction < 2; ++direction) + { + shared_ptr stream (new MemoryStream); + device.Serialize (stream); + vector < shared_ptr > scopes; + for (unsigned int i = 0; i < Serializer::MaxNestingDepth - 1; ++i) + scopes.push_back (shared_ptr (new SerializationScope (stream))); + try + { + if (direction == 0) + Serializable::DeserializeNew (stream); + else + device.Serialize (stream); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + scopes.clear(); + SerializationScope reusable (stream); + } + + device.Partitions.assign ((size_t) Serializer::MaxCollectionSize + 1, partition); + try + { + shared_ptr stream (new MemoryStream); + device.Serialize (stream); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + } + + void CoreTest::VolumePasswordTest () + { + SecureBuffer password (VolumePassword::MaxSize); + for (size_t i = 0; i < password.Size(); ++i) + password[i] = (uint8) i; + const size_t sizes[] = { 0, VolumePassword::MaxLegacySize, VolumePassword::MaxSize }; + for (size_t i = 0; i < array_capacity (sizes); ++i) + { + shared_ptr stream (new MemoryStream); + VolumePassword original (password.Ptr(), sizes[i]); + original.Serialize (stream); + shared_ptr result = Serializable::DeserializeNew (stream); + if (*result != original) + throw TestFailed (SRC_POS); + } + + shared_ptr stream (new MemoryStream); + Serializer sr (stream); + Serializable::SerializeHeader (sr, "VolumePassword"); + sr.Serialize ("PasswordSize", (uint64) VolumePassword::MaxSize + 1); + try + { + Serializable::DeserializeNew (stream); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + try + { + VolumePassword invalid (password.Ptr(), VolumePassword::MaxSize + 1); + throw TestFailed (SRC_POS); + } + catch (PasswordTooLong &) { } + } + + void CoreTest::ServiceRequestTest () + { + shared_ptr stream (new MemoryStream); + GetDeviceSizeRequest request (DevicePath (L"/dev/test")); + request.Serialize (stream); + shared_ptr result = Serializable::DeserializeNew (stream); + GetDeviceSizeRequest *deviceRequest = dynamic_cast (result.get()); + if (!deviceRequest || deviceRequest->Path != request.Path) + throw TestFailed (SRC_POS); + } + + void CoreTest::TestAll () + { + HostDeviceTest(); + VolumePasswordTest(); + ServiceRequestTest(); + } +} diff --git a/src/Core/CoreTest.h b/src/Core/CoreTest.h new file mode 100644 index 00000000..644de318 --- /dev/null +++ b/src/Core/CoreTest.h @@ -0,0 +1,26 @@ +/* + Copyright (c) 2026 AM Crypto. All rights reserved. + + Governed by the Apache License 2.0, the full text of which is contained in + the file License.txt included in VeraCrypt binary and source distributions. +*/ + +#ifndef TC_HEADER_Core_CoreTest +#define TC_HEADER_Core_CoreTest + +namespace VeraCrypt +{ + class CoreTest + { + public: + static void TestAll (); + + private: + CoreTest (); + static void HostDeviceTest (); + static void VolumePasswordTest (); + static void ServiceRequestTest (); + }; +} + +#endif // TC_HEADER_Core_CoreTest diff --git a/src/Core/HostDevice.cpp b/src/Core/HostDevice.cpp index 69fa3d15..811a298c 100644 --- a/src/Core/HostDevice.cpp +++ b/src/Core/HostDevice.cpp @@ -15,7 +15,7 @@ namespace VeraCrypt { - void HostDevice::Deserialize (shared_ptr stream) + void HostDevice::DeserializeData (shared_ptr stream) { Serializer sr (stream); MountPoint = sr.DeserializeWString ("MountPoint"); @@ -27,13 +27,16 @@ namespace VeraCrypt uint32 partitionCount; sr.Deserialize ("Partitions", partitionCount); + Serializer::ValidateCollectionSize (partitionCount); + HostDeviceList partitions; for (uint32 i = 0; i < partitionCount; i++) - Partitions.push_back (Serializable::DeserializeNew (stream)); + partitions.push_back (Serializable::DeserializeNew (stream)); + Partitions.swap (partitions); } - void HostDevice::Serialize (shared_ptr stream) const + void HostDevice::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); + Serializer::ValidateCollectionSize (Partitions.size()); Serializer sr (stream); sr.Serialize ("MountPoint", wstring (MountPoint)); sr.Serialize ("Name", Name); diff --git a/src/Core/MountOptions.cpp b/src/Core/MountOptions.cpp index 3b776520..a33ed4cd 100644 --- a/src/Core/MountOptions.cpp +++ b/src/Core/MountOptions.cpp @@ -58,7 +58,7 @@ namespace VeraCrypt TC_CLONE (EMVSupportEnabled); } - void MountOptions::Deserialize (shared_ptr stream) + void MountOptions::DeserializeData (shared_ptr stream) { Serializer sr (stream); wstring nameValue; @@ -132,9 +132,8 @@ namespace VeraCrypt sr.Deserialize ("ProtectionPim", ProtectionPim); } - void MountOptions::Serialize (shared_ptr stream) const + void MountOptions::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); sr.Serialize ("CachePassword", CachePassword); diff --git a/src/Core/Unix/CoreService.cpp b/src/Core/Unix/CoreService.cpp index ae7381ff..380de05b 100644 --- a/src/Core/Unix/CoreService.cpp +++ b/src/Core/Unix/CoreService.cpp @@ -574,14 +574,12 @@ namespace VeraCrypt if (errOutput.empty()) return; - unique_ptr deserializedObject; - Exception *deserializedException = nullptr; + shared_ptr deserializedException; try { shared_ptr stream (new MemoryStream (ConstBufferPtr ((uint8 *) &errOutput[0], errOutput.size()))); - deserializedObject.reset (Serializable::DeserializeNew (stream)); - deserializedException = dynamic_cast (deserializedObject.get()); + deserializedException = Serializable::DeserializeNew (stream); } catch (...) { } @@ -1076,9 +1074,21 @@ namespace VeraCrypt } #endif - unique_ptr CoreService::GetResponseObject () + template + static bool IsResponseType (const Serializable &object) { - unique_ptr deserializedObject (Serializable::DeserializeNew (ServiceOutputStream)); + return Serializable::IsType (object) || Serializable::IsType (object); + } + + template + static bool IsInitialResponseType (const Serializable &object) + { + return IsResponseType (object) || Serializable::IsType (object); + } + + unique_ptr CoreService::GetResponseObject (Serializable::TypeValidator isExpectedType) + { + unique_ptr deserializedObject (Serializable::DeserializeNew (ServiceOutputStream, isExpectedType)); Exception *deserializedException = dynamic_cast (deserializedObject.get()); if (deserializedException) @@ -1090,7 +1100,7 @@ namespace VeraCrypt template unique_ptr CoreService::GetResponse () { - unique_ptr deserializedObject (GetResponseObject()); + unique_ptr deserializedObject (GetResponseObject (&IsResponseType )); if (dynamic_cast (deserializedObject.get()) == nullptr) throw ParameterIncorrect (SRC_POS); @@ -1206,7 +1216,7 @@ namespace VeraCrypt ElevatedServiceStartedResponse().Serialize (outputStream); request->Serialize (ServiceInputStream); - GetResponse ()->Serialize (outputStream); + GetResponse ()->Serialize (outputStream); continue; } @@ -1500,7 +1510,7 @@ namespace VeraCrypt { request.Serialize (ServiceInputStream); - unique_ptr response (GetResponseObject()); + unique_ptr response (GetResponseObject (&IsInitialResponseType )); if (dynamic_cast (response.get()) != nullptr) { // The elevated channel is usable even if the forwarded request fails. diff --git a/src/Core/Unix/CoreService.h b/src/Core/Unix/CoreService.h index f11f259c..e159e1b7 100644 --- a/src/Core/Unix/CoreService.h +++ b/src/Core/Unix/CoreService.h @@ -50,7 +50,7 @@ namespace VeraCrypt static void Stop (); protected: - static unique_ptr GetResponseObject (); + static unique_ptr GetResponseObject (Serializable::TypeValidator isExpectedType); template static unique_ptr GetResponse (); template static unique_ptr SendRequest (CoreServiceRequest &request); static void StartElevated (const CoreServiceRequest &request); diff --git a/src/Core/Unix/CoreServiceRequest.cpp b/src/Core/Unix/CoreServiceRequest.cpp index e9fcc112..d72f4382 100644 --- a/src/Core/Unix/CoreServiceRequest.cpp +++ b/src/Core/Unix/CoreServiceRequest.cpp @@ -16,7 +16,7 @@ namespace VeraCrypt { - void CoreServiceRequest::Deserialize (shared_ptr stream) + void CoreServiceRequest::DeserializeData (shared_ptr stream) { Serializer sr (stream); sr.Deserialize ("AdminPassword", AdminPassword); @@ -28,9 +28,8 @@ namespace VeraCrypt sr.Deserialize ("AllowInsecureMount", AllowInsecureMount); } - void CoreServiceRequest::Serialize (shared_ptr stream) const + void CoreServiceRequest::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); sr.Serialize ("AdminPassword", AdminPassword); sr.Serialize ("ApplicationExecutablePath", wstring (ApplicationExecutablePath)); @@ -42,9 +41,9 @@ namespace VeraCrypt } // CheckFilesystemRequest - void CheckFilesystemRequest::Deserialize (shared_ptr stream) + void CheckFilesystemRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); MountedVolumeInfo = Serializable::DeserializeNew (stream); sr.Deserialize ("Repair", Repair); @@ -58,18 +57,18 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void CheckFilesystemRequest::Serialize (shared_ptr stream) const + void CheckFilesystemRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); MountedVolumeInfo->Serialize (stream); sr.Serialize ("Repair", Repair); } // DismountFilesystemRequest - void DismountFilesystemRequest::Deserialize (shared_ptr stream) + void DismountFilesystemRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); sr.Deserialize ("Force", Force); MountPoint = sr.DeserializeWString ("MountPoint"); @@ -80,18 +79,18 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void DismountFilesystemRequest::Serialize (shared_ptr stream) const + void DismountFilesystemRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("Force", Force); sr.Serialize ("MountPoint", wstring (MountPoint)); } // DismountVolumeRequest - void DismountVolumeRequest::Deserialize (shared_ptr stream) + void DismountVolumeRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); sr.Deserialize ("IgnoreOpenFiles", IgnoreOpenFiles); sr.Deserialize ("SyncVolumeInfo", SyncVolumeInfo); @@ -119,9 +118,9 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void DismountVolumeRequest::Serialize (shared_ptr stream) const + void DismountVolumeRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("IgnoreOpenFiles", IgnoreOpenFiles); sr.Serialize ("SyncVolumeInfo", SyncVolumeInfo); @@ -130,9 +129,9 @@ namespace VeraCrypt #ifdef TC_LINUX // EmergencyDismountVolumeRequest - void EmergencyDismountVolumeRequest::Deserialize (shared_ptr stream) + void EmergencyDismountVolumeRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); MountedVolumeInfo = Serializable::DeserializeNew (stream); } @@ -141,17 +140,17 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void EmergencyDismountVolumeRequest::Serialize (shared_ptr stream) const + void EmergencyDismountVolumeRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); MountedVolumeInfo->Serialize (stream); } #endif // GetDeviceSectorSizeRequest - void GetDeviceSectorSizeRequest::Deserialize (shared_ptr stream) + void GetDeviceSectorSizeRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); Path = sr.DeserializeWString ("Path"); } @@ -161,17 +160,17 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void GetDeviceSectorSizeRequest::Serialize (shared_ptr stream) const + void GetDeviceSectorSizeRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("Path", wstring (Path)); } // GetDeviceSizeRequest - void GetDeviceSizeRequest::Deserialize (shared_ptr stream) + void GetDeviceSizeRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); Path = sr.DeserializeWString ("Path"); } @@ -181,17 +180,17 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void GetDeviceSizeRequest::Serialize (shared_ptr stream) const + void GetDeviceSizeRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("Path", wstring (Path)); } // GetHostDevicesRequest - void GetHostDevicesRequest::Deserialize (shared_ptr stream) + void GetHostDevicesRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); sr.Deserialize ("PathListOnly", PathListOnly); } @@ -201,29 +200,29 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void GetHostDevicesRequest::Serialize (shared_ptr stream) const + void GetHostDevicesRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("PathListOnly", PathListOnly); } // ExitRequest - void ExitRequest::Deserialize (shared_ptr stream) + void ExitRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); } - void ExitRequest::Serialize (shared_ptr stream) const + void ExitRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); } #ifdef TC_MACOSX // ExecuteMacOSXAPFSFormatterRequest - void ExecuteMacOSXAPFSFormatterRequest::Deserialize (shared_ptr stream) + void ExecuteMacOSXAPFSFormatterRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); Device = sr.DeserializeWString ("Device"); sr.Deserialize ("OwnerGroupId", OwnerGroupId); @@ -235,9 +234,9 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void ExecuteMacOSXAPFSFormatterRequest::Serialize (shared_ptr stream) const + void ExecuteMacOSXAPFSFormatterRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("Device", wstring (Device)); sr.Serialize ("OwnerGroupId", OwnerGroupId); @@ -247,9 +246,9 @@ namespace VeraCrypt #ifdef TC_OPENBSD // ExecuteOpenBSDFFSFormatterRequest - void ExecuteOpenBSDFFSFormatterRequest::Deserialize (shared_ptr stream) + void ExecuteOpenBSDFFSFormatterRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); Device = sr.DeserializeWString ("Device"); sr.Deserialize ("OwnerGroupId", OwnerGroupId); @@ -261,9 +260,9 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void ExecuteOpenBSDFFSFormatterRequest::Serialize (shared_ptr stream) const + void ExecuteOpenBSDFFSFormatterRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); sr.Serialize ("Device", wstring (Device)); sr.Serialize ("OwnerGroupId", OwnerGroupId); @@ -272,9 +271,9 @@ namespace VeraCrypt #endif // MountVolumeRequest - void MountVolumeRequest::Deserialize (shared_ptr stream) + void MountVolumeRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); DeserializedOptions = Serializable::DeserializeNew (stream); Options = DeserializedOptions.get(); @@ -301,17 +300,17 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void MountVolumeRequest::Serialize (shared_ptr stream) const + void MountVolumeRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); Options->Serialize (stream); } // SetFileOwnerRequest - void SetFileOwnerRequest::Deserialize (shared_ptr stream) + void SetFileOwnerRequest::DeserializeData (shared_ptr stream) { - CoreServiceRequest::Deserialize (stream); + CoreServiceRequest::DeserializeData (stream); Serializer sr (stream); uint64 owner; @@ -326,9 +325,9 @@ namespace VeraCrypt return !Core->HasAdminPrivileges(); } - void SetFileOwnerRequest::Serialize (shared_ptr stream) const + void SetFileOwnerRequest::SerializeData (shared_ptr stream) const { - CoreServiceRequest::Serialize (stream); + CoreServiceRequest::SerializeData (stream); Serializer sr (stream); uint64 owner = Owner.SystemId; diff --git a/src/Core/Unix/CoreServiceResponse.cpp b/src/Core/Unix/CoreServiceResponse.cpp index 0209635d..6e742095 100644 --- a/src/Core/Unix/CoreServiceResponse.cpp +++ b/src/Core/Unix/CoreServiceResponse.cpp @@ -16,134 +16,123 @@ namespace VeraCrypt { // ElevatedServiceStartedResponse - void ElevatedServiceStartedResponse::Deserialize (shared_ptr stream) + void ElevatedServiceStartedResponse::DeserializeData (shared_ptr stream) { } - void ElevatedServiceStartedResponse::Serialize (shared_ptr stream) const + void ElevatedServiceStartedResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); } // CheckFilesystemResponse - void CheckFilesystemResponse::Deserialize (shared_ptr stream) + void CheckFilesystemResponse::DeserializeData (shared_ptr stream) { } - void CheckFilesystemResponse::Serialize (shared_ptr stream) const + void CheckFilesystemResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); } // DismountFilesystemResponse - void DismountFilesystemResponse::Deserialize (shared_ptr stream) + void DismountFilesystemResponse::DeserializeData (shared_ptr stream) { } - void DismountFilesystemResponse::Serialize (shared_ptr stream) const + void DismountFilesystemResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); } // DismountVolumeResponse - void DismountVolumeResponse::Deserialize (shared_ptr stream) + void DismountVolumeResponse::DeserializeData (shared_ptr stream) { DismountedVolumeInfo = Serializable::DeserializeNew (stream); } - void DismountVolumeResponse::Serialize (shared_ptr stream) const + void DismountVolumeResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); DismountedVolumeInfo->Serialize (stream); } // GetDeviceSectorSizeResponse - void GetDeviceSectorSizeResponse::Deserialize (shared_ptr stream) + void GetDeviceSectorSizeResponse::DeserializeData (shared_ptr stream) { Serializer sr (stream); sr.Deserialize ("Size", Size); } - void GetDeviceSectorSizeResponse::Serialize (shared_ptr stream) const + void GetDeviceSectorSizeResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); sr.Serialize ("Size", Size); } // GetDeviceSizeResponse - void GetDeviceSizeResponse::Deserialize (shared_ptr stream) + void GetDeviceSizeResponse::DeserializeData (shared_ptr stream) { Serializer sr (stream); sr.Deserialize ("Size", Size); } - void GetDeviceSizeResponse::Serialize (shared_ptr stream) const + void GetDeviceSizeResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); sr.Serialize ("Size", Size); } // GetHostDevicesResponse - void GetHostDevicesResponse::Deserialize (shared_ptr stream) + void GetHostDevicesResponse::DeserializeData (shared_ptr stream) { Serializable::DeserializeList (stream, HostDevices); } - void GetHostDevicesResponse::Serialize (shared_ptr stream) const + void GetHostDevicesResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializable::SerializeList (stream, HostDevices); } #ifdef TC_MACOSX // ExecuteMacOSXAPFSFormatterResponse - void ExecuteMacOSXAPFSFormatterResponse::Deserialize (shared_ptr stream) + void ExecuteMacOSXAPFSFormatterResponse::DeserializeData (shared_ptr stream) { } - void ExecuteMacOSXAPFSFormatterResponse::Serialize (shared_ptr stream) const + void ExecuteMacOSXAPFSFormatterResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); } #endif #ifdef TC_OPENBSD // ExecuteOpenBSDFFSFormatterResponse - void ExecuteOpenBSDFFSFormatterResponse::Deserialize (shared_ptr stream) + void ExecuteOpenBSDFFSFormatterResponse::DeserializeData (shared_ptr stream) { } - void ExecuteOpenBSDFFSFormatterResponse::Serialize (shared_ptr stream) const + void ExecuteOpenBSDFFSFormatterResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); } #endif // MountVolumeResponse - void MountVolumeResponse::Deserialize (shared_ptr stream) + void MountVolumeResponse::DeserializeData (shared_ptr stream) { Serializer sr (stream); MountedVolumeInfo = Serializable::DeserializeNew (stream); } - void MountVolumeResponse::Serialize (shared_ptr stream) const + void MountVolumeResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); MountedVolumeInfo->Serialize (stream); } // SetFileOwnerResponse - void SetFileOwnerResponse::Deserialize (shared_ptr stream) + void SetFileOwnerResponse::DeserializeData (shared_ptr stream) { } - void SetFileOwnerResponse::Serialize (shared_ptr stream) const + void SetFileOwnerResponse::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); } TC_SERIALIZER_FACTORY_ADD_CLASS (ElevatedServiceStartedResponse); diff --git a/src/Main/UserInterface.cpp b/src/Main/UserInterface.cpp index a51d7d6c..4c8c9215 100644 --- a/src/Main/UserInterface.cpp +++ b/src/Main/UserInterface.cpp @@ -19,6 +19,7 @@ #include #include #include "Crypto/cpu.h" +#include "Core/CoreTest.h" #include "Platform/PlatformTest.h" #include "Common/PCSCException.h" #ifdef TC_UNIX @@ -1829,6 +1830,7 @@ const FileManager fileManagers[] = { if (!PlatformTest::TestAll()) throw TestFailed (SRC_POS); + CoreTest::TestAll(); EncryptionTest::TestAll(); // StringFormatter diff --git a/src/Platform/Exception.cpp b/src/Platform/Exception.cpp index 3bb04112..6e83eb90 100644 --- a/src/Platform/Exception.cpp +++ b/src/Platform/Exception.cpp @@ -15,33 +15,32 @@ namespace VeraCrypt { - void Exception::Deserialize (shared_ptr stream) + void Exception::DeserializeData (shared_ptr stream) { Serializer sr (stream); sr.Deserialize ("Message", Message); sr.Deserialize ("Subject", Subject); } - void Exception::Serialize (shared_ptr stream) const + void Exception::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); sr.Serialize ("Message", Message); sr.Serialize ("Subject", Subject); } - void ExecutedProcessFailed::Deserialize (shared_ptr stream) + void ExecutedProcessFailed::DeserializeData (shared_ptr stream) { - Exception::Deserialize (stream); + Exception::DeserializeData (stream); Serializer sr (stream); sr.Deserialize ("Command", Command); sr.Deserialize ("ExitCode", ExitCode); sr.Deserialize ("ErrorOutput", ErrorOutput); } - void ExecutedProcessFailed::Serialize (shared_ptr stream) const + void ExecutedProcessFailed::SerializeData (shared_ptr stream) const { - Exception::Serialize (stream); + Exception::SerializeData (stream); Serializer sr (stream); sr.Serialize ("Command", Command); sr.Serialize ("ExitCode", ExitCode); diff --git a/src/Platform/PlatformTest.cpp b/src/Platform/PlatformTest.cpp index 11f6a49b..54604e03 100644 --- a/src/Platform/PlatformTest.cpp +++ b/src/Platform/PlatformTest.cpp @@ -26,6 +26,312 @@ namespace VeraCrypt { + class TestSerializer : public Serializer + { + public: + TestSerializer (shared_ptr stream) : Serializer (stream) { } + string ReadString () { return DeserializeString (); } + wstring ReadWString () { return DeserializeWString (); } + }; + + static shared_ptr CreateStringTestStream (uint64 declaredSize, const ConstBufferPtr &data) + { + shared_ptr stream (new MemoryStream); + uint64 fieldSize = Endian::Big (uint64 (sizeof (declaredSize))); + uint64 size = Endian::Big (declaredSize); + stream->Write (ConstBufferPtr ((uint8 *) &fieldSize, sizeof (fieldSize))); + stream->Write (ConstBufferPtr ((uint8 *) &size, sizeof (size))); + if (data.Size() > 0) + stream->Write (data); + return stream; + } + + static void SerializerFailureTest () + { + bool exceptionThrown = false; + try + { + TestSerializer ser (CreateStringTestStream (0, ConstBufferPtr())); + ser.ReadString (); + } + catch (ParameterIncorrect &) { exceptionThrown = true; } + if (!exceptionThrown) + throw TestFailed (SRC_POS); + + exceptionThrown = false; + try + { + TestSerializer ser (CreateStringTestStream (1024 * 1024 + 1, ConstBufferPtr())); + ser.ReadString (); + } + catch (ParameterIncorrect &) { exceptionThrown = true; } + if (!exceptionThrown) + throw TestFailed (SRC_POS); + + uint8 unterminatedString = 'x'; + exceptionThrown = false; + try + { + TestSerializer ser (CreateStringTestStream (1, ConstBufferPtr (&unterminatedString, 1))); + ser.ReadString (); + } + catch (ParameterIncorrect &) { exceptionThrown = true; } + if (!exceptionThrown) + throw TestFailed (SRC_POS); + + exceptionThrown = false; + try + { + TestSerializer ser (CreateStringTestStream (sizeof (wchar_t) - 1, ConstBufferPtr())); + ser.ReadWString (); + } + catch (ParameterIncorrect &) { exceptionThrown = true; } + if (!exceptionThrown) + throw TestFailed (SRC_POS); + + wchar_t unterminatedWString = L'x'; + exceptionThrown = false; + try + { + TestSerializer ser (CreateStringTestStream (sizeof (unterminatedWString), ConstBufferPtr ((uint8 *) &unterminatedWString, sizeof (unterminatedWString)))); + ser.ReadWString (); + } + catch (ParameterIncorrect &) { exceptionThrown = true; } + if (!exceptionThrown) + throw TestFailed (SRC_POS); + + exceptionThrown = false; + try + { + Serializer::ValidateCollectionSize (65537); + } + catch (ParameterIncorrect &) { exceptionThrown = true; } + if (!exceptionThrown) + throw TestFailed (SRC_POS); + } + + static void SerializerStringPolicyTest () + { + shared_ptr stream (new MemoryStream); + Serializer sr (stream); + const string strings[] = { "", "text", string ((size_t) Serializer::MaxStringSize - 1, 'x') }; + const wstring wstrings[] = { L"", L"text", wstring ((size_t) Serializer::MaxStringSize / sizeof (wchar_t) - 1, L'x') }; + for (size_t i = 0; i < array_capacity (strings); ++i) + { + sr.Serialize ("String", strings[i]); + sr.Serialize ("WString", wstrings[i]); + if (sr.DeserializeString ("String") != strings[i] || sr.DeserializeWString ("WString") != wstrings[i]) + throw TestFailed (SRC_POS); + } + + const string invalidStrings[] = { string ("x\0y", 3), string (1, '\0'), string ((size_t) Serializer::MaxStringSize, 'x') }; + const wstring invalidWStrings[] = { wstring (L"x\0y", 3), wstring (1, L'\0'), wstring ((size_t) Serializer::MaxStringSize / sizeof (wchar_t), L'x') }; + for (size_t i = 0; i < array_capacity (invalidStrings); ++i) + { + try + { + sr.Serialize ("String", invalidStrings[i]); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + try + { + sr.Serialize ("WString", invalidWStrings[i]); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + + try + { + TestSerializer reader (CreateStringTestStream (invalidStrings[i].size() + 1, + ConstBufferPtr ((const uint8 *) invalidStrings[i].c_str(), invalidStrings[i].size() + 1))); + reader.ReadString(); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + try + { + size_t size = (invalidWStrings[i].size() + 1) * sizeof (wchar_t); + TestSerializer reader (CreateStringTestStream (size, ConstBufferPtr ((const uint8 *) invalidWStrings[i].c_str(), size))); + reader.ReadWString(); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + } + + try + { + TestSerializer reader (CreateStringTestStream (0, ConstBufferPtr())); + reader.ReadWString(); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + } + + static void SerializerCollectionTest () + { + shared_ptr stream (new MemoryStream); + Serializer sr (stream); + list strings ((size_t) Serializer::MaxCollectionSize + 1); + list wstrings ((size_t) Serializer::MaxCollectionSize + 1); + list < shared_ptr > objects ((size_t) Serializer::MaxCollectionSize + 1); + try + { + sr.Serialize ("Strings", strings); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + try + { + sr.Serialize ("WStrings", wstrings); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + try + { + Serializable::SerializeList (stream, objects); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + + strings.pop_back(); + wstrings.pop_back(); + sr.Serialize ("Strings", strings); + sr.Serialize ("WStrings", wstrings); + if (sr.DeserializeStringList ("Strings") != strings || sr.DeserializeWStringList ("WStrings") != wstrings) + throw TestFailed (SRC_POS); + + sr.Serialize ("Strings", Serializer::MaxCollectionSize + 1); + try + { + sr.DeserializeStringList ("Strings"); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + sr.Serialize ("WStrings", Serializer::MaxCollectionSize + 1); + try + { + sr.DeserializeWStringList ("WStrings"); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + Serializable::SerializeHeader (sr, "list"); + sr.Serialize ("ListSize", Serializer::MaxCollectionSize + 1); + objects.clear(); + try + { + Serializable::DeserializeList (stream, objects); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + } + + // Registered only for this test; counters check that rejection happens before parsing + // and that failed reads release the object, including through the raw-pointer API. + class SerializerTestObject : public Serializable + { + public: + SerializerTestObject () { ++LiveCount; } + virtual ~SerializerTestObject () { --LiveCount; } + static Serializable *GetNewSerializable () { return new SerializerTestObject; } + virtual void DeserializeData (shared_ptr stream) + { + ++ParseCount; + Serializer sr (stream); + sr.DeserializeUInt32 ("Value"); + } + static int LiveCount; + static int ParseCount; + }; + + int SerializerTestObject::LiveCount = 0; + int SerializerTestObject::ParseCount = 0; + + static void SerializableTypeTest () + { + TC_SERIALIZER_FACTORY_ADD (SerializerTestObject); + finally_do ({ + SerializerFactory::NameToTypeMap->erase ("SerializerTestObject"); + SerializerFactory::TypeToNameMap->erase (StringConverter::GetTypeName (typeid (SerializerTestObject))); + }); + SerializerTestObject::ParseCount = 0; + + shared_ptr stream (new MemoryStream); + Serializer sr (stream); + Serializable::SerializeHeader (sr, "SerializerTestObject"); + try + { + Serializable::DeserializeNew (stream); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + if (SerializerTestObject::LiveCount != 0 || SerializerTestObject::ParseCount != 0) + throw TestFailed (SRC_POS); + + Serializable::SerializeHeader (sr, "list"); + sr.Serialize ("ListSize", uint64 (1)); + Serializable::SerializeHeader (sr, "SerializerTestObject"); + list < shared_ptr > objects; + try + { + Serializable::DeserializeList (stream, objects); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + if (SerializerTestObject::LiveCount != 0 || SerializerTestObject::ParseCount != 0 || !objects.empty()) + throw TestFailed (SRC_POS); + + for (int i = 0; i < 2; ++i) + { + Serializable::SerializeHeader (sr, "SerializerTestObject"); + try + { + if (i == 0) + { + unique_ptr object (Serializable::DeserializeNew (stream)); + } + else + Serializable::DeserializeNew (stream); + throw TestFailed (SRC_POS); + } + catch (InsufficientData &) { } + if (SerializerTestObject::LiveCount != 0 || SerializerTestObject::ParseCount != i + 1) + throw TestFailed (SRC_POS); + } + + ExecutedProcessFailed exception ("message", "command", 1, "output"); + exception.Serialize (stream); + shared_ptr result = Serializable::DeserializeNew (stream); + if (!dynamic_cast (result.get())) + throw TestFailed (SRC_POS); + } + + static void SerializerNestingTest () + { + shared_ptr stream (new MemoryStream); + for (int attempt = 0; attempt < 2; ++attempt) + { + vector < shared_ptr > scopes; + for (unsigned int i = 0; i < Serializer::MaxNestingDepth; ++i) + scopes.push_back (shared_ptr (new SerializationScope (stream))); + try + { + SerializationScope excess (stream); + throw TestFailed (SRC_POS); + } + catch (ParameterIncorrect &) { } + + // Nesting is local to a stream and is restored when scopes unwind. + shared_ptr otherStream (new MemoryStream); + SerializationScope other (otherStream); + scopes.clear(); + Serializer sr (stream); + sr.Serialize ("Value", uint32 (7)); + if (sr.DeserializeUInt32 ("Value") != 7) + throw TestFailed (SRC_POS); + } + } + // make_shared_auto, File, Stream, MemoryStream, Endian, Serializer, Serializable void PlatformTest::SerializerTest () { @@ -345,6 +651,11 @@ namespace VeraCrypt } SerializerTest(); + SerializerFailureTest(); + SerializerStringPolicyTest(); + SerializerCollectionTest(); + SerializableTypeTest(); + SerializerNestingTest(); ThreadTest(); return true; diff --git a/src/Platform/Serializable.cpp b/src/Platform/Serializable.cpp index 3a875685..2d4bbaa1 100644 --- a/src/Platform/Serializable.cpp +++ b/src/Platform/Serializable.cpp @@ -10,30 +10,42 @@ code distribution packages. */ +#include "Exception.h" #include "Serializable.h" #include "SerializerFactory.h" namespace VeraCrypt { + void Serializable::Deserialize (shared_ptr stream) + { + SerializationScope scope (stream); + DeserializeData (stream); + } + string Serializable::DeserializeHeader (shared_ptr stream) { Serializer sr (stream); return sr.DeserializeString ("SerializableName"); } - Serializable *Serializable::DeserializeNew (shared_ptr stream) + Serializable *Serializable::DeserializeNew (shared_ptr stream, TypeValidator isExpectedType) { string name = Serializable::DeserializeHeader (stream); - Serializable *serializable = SerializerFactory::GetNewSerializable (name); + unique_ptr serializable (SerializerFactory::GetNewSerializable (name)); + // Allow legitimate subtypes, but never run an unexpected object's parser. + if (isExpectedType && !isExpectedType (*serializable)) + throw ParameterIncorrect (SRC_POS); serializable->Deserialize (stream); - return serializable; + return serializable.release(); } void Serializable::Serialize (shared_ptr stream) const { + SerializationScope scope (stream); Serializer sr (stream); Serializable::SerializeHeader (sr, SerializerFactory::GetName (typeid (*this))); + SerializeData (stream); } void Serializable::SerializeHeader (Serializer &serializer, const string &name) diff --git a/src/Platform/Serializable.h b/src/Platform/Serializable.h index 4475978e..2de19e8f 100644 --- a/src/Platform/Serializable.h +++ b/src/Platform/Serializable.h @@ -26,43 +26,50 @@ namespace VeraCrypt public: virtual ~Serializable () { } - virtual void Deserialize (shared_ptr stream) = 0; + // Non-virtual entry points enforce nesting limits for every object. Derived + // classes implement only the field operations below; Serialize owns the header. + void Deserialize (shared_ptr stream); static string DeserializeHeader (shared_ptr stream); - static Serializable *DeserializeNew (shared_ptr stream); + typedef bool (*TypeValidator) (const Serializable &object); + static Serializable *DeserializeNew (shared_ptr stream, TypeValidator isExpectedType = nullptr); + + template + static bool IsType (const Serializable &object) + { + return dynamic_cast (&object) != nullptr; + } template static shared_ptr DeserializeNew (shared_ptr stream) { - shared_ptr p (dynamic_cast (DeserializeNew (stream))); - if (!p) - throw std::runtime_error (SRC_POS); - return p; + return shared_ptr (dynamic_cast (DeserializeNew (stream, &IsType ))); } template static void DeserializeList (shared_ptr stream, list < shared_ptr > &dataList) { + SerializationScope scope (stream); if (DeserializeHeader (stream) != string ("list<") + SerializerFactory::GetName (typeid (T)) + ">") throw std::runtime_error (SRC_POS); Serializer sr (stream); uint64 listSize; sr.Deserialize ("ListSize", listSize); + Serializer::ValidateCollectionSize (listSize); - for (size_t i = 0; i < listSize; i++) - { - shared_ptr p (dynamic_cast (DeserializeNew (stream))); - if (!p) - throw std::runtime_error (SRC_POS); - dataList.push_back (p); - } + list < shared_ptr > deserializedList; + for (uint64 i = 0; i < listSize; i++) + deserializedList.push_back (DeserializeNew (stream)); + dataList.splice (dataList.end(), deserializedList); } - virtual void Serialize (shared_ptr stream) const; + void Serialize (shared_ptr stream) const; template static void SerializeList (shared_ptr stream, const list < shared_ptr > &dataList) { + Serializer::ValidateCollectionSize (dataList.size()); + SerializationScope scope (stream); Serializer sr (stream); SerializeHeader (sr, string ("list<") + SerializerFactory::GetName (typeid (T)) + ">"); @@ -75,12 +82,17 @@ namespace VeraCrypt protected: Serializable () { } + // Call base-class field operations here, never their public entry points. + virtual void DeserializeData (shared_ptr stream) = 0; + virtual void SerializeData (shared_ptr stream) const { } }; } #define TC_SERIALIZABLE(TYPE) \ static Serializable *GetNewSerializable () { return new TYPE(); } \ - virtual void Deserialize (shared_ptr stream); \ - virtual void Serialize (shared_ptr stream) const +protected: \ + virtual void DeserializeData (shared_ptr stream); \ + virtual void SerializeData (shared_ptr stream) const; \ +public: #endif // TC_HEADER_Platform_Serializable diff --git a/src/Platform/Serializer.cpp b/src/Platform/Serializer.cpp index 1c918384..f3b11c1d 100644 --- a/src/Platform/Serializer.cpp +++ b/src/Platform/Serializer.cpp @@ -10,6 +10,7 @@ code distribution packages. */ +#include #include "Exception.h" #include "ForEach.h" #include "Memory.h" @@ -17,6 +18,24 @@ namespace VeraCrypt { + SerializationScope::SerializationScope (shared_ptr stream) : DataStream (stream) + { + if (!DataStream || DataStream->SerializationDepth >= Serializer::MaxNestingDepth) + throw ParameterIncorrect (SRC_POS); + ++DataStream->SerializationDepth; + } + + SerializationScope::~SerializationScope () + { + --DataStream->SerializationDepth; + } + + Serializer::Serializer (shared_ptr stream) : DataStream (stream) + { + if (!DataStream) + throw ParameterIncorrect (SRC_POS); + } + template T Serializer::Deserialize () { @@ -125,11 +144,15 @@ namespace VeraCrypt string Serializer::DeserializeString () { uint64 size = Deserialize (); + if (size == 0 || size > MaxStringSize) + throw ParameterIncorrect (SRC_POS); vector data ((size_t) size); DataStream->ReadCompleteBuffer (BufferPtr ((uint8 *) &data[0], (size_t) size)); + if (data.back() != 0 || find (data.begin(), data.end() - 1, '\0') != data.end() - 1) + throw ParameterIncorrect (SRC_POS); - return string (&data[0]); + return string (&data[0], data.size() - 1); } string Serializer::DeserializeString (const string &name) @@ -143,8 +166,9 @@ namespace VeraCrypt ValidateName (name); list deserializedList; uint64 listSize = Deserialize (); + ValidateCollectionSize (listSize); - for (size_t i = 0; i < listSize; i++) + for (uint64 i = 0; i < listSize; i++) deserializedList.push_back (DeserializeString ()); return deserializedList; @@ -153,11 +177,15 @@ namespace VeraCrypt wstring Serializer::DeserializeWString () { uint64 size = Deserialize (); + if (size == 0 || size > MaxStringSize || size % sizeof (wchar_t) != 0) + throw ParameterIncorrect (SRC_POS); vector data ((size_t) size / sizeof (wchar_t)); DataStream->ReadCompleteBuffer (BufferPtr ((uint8 *) &data[0], (size_t) size)); + if (data.back() != 0 || find (data.begin(), data.end() - 1, L'\0') != data.end() - 1) + throw ParameterIncorrect (SRC_POS); - return wstring (&data[0]); + return wstring (&data[0], data.size() - 1); } list Serializer::DeserializeWStringList (const string &name) @@ -165,8 +193,9 @@ namespace VeraCrypt ValidateName (name); list deserializedList; uint64 listSize = Deserialize (); + ValidateCollectionSize (listSize); - for (size_t i = 0; i < listSize; i++) + for (uint64 i = 0; i < listSize; i++) deserializedList.push_back (DeserializeWString ()); return deserializedList; @@ -178,6 +207,12 @@ namespace VeraCrypt return DeserializeWString (); } + void Serializer::ValidateCollectionSize (uint64 size) + { + if (size > MaxCollectionSize) + throw ParameterIncorrect (SRC_POS); + } + template void Serializer::Serialize (T data) { @@ -249,6 +284,7 @@ namespace VeraCrypt void Serializer::Serialize (const string &name, const list &stringList) { + ValidateCollectionSize (stringList.size()); SerializeString (name); uint64 listSize = stringList.size(); @@ -260,6 +296,7 @@ namespace VeraCrypt void Serializer::Serialize (const string &name, const list &stringList) { + ValidateCollectionSize (stringList.size()); SerializeString (name); uint64 listSize = stringList.size(); @@ -281,15 +318,22 @@ namespace VeraCrypt void Serializer::SerializeString (const string &data) { + // Embedded NULs would be interpreted differently by C-string consumers. + if (data.size() >= MaxStringSize || data.find ('\0') != string::npos) + throw ParameterIncorrect (SRC_POS); + Serialize ((uint64) data.size() + 1); - DataStream->Write (ConstBufferPtr ((uint8 *) (data.data() ? data.data() : data.c_str()), data.size() + 1)); + DataStream->Write (ConstBufferPtr ((const uint8 *) data.c_str(), data.size() + 1)); } void Serializer::SerializeWString (const wstring &data) { - uint64 size = (data.size() + 1) * sizeof (wchar_t); + if (data.size() >= MaxStringSize / sizeof (wchar_t) || data.find (L'\0') != wstring::npos) + throw ParameterIncorrect (SRC_POS); + + uint64 size = ((uint64) data.size() + 1) * sizeof (wchar_t); Serialize (size); - DataStream->Write (ConstBufferPtr ((uint8 *) (data.data() ? data.data() : data.c_str()), (size_t) size)); + DataStream->Write (ConstBufferPtr ((const uint8 *) data.c_str(), (size_t) size)); } void Serializer::ValidateName (const string &name) diff --git a/src/Platform/Serializer.h b/src/Platform/Serializer.h index 6e103609..7c0e1d78 100644 --- a/src/Platform/Serializer.h +++ b/src/Platform/Serializer.h @@ -20,12 +20,31 @@ namespace VeraCrypt { + // An object or object-list operation retains this scope across all its fields. + // Sibling operations and separate streams have independent nesting limits. + class SerializationScope + { + public: + explicit SerializationScope (shared_ptr stream); + ~SerializationScope (); + + private: + shared_ptr DataStream; + SerializationScope (const SerializationScope &); + SerializationScope &operator= (const SerializationScope &); + }; + class Serializer { public: - Serializer (shared_ptr stream) : DataStream (stream) { } + Serializer (shared_ptr stream); virtual ~Serializer () { } + // Wire sizes include the terminating NUL. The same limits apply in both directions. + static const uint64 MaxCollectionSize = 65536; + static const uint64 MaxStringSize = 1024 * 1024; + static const unsigned int MaxNestingDepth = 64; + void Deserialize (const string &name, bool &data); void Deserialize (const string &name, uint8 &data); void Deserialize (const string &name, int32 &data); @@ -44,6 +63,7 @@ namespace VeraCrypt list DeserializeStringList (const string &name); wstring DeserializeWString (const string &name); list DeserializeWStringList (const string &name); + static void ValidateCollectionSize (uint64 size); void Serialize (const string &name, bool data); void Serialize (const string &name, uint8 data); void Serialize (const string &name, const char *data); diff --git a/src/Platform/SharedPtr.h b/src/Platform/SharedPtr.h index ca3ab44d..9b16ea86 100644 --- a/src/Platform/SharedPtr.h +++ b/src/Platform/SharedPtr.h @@ -41,7 +41,18 @@ namespace VeraCrypt : Pointer (nullptr), UseCount (nullptr) { } explicit SharedPtr (T *pointer) - : Pointer (pointer), UseCount (new SharedVal (1)) { } + : Pointer (pointer), UseCount (nullptr) + { + try + { + UseCount = new SharedVal (1); + } + catch (...) + { + delete Pointer; + throw; + } + } SharedPtr (const SharedPtr &source) { diff --git a/src/Platform/Stream.h b/src/Platform/Stream.h index 8ad457f3..f9bd1925 100644 --- a/src/Platform/Stream.h +++ b/src/Platform/Stream.h @@ -27,9 +27,12 @@ namespace VeraCrypt virtual void Write (const ConstBufferPtr &data) = 0; protected: - Stream () { }; + Stream () : SerializationDepth (0) { } private: + friend class SerializationScope; + unsigned int SerializationDepth; + Stream (const Stream &); Stream &operator= (const Stream &); }; diff --git a/src/Platform/Unix/Process.cpp b/src/Platform/Unix/Process.cpp index 234c43e2..812ece6d 100644 --- a/src/Platform/Unix/Process.cpp +++ b/src/Platform/Unix/Process.cpp @@ -412,14 +412,12 @@ namespace VeraCrypt if (!exOutput.empty()) { - unique_ptr deserializedObject; - Exception *deserializedException = nullptr; + shared_ptr deserializedException; try { shared_ptr stream (new MemoryStream (ConstBufferPtr ((uint8 *) &exOutput[0], exOutput.size()))); - deserializedObject.reset (Serializable::DeserializeNew (stream)); - deserializedException = dynamic_cast (deserializedObject.get()); + deserializedException = Serializable::DeserializeNew (stream); } catch (...) { } diff --git a/src/Platform/Unix/SystemException.cpp b/src/Platform/Unix/SystemException.cpp index b45562c0..8d2c3cf7 100644 --- a/src/Platform/Unix/SystemException.cpp +++ b/src/Platform/Unix/SystemException.cpp @@ -38,9 +38,9 @@ namespace VeraCrypt { } - void SystemException::Deserialize (shared_ptr stream) + void SystemException::DeserializeData (shared_ptr stream) { - Exception::Deserialize (stream); + Exception::DeserializeData (stream); Serializer sr (stream); sr.Deserialize ("ErrorCode", ErrorCode); } @@ -50,9 +50,9 @@ namespace VeraCrypt return ErrorCode != 0; } - void SystemException::Serialize (shared_ptr stream) const + void SystemException::SerializeData (shared_ptr stream) const { - Exception::Serialize (stream); + Exception::SerializeData (stream); Serializer sr (stream); sr.Serialize ("ErrorCode", ErrorCode); } diff --git a/src/Volume/VolumeInfo.cpp b/src/Volume/VolumeInfo.cpp index c427c48d..98d4d032 100644 --- a/src/Volume/VolumeInfo.cpp +++ b/src/Volume/VolumeInfo.cpp @@ -16,7 +16,7 @@ namespace VeraCrypt { - void VolumeInfo::Deserialize (shared_ptr stream) + void VolumeInfo::DeserializeData (shared_ptr stream) { Serializer sr (stream); @@ -63,9 +63,8 @@ namespace VeraCrypt return first->SerialInstanceNumber > second->SerialInstanceNumber; } - void VolumeInfo::Serialize (shared_ptr stream) const + void VolumeInfo::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); Serializer sr (stream); const uint32 version = VERSION_NUM; diff --git a/src/Volume/VolumePassword.cpp b/src/Volume/VolumePassword.cpp index 94f49583..d53cd92b 100644 --- a/src/Volume/VolumePassword.cpp +++ b/src/Volume/VolumePassword.cpp @@ -36,11 +36,13 @@ namespace VeraCrypt PasswordBuffer.Allocate (MaxSize); } - void VolumePassword::Deserialize (shared_ptr stream) + void VolumePassword::DeserializeData (shared_ptr stream) { Serializer sr (stream); uint64 passwordSize; sr.Deserialize ("PasswordSize", passwordSize); + if (passwordSize > MaxSize) + throw ParameterIncorrect (SRC_POS); PasswordSize = static_cast (passwordSize); sr.Deserialize ("PasswordBuffer", BufferPtr (PasswordBuffer)); @@ -48,9 +50,10 @@ namespace VeraCrypt sr.Deserialize ("WipeData", wipeBuffer); } - void VolumePassword::Serialize (shared_ptr stream) const + void VolumePassword::SerializeData (shared_ptr stream) const { - Serializable::Serialize (stream); + if (PasswordSize > MaxSize) + throw ParameterIncorrect (SRC_POS); Serializer sr (stream); sr.Serialize ("PasswordSize", static_cast (PasswordSize)); sr.Serialize ("PasswordBuffer", ConstBufferPtr (PasswordBuffer));