mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-07-09 16:55:10 +03:00
Compare commits
10 Commits
1c2188f80b
...
8aeafd5401
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8aeafd5401 | ||
|
|
e264283bf2 | ||
|
|
1053b53b57 | ||
|
|
233eb49e4d | ||
|
|
c974514710 | ||
|
|
52ff648411 | ||
|
|
55ae167394 | ||
|
|
ba5a535891 | ||
|
|
3ab9d4e46d | ||
|
|
bb8ffef7f0 |
85
.github/workflows/wf-cliprdr-ci.yml
vendored
85
.github/workflows/wf-cliprdr-ci.yml
vendored
@@ -1,85 +0,0 @@
|
||||
name: wf-cliprdr CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "libs/clipboard/src/windows/**"
|
||||
- "tests/test_invariant_wf_cliprdr.c"
|
||||
- ".github/workflows/wf-cliprdr-ci.yml"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "libs/clipboard/src/windows/**"
|
||||
- "tests/test_invariant_wf_cliprdr.c"
|
||||
- ".github/workflows/wf-cliprdr-ci.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: wf_cliprdr invariant test
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Setup vcpkg with GitHub Actions binary cache
|
||||
uses: lukka/run-vcpkg@b1a0dd252f06b9e25b3c022a9a03bd7a427fb6a2 # v11
|
||||
with:
|
||||
vcpkgDirectory: C:\vcpkg
|
||||
doNotCache: false
|
||||
|
||||
- name: Install vcpkg dependency
|
||||
shell: pwsh
|
||||
run: |
|
||||
& "$env:VCPKG_ROOT\vcpkg.exe" install check:x64-windows --classic --x-install-root="$env:VCPKG_ROOT\installed"
|
||||
|
||||
- name: Build test
|
||||
shell: pwsh
|
||||
run: |
|
||||
$testRoot = Join-Path $env:GITHUB_WORKSPACE 'build\wf-cliprdr'
|
||||
New-Item -ItemType Directory -Force $testRoot | Out-Null
|
||||
|
||||
$testSource = (($env:GITHUB_WORKSPACE -replace '\\', '/') + '/tests/test_invariant_wf_cliprdr.c')
|
||||
$cmakeLists = @(
|
||||
'cmake_minimum_required(VERSION 3.20)'
|
||||
'project(test_invariant_wf_cliprdr C)'
|
||||
''
|
||||
'set(CMAKE_C_STANDARD 11)'
|
||||
'set(CMAKE_C_STANDARD_REQUIRED ON)'
|
||||
'set(CMAKE_C_EXTENSIONS OFF)'
|
||||
''
|
||||
'find_package(check CONFIG REQUIRED)'
|
||||
''
|
||||
'add_executable(test_invariant_wf_cliprdr'
|
||||
' "TEST_SOURCE"'
|
||||
')'
|
||||
''
|
||||
'target_link_libraries(test_invariant_wf_cliprdr PRIVATE'
|
||||
' $<$<TARGET_EXISTS:Check::check>:Check::check>'
|
||||
' $<$<NOT:$<TARGET_EXISTS:Check::check>>:Check::checkShared>'
|
||||
')'
|
||||
) -join [Environment]::NewLine
|
||||
$cmakeLists.Replace('TEST_SOURCE', $testSource) | Set-Content -NoNewline (Join-Path $testRoot 'CMakeLists.txt')
|
||||
|
||||
cmake -S $testRoot -B (Join-Path $testRoot 'out') -G "Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows
|
||||
cmake --build (Join-Path $testRoot 'out') --config Release
|
||||
|
||||
- name: Run test
|
||||
shell: pwsh
|
||||
run: .\build\wf-cliprdr\out\Release\test_invariant_wf_cliprdr.exe
|
||||
@@ -232,6 +232,7 @@ struct _CliprdrStream
|
||||
FILEDESCRIPTORW m_Dsc;
|
||||
void *m_pData;
|
||||
UINT32 m_connID;
|
||||
UINT32 m_streamId; // unique CLIPRDR streamId; avoids leaking a heap pointer
|
||||
};
|
||||
typedef struct _CliprdrStream CliprdrStream;
|
||||
|
||||
@@ -281,22 +282,13 @@ struct wf_clipboard
|
||||
LPDATAOBJECT data_obj;
|
||||
HANDLE data_obj_mutex;
|
||||
|
||||
// The req_f* fields are a single-slot rendezvous assuming one outstanding
|
||||
// file-contents request at a time. req_f_mutex guards the req_fdata/req_fsize
|
||||
// hand-off between the channel thread and the explorer-thread consumers; it must
|
||||
// not be held across a blocking wait and does not serialize whole requests.
|
||||
ULONG req_fsize;
|
||||
char *req_fdata;
|
||||
HANDLE req_fevent;
|
||||
BOOL req_f_received;
|
||||
// Distinguishes a successful zero-byte response (EOF) from a failed one; both
|
||||
// leave req_fdata NULL.
|
||||
BOOL req_f_response_ok;
|
||||
// Bytes asked for by the last request; a response claiming more is rejected.
|
||||
ULONG req_f_size_requested;
|
||||
// Stream ID of the last request; responses for another stream are ignored.
|
||||
UINT32 req_f_stream_id_expected;
|
||||
HANDLE req_f_mutex;
|
||||
UINT32 req_f_conn_id_expected; // connID of the outstanding request
|
||||
UINT32 req_f_stream_id_expected; // streamId of the outstanding request; responses for another are dropped
|
||||
LONG req_f_stream_id_seq; // source of unique per-stream ids
|
||||
|
||||
size_t nFiles;
|
||||
size_t file_array_size;
|
||||
@@ -327,7 +319,7 @@ static UINT32 get_remote_format_id(wfClipboard *clipboard, UINT32 local_format);
|
||||
static UINT cliprdr_send_data_request(UINT32 connID, wfClipboard *clipboard, UINT32 format);
|
||||
static UINT cliprdr_send_lock(wfClipboard *clipboard);
|
||||
static UINT cliprdr_send_unlock(wfClipboard *clipboard);
|
||||
static UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, const void *streamid,
|
||||
static UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, UINT32 streamId,
|
||||
ULONG index, UINT32 flag, DWORD positionhigh,
|
||||
DWORD positionlow, ULONG request);
|
||||
|
||||
@@ -407,30 +399,10 @@ static ULONG STDMETHODCALLTYPE CliprdrStream_Release(IStream *This)
|
||||
}
|
||||
}
|
||||
|
||||
// Take ownership of the pending file-contents response buffer, clearing the shared
|
||||
// slot under req_f_mutex. The caller must free the returned buffer.
|
||||
static void take_req_fdata(wfClipboard *clipboard, char **data, ULONG *size)
|
||||
{
|
||||
DWORD wait = WaitForSingleObject(clipboard->req_f_mutex, INFINITE);
|
||||
if (wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED)
|
||||
{
|
||||
*data = NULL;
|
||||
*size = 0;
|
||||
return;
|
||||
}
|
||||
*data = clipboard->req_fdata;
|
||||
*size = clipboard->req_fsize;
|
||||
clipboard->req_fdata = NULL;
|
||||
clipboard->req_fsize = 0;
|
||||
ReleaseMutex(clipboard->req_f_mutex);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE CliprdrStream_Read(IStream *This, void *pv, ULONG cb,
|
||||
ULONG *pcbRead)
|
||||
{
|
||||
int ret;
|
||||
ULONG req_size = 0;
|
||||
char *req_data = NULL;
|
||||
UINT ret;
|
||||
CliprdrStream *instance = (CliprdrStream *)This;
|
||||
wfClipboard *clipboard;
|
||||
|
||||
@@ -443,45 +415,42 @@ static HRESULT STDMETHODCALLTYPE CliprdrStream_Read(IStream *This, void *pv, ULO
|
||||
if (instance->m_lOffset.QuadPart >= instance->m_lSize.QuadPart)
|
||||
return S_FALSE;
|
||||
|
||||
ret = cliprdr_send_request_filecontents(clipboard, instance->m_connID, (void *)This, instance->m_lIndex,
|
||||
ret = cliprdr_send_request_filecontents(clipboard, instance->m_connID, instance->m_streamId, instance->m_lIndex,
|
||||
FILECONTENTS_RANGE, instance->m_lOffset.HighPart,
|
||||
instance->m_lOffset.LowPart, cb);
|
||||
|
||||
take_req_fdata(clipboard, &req_data, &req_size);
|
||||
|
||||
if (ret != CHANNEL_RC_OK)
|
||||
{
|
||||
free(req_data);
|
||||
free(clipboard->req_fdata);
|
||||
clipboard->req_fdata = NULL;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// A response larger than requested must never overflow `pv`.
|
||||
if (req_size > cb)
|
||||
if (clipboard->req_fsize > cb)
|
||||
{
|
||||
free(req_data);
|
||||
free(clipboard->req_fdata);
|
||||
clipboard->req_fdata = NULL;
|
||||
return STG_E_READFAULT;
|
||||
}
|
||||
|
||||
// A successful request may legitimately return 0 bytes (EOF).
|
||||
if (req_size > 0)
|
||||
if (clipboard->req_fdata)
|
||||
{
|
||||
if (!req_data)
|
||||
return E_FAIL;
|
||||
CopyMemory(pv, req_data, req_size);
|
||||
CopyMemory(pv, clipboard->req_fdata, clipboard->req_fsize);
|
||||
free(clipboard->req_fdata);
|
||||
clipboard->req_fdata = NULL;
|
||||
}
|
||||
free(req_data);
|
||||
|
||||
*pcbRead = req_size;
|
||||
*pcbRead = clipboard->req_fsize;
|
||||
// Check overflow, can not be a real case
|
||||
if ((instance->m_lOffset.QuadPart + req_size) < instance->m_lOffset.QuadPart) {
|
||||
if ((instance->m_lOffset.QuadPart + clipboard->req_fsize) < instance->m_lOffset.QuadPart) {
|
||||
// It's better to crash to release the explorer.exe
|
||||
// This is a critical error, because the explorer is waiting for the data
|
||||
// and the m_lOffset is wrong(overflowed)
|
||||
return S_FALSE;
|
||||
}
|
||||
instance->m_lOffset.QuadPart += req_size;
|
||||
instance->m_lOffset.QuadPart += clipboard->req_fsize;
|
||||
|
||||
if (req_size < cb)
|
||||
if (clipboard->req_fsize < cb)
|
||||
return S_FALSE;
|
||||
|
||||
return S_OK;
|
||||
@@ -637,8 +606,6 @@ static CliprdrStream *CliprdrStream_New(UINT32 connID, ULONG index, void *pData,
|
||||
IStream *iStream = NULL;
|
||||
BOOL success = FALSE;
|
||||
BOOL isDir = FALSE;
|
||||
char *req_data = NULL;
|
||||
ULONG req_sz = 0;
|
||||
CliprdrStream *instance = NULL;
|
||||
wfClipboard *clipboard = (wfClipboard *)pData;
|
||||
|
||||
@@ -676,6 +643,7 @@ static CliprdrStream *CliprdrStream_New(UINT32 connID, ULONG index, void *pData,
|
||||
instance->m_pData = pData;
|
||||
instance->m_lOffset.QuadPart = 0;
|
||||
instance->m_connID = connID;
|
||||
instance->m_streamId = (UINT32)InterlockedIncrement(&clipboard->req_f_stream_id_seq);
|
||||
|
||||
if (instance->m_Dsc.dwFlags & FD_ATTRIBUTES)
|
||||
{
|
||||
@@ -686,28 +654,31 @@ static CliprdrStream *CliprdrStream_New(UINT32 connID, ULONG index, void *pData,
|
||||
if (((instance->m_Dsc.dwFlags & FD_FILESIZE) == 0) && !isDir)
|
||||
{
|
||||
/* get content size of this stream */
|
||||
if (cliprdr_send_request_filecontents(clipboard, instance->m_connID, (void *)instance,
|
||||
if (cliprdr_send_request_filecontents(clipboard, instance->m_connID, instance->m_streamId,
|
||||
instance->m_lIndex, FILECONTENTS_SIZE, 0, 0,
|
||||
8) == CHANNEL_RC_OK)
|
||||
{
|
||||
success = TRUE;
|
||||
}
|
||||
|
||||
take_req_fdata(clipboard, &req_data, &req_sz);
|
||||
|
||||
if (req_data != NULL && req_sz >= sizeof(LONGLONG))
|
||||
if (clipboard->req_fdata != NULL && clipboard->req_fsize >= sizeof(LONGLONG))
|
||||
{
|
||||
LONGLONG sz = 0;
|
||||
CopyMemory(&sz, req_data, sizeof(sz));
|
||||
instance->m_lSize.QuadPart = sz;
|
||||
CopyMemory(&sz, clipboard->req_fdata, sizeof(sz));
|
||||
if (sz < 0)
|
||||
success = FALSE;
|
||||
else
|
||||
instance->m_lSize.QuadPart = sz;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The size probe must return at least sizeof(LONGLONG) bytes.
|
||||
success = FALSE;
|
||||
}
|
||||
|
||||
free(req_data);
|
||||
if (clipboard->req_fdata)
|
||||
{
|
||||
free(clipboard->req_fdata);
|
||||
clipboard->req_fdata = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
instance->m_lSize.QuadPart =
|
||||
@@ -1726,8 +1697,7 @@ UINT try_reset_event(HANDLE event)
|
||||
}
|
||||
}
|
||||
|
||||
UINT wait_response_event(UINT32 connID, wfClipboard *clipboard, HANDLE event, BOOL* recvedFlag,
|
||||
void **data, const BOOL *responseSucceeded)
|
||||
UINT wait_response_event(UINT32 connID, wfClipboard *clipboard, HANDLE event, BOOL* recvedFlag, void **data)
|
||||
{
|
||||
UINT rc = ERROR_SUCCESS;
|
||||
clipboard->context->IsStopped = FALSE;
|
||||
@@ -1767,8 +1737,7 @@ UINT wait_response_event(UINT32 connID, wfClipboard *clipboard, HANDLE event, BO
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if ((responseSucceeded && !*responseSucceeded) ||
|
||||
(!responseSucceeded && (*data) == NULL))
|
||||
if ((*data) == NULL)
|
||||
{
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
@@ -1829,11 +1798,10 @@ static UINT cliprdr_send_data_request(UINT32 connID, wfClipboard *clipboard, UIN
|
||||
return rc;
|
||||
}
|
||||
|
||||
return wait_response_event(connID, clipboard, clipboard->formatDataRespEvent,
|
||||
&clipboard->formatDataRespReceived, &clipboard->hmem, NULL);
|
||||
return wait_response_event(connID, clipboard, clipboard->formatDataRespEvent, &clipboard->formatDataRespReceived, &clipboard->hmem);
|
||||
}
|
||||
|
||||
UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, const void *streamid, ULONG index,
|
||||
static UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, UINT32 streamId, ULONG index,
|
||||
UINT32 flag, DWORD positionhigh, DWORD positionlow,
|
||||
ULONG nreq)
|
||||
{
|
||||
@@ -1849,15 +1817,11 @@ UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, co
|
||||
return rc;
|
||||
}
|
||||
clipboard->req_f_received = FALSE;
|
||||
clipboard->req_f_response_ok = FALSE;
|
||||
clipboard->req_f_size_requested = nreq;
|
||||
clipboard->req_f_conn_id_expected = connID;
|
||||
clipboard->req_f_stream_id_expected = streamId;
|
||||
|
||||
fileContentsRequest.connID = connID;
|
||||
// streamId is `IStream*` pointer, though it is not very good on a 64-bit system.
|
||||
// But it is OK, because it is only used to check if the stream is the same in
|
||||
// `wf_cliprdr_server_file_contents_request()` function.
|
||||
fileContentsRequest.streamId = (UINT32)(ULONG_PTR)streamid;
|
||||
clipboard->req_f_stream_id_expected = fileContentsRequest.streamId;
|
||||
fileContentsRequest.streamId = streamId;
|
||||
fileContentsRequest.listIndex = index;
|
||||
fileContentsRequest.dwFlags = flag;
|
||||
fileContentsRequest.nPositionLow = positionlow;
|
||||
@@ -1871,9 +1835,7 @@ UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, co
|
||||
return rc;
|
||||
}
|
||||
|
||||
return wait_response_event(connID, clipboard, clipboard->req_fevent,
|
||||
&clipboard->req_f_received, (void **)&clipboard->req_fdata,
|
||||
&clipboard->req_f_response_ok);
|
||||
return wait_response_event(connID, clipboard, clipboard->req_fevent, &clipboard->req_f_received, (void **)&clipboard->req_fdata);
|
||||
}
|
||||
|
||||
static UINT cliprdr_send_response_filecontents(
|
||||
@@ -3080,6 +3042,7 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context,
|
||||
BOOL bIsStreamFile = TRUE;
|
||||
static LPSTREAM pStreamStc = NULL;
|
||||
static UINT32 uStreamIdStc = 0;
|
||||
static UINT32 uConnIdStc = 0;
|
||||
wfClipboard *clipboard;
|
||||
UINT rc = ERROR_INTERNAL_ERROR;
|
||||
UINT sRc;
|
||||
@@ -3153,7 +3116,8 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context,
|
||||
vFormatEtc.lindex = fileContentsRequest->listIndex;
|
||||
vFormatEtc.ptd = NULL;
|
||||
|
||||
if ((uStreamIdStc != fileContentsRequest->streamId) || !pStreamStc)
|
||||
if ((uStreamIdStc != fileContentsRequest->streamId) ||
|
||||
(uConnIdStc != fileContentsRequest->connID) || !pStreamStc)
|
||||
{
|
||||
LPENUMFORMATETC pEnumFormatEtc;
|
||||
ULONG CeltFetched;
|
||||
@@ -3184,6 +3148,7 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context,
|
||||
{
|
||||
pStreamStc = vStgMedium.pstm;
|
||||
uStreamIdStc = fileContentsRequest->streamId;
|
||||
uConnIdStc = fileContentsRequest->connID;
|
||||
bIsStreamFile = TRUE;
|
||||
}
|
||||
|
||||
@@ -3330,10 +3295,8 @@ static UINT
|
||||
wf_cliprdr_server_file_contents_response(CliprdrClientContext *context,
|
||||
const CLIPRDR_FILE_CONTENTS_RESPONSE *fileContentsResponse)
|
||||
{
|
||||
wfClipboard *clipboard = NULL;
|
||||
wfClipboard *clipboard;
|
||||
UINT rc = ERROR_INTERNAL_ERROR;
|
||||
BOOL locked = FALSE;
|
||||
DWORD wait;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -3349,26 +3312,11 @@ wf_cliprdr_server_file_contents_response(CliprdrClientContext *context,
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// A response for another stream is ignored without waking the waiter.
|
||||
if (fileContentsResponse->streamId != clipboard->req_f_stream_id_expected)
|
||||
if (fileContentsResponse->connID != clipboard->req_f_conn_id_expected ||
|
||||
fileContentsResponse->streamId != clipboard->req_f_stream_id_expected)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
// WAIT_ABANDONED still means the mutex is held; WAIT_FAILED means it is not,
|
||||
// so it must not be released.
|
||||
wait = WaitForSingleObject(clipboard->req_f_mutex, INFINITE);
|
||||
locked = (wait == WAIT_OBJECT_0 || wait == WAIT_ABANDONED);
|
||||
if (!locked)
|
||||
{
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Free any leftover buffer from a prior request that was never consumed.
|
||||
free(clipboard->req_fdata);
|
||||
clipboard->req_fsize = 0;
|
||||
clipboard->req_fdata = NULL;
|
||||
clipboard->req_f_response_ok = FALSE;
|
||||
|
||||
if (fileContentsResponse->msgFlags != CB_RESPONSE_OK)
|
||||
{
|
||||
@@ -3376,31 +3324,17 @@ wf_cliprdr_server_file_contents_response(CliprdrClientContext *context,
|
||||
break;
|
||||
}
|
||||
|
||||
// Reject a response claiming more data than was requested.
|
||||
if (fileContentsResponse->cbRequested > clipboard->req_f_size_requested)
|
||||
{
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileContentsResponse->cbRequested == 0)
|
||||
{
|
||||
clipboard->req_f_response_ok = TRUE;
|
||||
rc = CHANNEL_RC_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!fileContentsResponse->requestedData)
|
||||
{
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
clipboard->req_fsize = fileContentsResponse->cbRequested;
|
||||
/*
|
||||
* Keep the zero-size allocation: supported Windows builds use the Microsoft
|
||||
* CRT, where malloc(0) returns a valid pointer. wait_response_event() also
|
||||
* uses a non-NULL req_fdata to recognize a successful zero-byte response.
|
||||
* The Rust FFI derives requestedData and cbRequested from the same Vec, so a
|
||||
* nonzero length cannot have a NULL data pointer on the normal call path.
|
||||
*/
|
||||
clipboard->req_fdata = (char *)malloc(fileContentsResponse->cbRequested);
|
||||
if (!clipboard->req_fdata)
|
||||
{
|
||||
clipboard->req_fsize = 0;
|
||||
rc = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
@@ -3408,14 +3342,10 @@ wf_cliprdr_server_file_contents_response(CliprdrClientContext *context,
|
||||
CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData,
|
||||
fileContentsResponse->cbRequested);
|
||||
|
||||
clipboard->req_f_response_ok = TRUE;
|
||||
rc = CHANNEL_RC_OK;
|
||||
} while (0);
|
||||
|
||||
if (locked)
|
||||
ReleaseMutex(clipboard->req_f_mutex);
|
||||
|
||||
if (clipboard && !SetEvent(clipboard->req_fevent))
|
||||
if (!SetEvent(clipboard->req_fevent))
|
||||
{
|
||||
// If failed to set event, set flag to indicate the event is received.
|
||||
DEBUG_CLIPRDR("wf_cliprdr_server_file_contents_response(), SetEvent failed with 0x%x", GetLastError());
|
||||
@@ -3487,9 +3417,6 @@ BOOL wf_cliprdr_init(wfClipboard *clipboard, CliprdrClientContext *cliprdr)
|
||||
goto error;
|
||||
clipboard->req_f_received = FALSE;
|
||||
|
||||
if (!(clipboard->req_f_mutex = CreateMutex(NULL, FALSE, NULL)))
|
||||
goto error;
|
||||
|
||||
if (!(clipboard->thread = CreateThread(NULL, 0, cliprdr_thread_func, clipboard, 0, NULL)))
|
||||
goto error;
|
||||
|
||||
@@ -3562,17 +3489,6 @@ BOOL wf_cliprdr_uninit(wfClipboard *clipboard, CliprdrClientContext *cliprdr)
|
||||
if (clipboard->req_fevent)
|
||||
CloseHandle(clipboard->req_fevent);
|
||||
|
||||
if (clipboard->req_f_mutex)
|
||||
{
|
||||
// Reclaim any response buffer that no consumer ever took, so it is not leaked.
|
||||
char *leftover = NULL;
|
||||
ULONG leftover_sz = 0;
|
||||
take_req_fdata(clipboard, &leftover, &leftover_sz);
|
||||
free(leftover);
|
||||
|
||||
CloseHandle(clipboard->req_f_mutex);
|
||||
}
|
||||
|
||||
clear_file_array(clipboard);
|
||||
clear_format_map(clipboard);
|
||||
free(clipboard->format_mappings);
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../libs/clipboard/src/windows/wf_cliprdr.c"
|
||||
|
||||
static SIZE_T descriptor_size(UINT count)
|
||||
{
|
||||
return offsetof(FILEGROUPDESCRIPTORW, fgd) + (SIZE_T)count * sizeof(FILEDESCRIPTORW);
|
||||
}
|
||||
|
||||
START_TEST(test_descriptor_size_rejects_buffer_smaller_than_header)
|
||||
{
|
||||
ck_assert_int_eq(wf_cliprdr_file_group_descriptor_size_valid(0, 1), FALSE);
|
||||
ck_assert_int_eq(wf_cliprdr_file_group_descriptor_size_valid(
|
||||
offsetof(FILEGROUPDESCRIPTORW, fgd) - 1, 1),
|
||||
FALSE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_descriptor_size_rejects_zero_items)
|
||||
{
|
||||
ck_assert_int_eq(
|
||||
wf_cliprdr_file_group_descriptor_size_valid(offsetof(FILEGROUPDESCRIPTORW, fgd), 0),
|
||||
FALSE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_descriptor_size_accepts_max_stream_count)
|
||||
{
|
||||
ck_assert_int_eq(wf_cliprdr_file_group_descriptor_size_valid(
|
||||
descriptor_size(WF_CLIPRDR_MAX_STREAMS), WF_CLIPRDR_MAX_STREAMS),
|
||||
TRUE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_descriptor_size_rejects_stream_count_above_limit)
|
||||
{
|
||||
ck_assert_int_eq(wf_cliprdr_file_group_descriptor_size_valid(
|
||||
descriptor_size(WF_CLIPRDR_MAX_STREAMS), WF_CLIPRDR_MAX_STREAMS + 1),
|
||||
FALSE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_descriptor_size_rejects_truncated_descriptor_array)
|
||||
{
|
||||
ck_assert_int_eq(wf_cliprdr_file_group_descriptor_size_valid(descriptor_size(2) - 1, 2),
|
||||
FALSE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_descriptor_size_rejects_extreme_count)
|
||||
{
|
||||
ck_assert_int_eq(wf_cliprdr_file_group_descriptor_size_valid((SIZE_T)-1, (UINT)-1),
|
||||
FALSE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static UINT test_client_file_contents_request(
|
||||
CliprdrClientContext *context,
|
||||
const CLIPRDR_FILE_CONTENTS_REQUEST *request)
|
||||
{
|
||||
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
|
||||
|
||||
ck_assert_int_eq(request->haveClipDataId, FALSE);
|
||||
ck_assert_int_eq(request->clipDataId, 0);
|
||||
response.msgFlags = CB_RESPONSE_OK;
|
||||
response.streamId = request->streamId;
|
||||
return wf_cliprdr_server_file_contents_response(context, &response);
|
||||
}
|
||||
|
||||
START_TEST(test_file_contents_request_initializes_optional_fields)
|
||||
{
|
||||
wfClipboard clipboard = { 0 };
|
||||
CliprdrClientContext context = { 0 };
|
||||
UINT rc;
|
||||
|
||||
clipboard.context = &context;
|
||||
clipboard.req_f_mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
clipboard.req_fevent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
context.Custom = &clipboard;
|
||||
context.ResponseWaitTimeoutSecs = 1;
|
||||
context.ClientFileContentsRequest = test_client_file_contents_request;
|
||||
|
||||
ck_assert_ptr_nonnull(clipboard.req_f_mutex);
|
||||
ck_assert_ptr_nonnull(clipboard.req_fevent);
|
||||
rc = cliprdr_send_request_filecontents(&clipboard, 1, (const void *)(ULONG_PTR)7,
|
||||
0, FILECONTENTS_SIZE, 0, 0, 0);
|
||||
ck_assert_int_eq(rc, CHANNEL_RC_OK);
|
||||
ck_assert_int_eq(clipboard.req_f_stream_id_expected, 7);
|
||||
|
||||
CloseHandle(clipboard.req_fevent);
|
||||
CloseHandle(clipboard.req_f_mutex);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_file_contents_response_validates_data)
|
||||
{
|
||||
wfClipboard clipboard = { 0 };
|
||||
CliprdrClientContext context = { 0 };
|
||||
CLIPRDR_FILE_CONTENTS_RESPONSE response = { 0 };
|
||||
UINT rc;
|
||||
|
||||
clipboard.context = &context;
|
||||
clipboard.req_f_mutex = CreateMutex(NULL, FALSE, NULL);
|
||||
clipboard.req_fevent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
context.Custom = &clipboard;
|
||||
context.ResponseWaitTimeoutSecs = 1;
|
||||
response.msgFlags = CB_RESPONSE_OK;
|
||||
response.cbRequested = 0;
|
||||
clipboard.req_f_stream_id_expected = 7;
|
||||
|
||||
ck_assert_ptr_nonnull(clipboard.req_f_mutex);
|
||||
ck_assert_ptr_nonnull(clipboard.req_fevent);
|
||||
|
||||
response.streamId = 8;
|
||||
ck_assert_int_eq(wf_cliprdr_server_file_contents_response(&context, &response),
|
||||
CHANNEL_RC_OK);
|
||||
ck_assert_int_eq(WaitForSingleObject(clipboard.req_fevent, 0), WAIT_TIMEOUT);
|
||||
ck_assert_ptr_null(clipboard.req_fdata);
|
||||
ck_assert_int_eq(clipboard.req_fsize, 0);
|
||||
ck_assert_int_eq(clipboard.req_f_response_ok, FALSE);
|
||||
|
||||
response.streamId = 7;
|
||||
ck_assert_int_eq(wf_cliprdr_server_file_contents_response(&context, &response),
|
||||
CHANNEL_RC_OK);
|
||||
ck_assert_ptr_null(clipboard.req_fdata);
|
||||
ck_assert_int_eq(clipboard.req_fsize, 0);
|
||||
ck_assert_int_eq(clipboard.req_f_response_ok, TRUE);
|
||||
|
||||
rc = wait_response_event(0, &clipboard, clipboard.req_fevent,
|
||||
&clipboard.req_f_received, (void **)&clipboard.req_fdata,
|
||||
&clipboard.req_f_response_ok);
|
||||
ck_assert_int_eq(rc, CHANNEL_RC_OK);
|
||||
|
||||
response.cbRequested = 1;
|
||||
clipboard.req_f_size_requested = 1;
|
||||
ck_assert_int_eq(wf_cliprdr_server_file_contents_response(&context, &response),
|
||||
ERROR_INTERNAL_ERROR);
|
||||
ck_assert_ptr_null(clipboard.req_fdata);
|
||||
ck_assert_int_eq(clipboard.req_fsize, 0);
|
||||
ck_assert_int_eq(clipboard.req_f_response_ok, FALSE);
|
||||
|
||||
rc = wait_response_event(0, &clipboard, clipboard.req_fevent,
|
||||
&clipboard.req_f_received, (void **)&clipboard.req_fdata,
|
||||
&clipboard.req_f_response_ok);
|
||||
ck_assert_int_eq(rc, ERROR_INTERNAL_ERROR);
|
||||
|
||||
CloseHandle(clipboard.req_fevent);
|
||||
CloseHandle(clipboard.req_f_mutex);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *wf_cliprdr_invariant_suite(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc_core;
|
||||
|
||||
s = suite_create("wf_cliprdr_invariants");
|
||||
tc_core = tcase_create("descriptor_size");
|
||||
|
||||
tcase_add_test(tc_core, test_descriptor_size_rejects_buffer_smaller_than_header);
|
||||
tcase_add_test(tc_core, test_descriptor_size_rejects_zero_items);
|
||||
tcase_add_test(tc_core, test_descriptor_size_accepts_max_stream_count);
|
||||
tcase_add_test(tc_core, test_descriptor_size_rejects_stream_count_above_limit);
|
||||
tcase_add_test(tc_core, test_descriptor_size_rejects_truncated_descriptor_array);
|
||||
tcase_add_test(tc_core, test_descriptor_size_rejects_extreme_count);
|
||||
tcase_add_test(tc_core, test_file_contents_request_initializes_optional_fields);
|
||||
tcase_add_test(tc_core, test_file_contents_response_validates_data);
|
||||
|
||||
suite_add_tcase(s, tc_core);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int number_failed;
|
||||
Suite *s;
|
||||
SRunner *sr;
|
||||
|
||||
s = wf_cliprdr_invariant_suite();
|
||||
sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
number_failed = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Reference in New Issue
Block a user