[PATCH v2 1/3] user32: Implement SendInput size parameter check.
Rémi Bernon
rbernon at codeweavers.com
Fri Mar 19 08:47:35 CDT 2021
On 3/15/21 7:26 PM, Rémi Bernon wrote:
> On 3/9/21 7:39 PM, Rémi Bernon wrote:
>> Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
>> ---
>>
>> v2: Fix WoW64 and 32bit test results, a bit or rewrite of PATCH 3 to
>> use of the INPUT_WINE flag as implicit way to inform of injected
>> messages.
>>
>> Supersedes: 201308-201310
>>
>> dlls/user32/input.c | 18 +++++++++++
>> dlls/user32/tests/input.c | 63 +++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 81 insertions(+)
>>
>> diff --git a/dlls/user32/input.c b/dlls/user32/input.c
>> index e06f8b4413e..e9a74f177b9 100644
>> --- a/dlls/user32/input.c
>> +++ b/dlls/user32/input.c
>> @@ -182,6 +182,24 @@ UINT WINAPI SendInput( UINT count, LPINPUT
>> inputs, int size )
>> UINT i;
>> NTSTATUS status;
>> + if (size != sizeof(INPUT))
>> + {
>> + SetLastError( ERROR_INVALID_PARAMETER );
>> + return 0;
>> + }
>> +
>> + if (!count)
>> + {
>> + SetLastError( ERROR_INVALID_PARAMETER );
>> + return 0;
>> + }
>> +
>> + if (!inputs)
>> + {
>> + SetLastError( ERROR_NOACCESS );
>> + return 0;
>> + }
>> +
>> for (i = 0; i < count; i++)
>> {
>> if (inputs[i].type == INPUT_MOUSE)
>> diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c
>> index 9d75daa0bd5..7e8829ce375 100644
>> --- a/dlls/user32/tests/input.c
>> +++ b/dlls/user32/tests/input.c
>> @@ -4118,6 +4118,68 @@ static void
>> test_UnregisterDeviceNotification(void)
>> ok(ret == FALSE, "Unregistering NULL Device Notification
>> returned: %d\n", ret);
>> }
>> +static void test_SendInput(void)
>> +{
>> + INPUT input[16];
>> + UINT res, i;
>> + HWND hwnd;
>> +
>> + hwnd = CreateWindowW( L"static", L"test", WS_OVERLAPPED, 0, 0,
>> 100, 100, 0, 0, 0, 0 );
>> + ok(hwnd != 0, "CreateWindowW failed\n");
>> +
>> + ShowWindow( hwnd, SW_SHOWNORMAL );
>> + UpdateWindow( hwnd );
>> + SetForegroundWindow( hwnd );
>> + SetFocus( hwnd );
>> + empty_message_queue();
>> +
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(0, NULL, 0);
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(1, NULL, 0);
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(1, NULL, sizeof(*input));
>> + ok(res == 0 && (GetLastError() == ERROR_NOACCESS ||
>> GetLastError() == ERROR_INVALID_PARAMETER), "SendInput returned %u,
>> error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(0, input, sizeof(*input));
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(0, NULL, sizeof(*input));
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> +
>> + memset(input, 0, sizeof(input));
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(1, input, sizeof(*input));
>> + ok(res == 1 && GetLastError() == 0xdeadbeef, "SendInput returned
>> %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(16, input, sizeof(*input));
>> + ok(res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned
>> %u, error %#x\n", res, GetLastError());
>> +
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(1, input, 0);
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(1, input, sizeof(*input) + 1);
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(1, input, sizeof(*input) - 1);
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> +
>> + for (i = 0; i < ARRAY_SIZE(input); ++i) input[i].type =
>> INPUT_KEYBOARD;
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(16, input, offsetof(INPUT, ki) +
>> sizeof(KEYBDINPUT));
>> + ok(res == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
>> "SendInput returned %u, error %#x\n", res, GetLastError());
>> + SetLastError(0xdeadbeef);
>> + res = SendInput(16, input, sizeof(*input));
>> + ok(res == 16 && GetLastError() == 0xdeadbeef, "SendInput returned
>> %u, error %#x\n", res, GetLastError());
>> + empty_message_queue();
>> +
>> + trace("done\n");
>> + DestroyWindow(hwnd);
>> +}
>> +
>> START_TEST(input)
>> {
>> char **argv;
>> @@ -4140,6 +4202,7 @@ START_TEST(input)
>> return;
>> }
>> + test_SendInput();
>> test_Input_blackbox();
>> test_Input_whitebox();
>> test_Input_unicode();
>>
>
> So, should I keep the custom __wine_send_input entry point and extend it
> to accept a dynamically sized structure instead?
I'm not completely sure what is wrong here, but it I understand it's not
liked very much. I can see that patch 3 has an issue where we probably
shouldn't call update_mouse_coords for internal messages.
Anyway it should be possible to send HID reports without this and, I
think, only using the current and public Win32 structures. My current
plan is the following, and although I haven't implemented it fully yet I
think it could work:
- Use __wine_send_input to send all HID input, using an INPUT structure,
with INPUT_HARDWARE type and WM_INPUT / WM_INPUT_DEVICE_CHANGE uMsg.
- For WM_INPUT message, it'd be difficult to pass a HRAWINPUT handle
as wParamL/H as afaics there's no way to convert a RAWINPUT struct or
pointer to a 32bit handle.
- For WM_INPUT_DEVICE_CHANGE, we could use the lparam as the device
index, as it's supposed to be, but we would then still miss the usage
page / usage field.
- So, in both cases, this INPUT will instead be implicitly followed in
memory with a RAWINPUT structure, that send_hardware_message will expect
for these internal / INPUT_HARDWARE / uMsg combination, and the INPUT
wParamL/H would be used as a size information (for instance).
- The RAWINPUT structure will be dynamically sized and hold the HID
report as well. In the WM_INPUT_DEVICE_CHANGE case, when a device is
added, the report will be the device descriptor report, which will
provide the device usage (page) bytes.
- Because other WM_INPUT reports won't have the device usage (page)
bytes anymore, we will instead have to keep the list of known rawinput
devices on the wineserver side, with their associated rawinput device
index, and match the hDevice field of the RAWINPUT structures to figure
their usage (page) and find interested processes.
Is that a better plan?
In all cases, I think the first two patches here are still useful to
check that we can use INPUT_HARDWARE type for internal use (and disallow
it for external use).
Cheers,
--
Rémi Bernon <rbernon at codeweavers.com>
More information about the wine-devel
mailing list