Remove CONSOLE_API_MSG::UpdateUserBufferPointers hack (#10326)
## Summary of the Pull Request This commit introduces a copy constructor/operator for `_CONSOLE_API_MSG`. The change is not trivial as the struct contains a union of unnamed structs that cannot be copied using regular language features. As such a copy operator using `memcpy` was implemented. Additionally all access specifiers were removed, as those allow a C++ compiler to reorder struct members. This would break message passing. This commit is a good opportunity to prevent such miscompilations proactively. ## Validation Steps Performed * Command prompts of WSL2 fish-shell and pwsh still work ✔️ Closes #10076
This commit is contained in:
parent
1cc383f865
commit
296037a0fa
|
@ -89,6 +89,7 @@ ntprivapi
|
|||
oaidl
|
||||
ocidl
|
||||
ODR
|
||||
offsetof
|
||||
osver
|
||||
OSVERSIONINFOEXW
|
||||
otms
|
||||
|
|
|
@ -8,18 +8,51 @@
|
|||
#include "ApiMessage.h"
|
||||
#include "DeviceComm.h"
|
||||
|
||||
_CONSOLE_API_MSG::_CONSOLE_API_MSG() :
|
||||
Complete{ 0 },
|
||||
State{ 0 },
|
||||
_pDeviceComm(nullptr),
|
||||
_pApiRoutines(nullptr),
|
||||
_inputBuffer{},
|
||||
_outputBuffer{},
|
||||
Descriptor{ 0 },
|
||||
CreateObject{ 0 },
|
||||
CreateScreenBuffer{ 0 },
|
||||
msgHeader{ 0 }
|
||||
constexpr size_t structPacketDataSize = sizeof(_CONSOLE_API_MSG) - offsetof(_CONSOLE_API_MSG, Descriptor);
|
||||
|
||||
_CONSOLE_API_MSG::_CONSOLE_API_MSG()
|
||||
{
|
||||
// A union cannot have more than one initializer,
|
||||
// but it isn't exactly clear which union case is the largest.
|
||||
// --> Just memset() the entire thing.
|
||||
memset(&Descriptor, 0, structPacketDataSize);
|
||||
}
|
||||
|
||||
_CONSOLE_API_MSG::_CONSOLE_API_MSG(const _CONSOLE_API_MSG& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
_CONSOLE_API_MSG& _CONSOLE_API_MSG::operator=(const _CONSOLE_API_MSG& other)
|
||||
{
|
||||
Complete = other.Complete;
|
||||
State = other.State;
|
||||
_pDeviceComm = other._pDeviceComm;
|
||||
_pApiRoutines = other._pApiRoutines;
|
||||
_inputBuffer = other._inputBuffer;
|
||||
_outputBuffer = other._outputBuffer;
|
||||
|
||||
// Since this struct uses anonymous unions and thus cannot
|
||||
// explicitly reference it, we have to a bit cheeky to copy it.
|
||||
// --> Just memcpy() the entire thing.
|
||||
memcpy(&Descriptor, &other.Descriptor, structPacketDataSize);
|
||||
|
||||
if (State.InputBuffer)
|
||||
{
|
||||
State.InputBuffer = _inputBuffer.data();
|
||||
}
|
||||
|
||||
if (State.OutputBuffer)
|
||||
{
|
||||
State.OutputBuffer = _outputBuffer.data();
|
||||
}
|
||||
|
||||
if (Complete.Write.Data)
|
||||
{
|
||||
Complete.Write.Data = &u;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ConsoleProcessHandle* _CONSOLE_API_MSG::GetProcessHandle() const
|
||||
|
@ -194,22 +227,3 @@ void _CONSOLE_API_MSG::SetReplyInformation(const ULONG_PTR pInformation)
|
|||
{
|
||||
Complete.IoStatus.Information = pInformation;
|
||||
}
|
||||
|
||||
void _CONSOLE_API_MSG::UpdateUserBufferPointers()
|
||||
{
|
||||
// There are some instances where an API message may get copied.
|
||||
// Because it is infeasible to write a copy constructor for this class
|
||||
// without rewriting large swaths of conhost (because of the unnamed union)
|
||||
// we have chosen to introduce a "post-copy" step.
|
||||
// This makes sure the buffers in State are in sync with the actual
|
||||
// buffers in the object.
|
||||
if (State.InputBuffer)
|
||||
{
|
||||
State.InputBuffer = _inputBuffer.data();
|
||||
}
|
||||
|
||||
if (State.OutputBuffer)
|
||||
{
|
||||
State.OutputBuffer = _outputBuffer.data();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,17 +29,42 @@ typedef struct _CONSOLE_API_MSG
|
|||
{
|
||||
_CONSOLE_API_MSG();
|
||||
|
||||
CD_IO_COMPLETE Complete;
|
||||
CONSOLE_API_STATE State;
|
||||
_CONSOLE_API_MSG(_CONSOLE_API_MSG&&) = delete;
|
||||
_CONSOLE_API_MSG& operator=(_CONSOLE_API_MSG&&) = delete;
|
||||
|
||||
IDeviceComm* _pDeviceComm;
|
||||
IApiRoutines* _pApiRoutines;
|
||||
_CONSOLE_API_MSG(const _CONSOLE_API_MSG& other);
|
||||
_CONSOLE_API_MSG& operator=(const _CONSOLE_API_MSG&);
|
||||
|
||||
ConsoleProcessHandle* GetProcessHandle() const;
|
||||
ConsoleHandleData* GetObjectHandle() const;
|
||||
|
||||
[[nodiscard]] HRESULT ReadMessageInput(const ULONG cbOffset, _Out_writes_bytes_(cbSize) PVOID pvBuffer, const ULONG cbSize);
|
||||
[[nodiscard]] HRESULT GetAugmentedOutputBuffer(const ULONG cbFactor,
|
||||
_Outptr_result_bytebuffer_(*pcbSize) PVOID* ppvBuffer,
|
||||
_Out_ PULONG pcbSize);
|
||||
[[nodiscard]] HRESULT GetOutputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize);
|
||||
[[nodiscard]] HRESULT GetInputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize);
|
||||
|
||||
[[nodiscard]] HRESULT ReleaseMessageBuffers();
|
||||
|
||||
void SetReplyStatus(const NTSTATUS Status);
|
||||
void SetReplyInformation(const ULONG_PTR pInformation);
|
||||
|
||||
// DO NOT PUT ACCESS SPECIFIERS HERE.
|
||||
// The tail end of this structure is overwritten with console driver packet.
|
||||
// It's important that we have a deterministic, C-like field ordering
|
||||
// as this ensures that the packet data fields are last.
|
||||
// Putting access specifiers here ("private:") would break this.
|
||||
|
||||
CD_IO_COMPLETE Complete{};
|
||||
CONSOLE_API_STATE State{};
|
||||
|
||||
IDeviceComm* _pDeviceComm{ nullptr };
|
||||
IApiRoutines* _pApiRoutines{ nullptr };
|
||||
|
||||
private:
|
||||
boost::container::small_vector<BYTE, 128> _inputBuffer;
|
||||
boost::container::small_vector<BYTE, 128> _outputBuffer;
|
||||
|
||||
public:
|
||||
// From here down is the actual packet data sent/received.
|
||||
CD_IO_DESCRIPTOR Descriptor;
|
||||
union
|
||||
|
@ -62,33 +87,6 @@ public:
|
|||
};
|
||||
// End packet data
|
||||
|
||||
public:
|
||||
// DO NOT PUT MORE FIELDS DOWN HERE.
|
||||
// The tail end of this structure will have a console driver packet
|
||||
// copied over it and it will overwrite any fields declared here.
|
||||
|
||||
ConsoleProcessHandle* GetProcessHandle() const;
|
||||
ConsoleHandleData* GetObjectHandle() const;
|
||||
|
||||
[[nodiscard]] HRESULT ReadMessageInput(const ULONG cbOffset, _Out_writes_bytes_(cbSize) PVOID pvBuffer, const ULONG cbSize);
|
||||
[[nodiscard]] HRESULT GetAugmentedOutputBuffer(const ULONG cbFactor,
|
||||
_Outptr_result_bytebuffer_(*pcbSize) PVOID* ppvBuffer,
|
||||
_Out_ PULONG pcbSize);
|
||||
[[nodiscard]] HRESULT GetOutputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize);
|
||||
[[nodiscard]] HRESULT GetInputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize);
|
||||
|
||||
[[nodiscard]] HRESULT ReleaseMessageBuffers();
|
||||
|
||||
void SetReplyStatus(const NTSTATUS Status);
|
||||
void SetReplyInformation(const ULONG_PTR pInformation);
|
||||
|
||||
// MSFT-33127449, GH#9692
|
||||
// We are not writing a copy constructor for this class
|
||||
// so as to scope a fix as narrowly as possible to the
|
||||
// "invalid user buffer" crash.
|
||||
// TODO GH#10076: remove this.
|
||||
void UpdateUserBufferPointers();
|
||||
|
||||
// DO NOT PUT MORE FIELDS DOWN HERE.
|
||||
// The tail end of this structure will have a console driver packet
|
||||
// copied over it and it will overwrite any fields declared here.
|
||||
|
|
|
@ -28,10 +28,9 @@ ConsoleWaitBlock::ConsoleWaitBlock(_In_ ConsoleWaitQueue* const pProcessQueue,
|
|||
_In_ IWaitRoutine* const pWaiter) :
|
||||
_pProcessQueue(THROW_HR_IF_NULL(E_INVALIDARG, pProcessQueue)),
|
||||
_pObjectQueue(THROW_HR_IF_NULL(E_INVALIDARG, pObjectQueue)),
|
||||
_WaitReplyMessage(*pWaitReplyMessage),
|
||||
_pWaiter(THROW_HR_IF_NULL(E_INVALIDARG, pWaiter))
|
||||
{
|
||||
_WaitReplyMessage = *pWaitReplyMessage;
|
||||
|
||||
// MSFT-33127449, GH#9692
|
||||
// Until there's a "Wait", there's only one API message inflight at a time. In our
|
||||
// quest for performance, we put that single API message in charge of its own
|
||||
|
@ -51,14 +50,8 @@ ConsoleWaitBlock::ConsoleWaitBlock(_In_ ConsoleWaitQueue* const pProcessQueue,
|
|||
// waiting message or the "wait completer" has a bunch of dangling pointers in it.
|
||||
// Oops.
|
||||
//
|
||||
// Here, we fix up the message's internal pointers (in lieu of giving it a proper
|
||||
// copy constructor; see GH#10076) and then tell the wait completion routine (which
|
||||
// is going to be a COOKED_READ, RAW_READ, DirectRead or WriteData) about the new
|
||||
// buffer location.
|
||||
//
|
||||
// This is a scoped fix that should be replaced (TODO GH#10076) with a final one
|
||||
// after Ask mode.
|
||||
_WaitReplyMessage.UpdateUserBufferPointers();
|
||||
// Here, we tell the wait completion routine (which is going to be a COOKED_READ,
|
||||
// RAW_READ, DirectRead or WriteData) about the new buffer location.
|
||||
|
||||
if (pWaitReplyMessage->State.InputBuffer)
|
||||
{
|
||||
|
@ -69,12 +62,6 @@ ConsoleWaitBlock::ConsoleWaitBlock(_In_ ConsoleWaitQueue* const pProcessQueue,
|
|||
{
|
||||
_pWaiter->MigrateUserBuffersOnTransitionToBackgroundWait(pWaitReplyMessage->State.OutputBuffer, _WaitReplyMessage.State.OutputBuffer);
|
||||
}
|
||||
|
||||
// We will write the original message back (with updated out parameters/payload) when the request is finally serviced.
|
||||
if (pWaitReplyMessage->Complete.Write.Data != nullptr)
|
||||
{
|
||||
_WaitReplyMessage.Complete.Write.Data = &_WaitReplyMessage.u;
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -85,11 +72,7 @@ ConsoleWaitBlock::~ConsoleWaitBlock()
|
|||
{
|
||||
_pProcessQueue->_blocks.erase(_itProcessQueue);
|
||||
_pObjectQueue->_blocks.erase(_itObjectQueue);
|
||||
|
||||
if (_pWaiter != nullptr)
|
||||
{
|
||||
delete _pWaiter;
|
||||
}
|
||||
delete _pWaiter;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
Loading…
Reference in New Issue