-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Support building cpprestsdk with /MT on Windows #884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
basic_container_buffer, basic_rawptr_buffer, winhttp_request_context
in Windows when compiled with /MT
…shared or make_unique
…shared or make_unique in pplx
@BillyONeal, @ras0219-msft Can you please let me know whether I went overboard with any of the changes to the library. Also can someone provide nuget packages of OpenSSL and zlib that are build with /MT instead of /MD to include as dependencies? Thanks in advance. |
You use utility:: things but I don't see their definitions anywhere; I don't understand this change. If utility:: is not the same as std:: in this context, that's a huge breaking change we probably cannot accept. If it is the same as std::, then changing all these places appears unmotivated. The standard containers, at least in the versions of VS that cpprestsdk supports, call HeapAlloc and HeapFree, because malloc and free were changed to call those in the UCRT transition -- so it isn't clear what supposed corruption avoidance this gains in any case. |
// construct by copying (do nothing) | ||
} | ||
|
||
allocator(_Alloc&& _Right) CPPREST_NOEXCEPT : _Mybase(std::move(_Right)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constructor doesn't do anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is copy paste from std::allocator
. I don't claim to know all std::allocator
requirements, but if any function is not being used it will not be compiled right? In any case anything not being used can be removed.
} | ||
|
||
template<class _Other> | ||
allocator(allocator<_Other>&& _Right) CPPREST_NOEXCEPT : _Mybase(std::forward<_Other>(_Right)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto constructor doesn't do anything.
return (*this); | ||
} | ||
|
||
_Alloc& operator=(_Alloc&& _Right) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto does nothing
} | ||
|
||
template<class _Other> | ||
_Alloc& operator=(const allocator<_Other>&) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not part of the allocator requirements.
|
||
void deallocate(pointer _Ptr, size_type _Count) { | ||
// deallocate object at _Ptr | ||
::SecureZeroMemory(_Ptr, _Count * sizeof(_Ty)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this will zero allocated memory blocks but will not necessarily zero everything a container touches. For example, node-based containers with container-internal sentinel nodes, or std::string's small string optimization.
In either case, this doesn't seem appropriate to include here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reasoning I included the secure_allocator was for std::vector when encrypting/decrypting user supplied credentials, where for example on line 109 the buffer may be resized and moved to another memory block, how would one ensure that the previous memory block has been securely zeroed ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes, you're correct, that's broken. :( For std::vector (and only std::vector) I think an allocator like this can work.
That said I would prefer to just fix that code to make the vector the correct size at the beginning :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this allocator doesn't make the type Win32Encryption safe, and I don't think any change in the memory allocation strategy can do that (for precisely the reason in this comment, the container body itself is not zeroed).
It looks like all the APIs that touch that though are deprecated for precisely this reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hardened what was possible to harden here: #896
|
||
_DECLSPEC_ALLOCATOR pointer allocate(size_type _Count) { | ||
// allocate array of _Count elements | ||
return static_cast<pointer>(::HeapAlloc(::GetProcessHeap(), NULL, _Count * sizeof(_Ty))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allocate is damaged vs. std::allocator -- it is not checking for overflow of _Count.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the following resolve this ?
__declspec(allocator) allocate(size_type _Count) {
// allocate array of _Count elements
if (_Count == 0)
return nullptr;
// check overflow of multiply
if ((size_t)(-1) / sizeof(_Ty) < _Count)
_Xbad_alloc(); // report no memory
return static_cast<pointer>(::HeapAlloc(::GetProcessHeap(), NULL, _Count * sizeof(_Ty)));
}
return (*this); | ||
} | ||
|
||
_DECLSPEC_ALLOCATOR pointer allocate(size_type _Count) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is user code so it isn't allowed to be _Ugly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand, can you please explain how to resolve ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http://eel.is/c++draft/lex.name#3 says that _Ugly and __ugly names are reserved for use by the C++ implementation (compiler and standard library), not for user code. That said I think this is kind of moot given the compat problems here.
return (*this); | ||
} | ||
|
||
_DECLSPEC_ALLOCATOR pointer allocate(size_type _Count) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_DECLSPEC_ALLOCATOR is not a publicly documented supported _Ugly name and will be removed as soon as real declspec allocator is usable unconditionally in the standard library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should i use __declspec(allocator)
instead ?
using queue = std::queue<_Ty, _Container>; | ||
|
||
template<class _Ty, class _Traits = std::char_traits<_Ty>, class _Alloc = utility::allocator<_Ty>> | ||
using basic_string = std::basic_string<_Ty, _Traits, _Alloc>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see this now; I don't think we can accept this change because it will be massively breaking to any existing customers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First of all thank you for the comments, I will try to go through them.
I don't consider this to be breaking existing customers compatibility because the utility:: should be equal to the std:: counterparts (i.e. utility::string = std::string) unless _WIN32 is defined and _DLL is not defined which is the case for /MT, which none of the existing customers would be using anyway, please correct me if i am wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe getting a configuration that uses /MT is already in wide use through vcpkg.
Have you considered customizing std::allocator to do what you want by statically linking in an ::operator new and ::operator delete that does what you want instead of needing to rewrite the universe? |
@BillyONeal Thank you for all of your comments, I agree that there is a lot of rewriting but I consider that it is the library's job to maintain the correct types being supplied by the user of the library in order to function correctly and not corrupt the heap (since for example user supplied strings are Maybe I am overthinking it, but this is the solution I came up with. Thanks again. |
I agree it would be a good thing in theory, but in this case we have existing users who would be heavily broken, and it is unclear what actual benefit this provides. Again, back in the era where each DLL had their own heap and users had to worry about that this may have been a can of worms worth opening, but in the UCRT era malloc, ::operator new, and std::allocator use the process heap and couldn't care less about /MT, just as your custom allocator does.
I can cherry pick them if you wish. f743245 and 5247191 also look fine. |
The basic concept for building cpprestsdk with /MT on Windows is to use:
In this respect all references to std::string, std::vector, etc... have been replaced with utility::string, utility::vector, etc..., and most of the references to new have been replaced with utility::make_shared and utility::make_unique.
Additionally plaintext_string has been replaced with utility::secure_unique_string which on Windows will call SecureZeroMemory