[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1054915: bookworm-pu: package freerdp2/2.11.2+dfsg1-1~deb12u1



Hi, 

Am Mon, Dec 30, 2024 at 08:51:17PM +0100 schrieb Salvatore Bonaccorso:
> Hi Tobi,
> 
> On Tue, Aug 27, 2024 at 09:51:41PM +0200, Salvatore Bonaccorso wrote:
> > Hi Tobi,
> > 
> > On Sat, Jun 22, 2024 at 08:46:39PM +0200, Salvatore Bonaccorso wrote:
> > > Hi Tobi,
> > > 
> > > On Wed, Feb 21, 2024 at 08:00:42AM +0000, Jonathan Wiltshire wrote:
> > > > Control: tag -1 moreinfo
> > > > 
> > > > Hi,
> > > > 
> > > > On Sat, Oct 28, 2023 at 05:58:38PM +0200, Tobias Frost wrote:
> > > > > Backporting the fixes is of course possible, but bears a significant
> > > > > risk for regression, therefor I would prefer to use the new upstream
> > > > > version, given also that upstream changes are only a few and fixing
> > > > > also a few bugs that would be nice to be fixed.
> > > > 
> > > > It's a balancing act, as always. I'm OK with new upstream releases if they
> > > > are small enough to sensibly review (or an upstream with a good trusted
> > > > history, which I don't yet have for freerdp2). If you think a new upstream
> > > > is reasonable, let's see how it looks.
> > > > 
> > > > Either way we need a source debdiff please.
> > > 
> > > Did you saw the followup question from Jonathan?
> > 
> > Friendly ping :). Note we are late for the 12.7 point release now, but
> > it still should ideally make it for 12.8.
> 
> Friendly ping (but I guess we might miss again 12.9 now for it as
> window for next point release is closing upcoming weekend already).
> 
> it would be good to see those CVEs addressed in a point release for
> bookworm.

Yes, sorry, slipped again through the cracks. I'll try / will be able to work
on this probably tomorrow and can reserve next weekend for it. (Currently in
the train back from Congress, so cannot jump into it right now)

Said that, I'll need an indication of thumbs up / down for the change
before putting significant work into it, as the patch has quite some size
(but the individual changes seems quite isolated and not lots of significant
rewrites) and I think the only sensible way is to do testing applications which
are using freerdp2, as I did for LTS/ELTS back then.

Upstream has moved to 2.11.7 in the mean time, and as upstream says it
fixes some problems found by fuzzing I think this is version we want
too. This version is in unstable already since 2024-07-15, so I
think any significant problems would have been reported since then.
On top, 2.11.7-dsfg2-4 seems to employ the upstream test suite. I've not
checked the details of the test suite in autopkgtest, but at least this sounds
promising.

The patch is a bit shy of 8000 lines.

I've attached the diff, it can be obtained from git (salsa) as well for those
prefering an UI:
https://salsa.debian.org/debian-remote-team/freerdp2/-/compare/debian%2F2.10.0+dfsg1-1...debian%2F2.11.7+dfsg1-6


diff on the upstream changelog (omitting the part fixing typos)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,97 @@
+# 2024-04-22 Version 2.11.7
+
+Noteworthy changes:
+* Backported oss-fuzz fixes
+
+For a complete and detailed change log since the last release run:
+git log 2.11.7...2.11.6
+
+# 2024-04-17 Version 2.11.6
+
+CVE:
+ CVE-2024-32041 [Low[ OutOfBound Read in zgfx_decompress_segment
+ CVE-2024-32039 [Moderate] Integer overflow & OutOfBound Write in clear_decompress_residual_data
+ CVE-2024-32040 [Low] integer underflow in nsc_rle_decode
+ CVE-2024-32458 [Low] OutOfBound Read in planar_skip_plane_rle
+ CVE-2024-32459 [Low] OutOfBound Read in ncrush_decompress
+ CVE-2024-32460 [Low] OutOfBound Read in interleaved_decompress
+
+Noteworthy changes:
+* Backported #10077
+
+For a complete and detailed change log since the last release run:
+git log 2.11.6...2.11.5
+
+# 2024-01-19 Version 2.11.5
+
+Noteworthy changes:
+* Fix integer overflow in progressive decoder
+* Update OpenSSL API usage for compatiblility with newer versions (#9747)
+* Prevent NULL dereference for single thread decoder (#9712)
+
+For a complete and detailed change log since the last release run:
+git log 2.11.5...2.11.4
+
+# 2023-12-14 Version 2.11.4
+
+Notworthy changes:
+* fix a typo in unicode commit (#9652)
+
+For a complete and detailed change log since the last release run:
+git log 2.11.4...2.11.3
+
+# 2023-12-14 Version 2.11.3
+
+Notworthy changes:
+* Disabled windows MEDIA FOUNDATION h264 decoder due to reported issues (#9469)
+* Fix issues with drive redirection (#9530,9554, #9586, #9617)
+* Use endian safe ICU string converter (#9631)
+* Improve AAC support (#9577)
+* Fix swiss german keyboard layout (#9560)
+* Enable rfx-mode:image (#9428)
+
+For a complete and detailed change log since the last release run:
+git log 2.11.3...2.11.2
+
+# 2023-09-20 Version 2.11.2
+
+Notworthy changes:
+* Backported #9378: backported wArrayList (optional) copy on insert
+* Backported #9360: backported certificate algorithm detection
+
+For a complete and detailed change log since the last release run:
+git log 2.11.2...2.11.1
+
+# 2023-09-04 Version 2.11.1
+
+Notworthy changes:
+* Backported #9356: Fix issues with order updates
+
+For a complete and detailed change log since the last release run:
+git log 2.11.1..2.11.0
+
+# 2023-08-28 Version 2.11.0
+
+Noteworthy changes:
+* Various input validation fixes
+* Added various CMake options #9317
+* LibreSSL build fixes #8709
+
+Fixed issues:
+* Backported #9233: Big endian support
+* Backported #9099: Mouse grabbing support
+* Backported #6851: wayland scrolling fix
+* Backported #8690: Update h264 to use new FFMPEG API
+* Backported #7306: early bail from update_read_window_state_order breaks protocol
+* Backported #8903: rdpecam/server: Remove wrong assertion
+* Backported #8994: bounds checks for gdi/gfx rectangles
+* Backported #9023: enforce rdpdr client side state checks
+* Backported #6331: deactivate mouse grabbing by default
+* Cherry-pick out of #9172: channels/cliprdr: Fix writing incorrect PDU type for unlock PDUs
+
 # 2023-02-16 Version 2.10.0







> Regards,
> Salvatore
diff --git a/.clang-format b/.clang-format
index 33a6584..6791025 100644
--- a/.clang-format
+++ b/.clang-format
@@ -104,7 +104,6 @@ ForEachMacros:
 ...
 Language:        ObjC
 PointerBindsToType: false
-ObjCSpaceAfterProperty: true
 SortIncludes: false
 ObjCBlockIndentWidth: 4
 ObjCSpaceAfterProperty: false
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cdca0a0..67f3437 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,9 +34,9 @@ if(NOT DEFINED FREERDP_VENDOR)
 	set(FREERDP_VENDOR 1)
 endif()
 
-set(CMAKE_COLOR_MAKEFILE ON)
-
-set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+option(CMAKE_COLOR_MAKEFILE "colorful CMake makefile" ON)
+option(CMAKE_VERBOSE_MAKEFILE "verbose CMake makefile" ON)
+option(CMAKE_POSITION_INDEPENDENT_CODE "build with position independent code (-fPIC or -fPIE)" ON)
 
 # Include our extra modules
 set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
@@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER})
 endif()
 set(WITH_LIBRARY_VERSIONING "ON")
 
-set(RAW_VERSION_STRING "2.10.0")
+set(RAW_VERSION_STRING "2.11.7")
 if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
 	file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
 elseif(USE_VERSION_FROM_GIT_TAG)
@@ -225,7 +225,7 @@ endif()
 if(MSVC)
 	include(MSVCRuntime)
 	if(NOT DEFINED MSVC_RUNTIME)
-		set(MSVC_RUNTIME "dynamic")
+		set(MSVC_RUNTIME "dynamic" CACHE STRING "MSVC runtime type [dynamic|static]")
 	endif()
 	if(MSVC_RUNTIME STREQUAL "static")
 		if(BUILD_SHARED_LIBS)
diff --git a/ChangeLog b/ChangeLog
index 17707a3..a2209be 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,97 @@
+# 2024-04-22 Version 2.11.7
+
+Noteworthy changes:
+* Backported oss-fuzz fixes
+
+For a complete and detailed change log since the last release run:
+git log 2.11.7...2.11.6
+
+# 2024-04-17 Version 2.11.6
+
+CVE:
+ CVE-2024-32041 [Low[ OutOfBound Read in zgfx_decompress_segment
+ CVE-2024-32039 [Moderate] Integer overflow & OutOfBound Write in clear_decompress_residual_data
+ CVE-2024-32040 [Low] integer underflow in nsc_rle_decode
+ CVE-2024-32458 [Low] OutOfBound Read in planar_skip_plane_rle
+ CVE-2024-32459 [Low] OutOfBound Read in ncrush_decompress
+ CVE-2024-32460 [Low] OutOfBound Read in interleaved_decompress
+
+Noteworthy changes:
+* Backported #10077
+
+For a complete and detailed change log since the last release run:
+git log 2.11.6...2.11.5
+
+# 2024-01-19 Version 2.11.5
+
+Noteworthy changes:
+* Fix integer overflow in progressive decoder
+* Update OpenSSL API usage for compatiblility with newer versions (#9747)
+* Prevent NULL dereference for single thread decoder (#9712)
+
+For a complete and detailed change log since the last release run:
+git log 2.11.5...2.11.4
+
+# 2023-12-14 Version 2.11.4
+
+Notworthy changes:
+* fix a typo in unicode commit (#9652)
+
+For a complete and detailed change log since the last release run:
+git log 2.11.4...2.11.3
+
+# 2023-12-14 Version 2.11.3
+
+Notworthy changes:
+* Disabled windows MEDIA FOUNDATION h264 decoder due to reported issues (#9469)
+* Fix issues with drive redirection (#9530,9554, #9586, #9617)
+* Use endian safe ICU string converter (#9631)
+* Improve AAC support (#9577)
+* Fix swiss german keyboard layout (#9560)
+* Enable rfx-mode:image (#9428)
+
+For a complete and detailed change log since the last release run:
+git log 2.11.3...2.11.2
+
+# 2023-09-20 Version 2.11.2
+
+Notworthy changes:
+* Backported #9378: backported wArrayList (optional) copy on insert
+* Backported #9360: backported certificate algorithm detection
+
+For a complete and detailed change log since the last release run:
+git log 2.11.2...2.11.1
+
+# 2023-09-04 Version 2.11.1
+
+Notworthy changes:
+* Backported #9356: Fix issues with order updates
+
+For a complete and detailed change log since the last release run:
+git log 2.11.1..2.11.0
+
+# 2023-08-28 Version 2.11.0
+
+Noteworthy changes:
+* Various input validation fixes
+* Added various CMake options #9317
+* LibreSSL build fixes #8709
+
+Fixed issues:
+* Backported #9233: Big endian support
+* Backported #9099: Mouse grabbing support
+* Backported #6851: wayland scrolling fix
+* Backported #8690: Update h264 to use new FFMPEG API
+* Backported #7306: early bail from update_read_window_state_order breaks protocol
+* Backported #8903: rdpecam/server: Remove wrong assertion
+* Backported #8994: bounds checks for gdi/gfx rectangles
+* Backported #9023: enforce rdpdr client side state checks
+* Backported #6331: deactivate mouse grabbing by default
+* Cherry-pick out of #9172: channels/cliprdr: Fix writing incorrect PDU type for unlock PDUs
+
 # 2023-02-16 Version 2.10.0
 
-Notewhorth changes:
+Noteworthy changes:
 * Fix android build scripts, use CMake from SDK
 * Fix connection negotiation with mstsc/msrdc #8426
 * [ntlm]: use rfc5929 binding hash algorithm #8430
@@ -23,7 +114,7 @@ Fixed issues:
 
 # 2022-11-16 Version 2.9.0
 
-Notewhorth changes:
+Noteworthy changes:
 * Backported #8252: Support sending server redirection PDU
 * Backported #8406: Ensure X11 client cursor is never smaller 1x1
 * Backported #8403: Fixed multiple client side input validation issues
@@ -49,7 +140,7 @@ git log 2.8.1..2.9.0
 
 # 2022-10-12 Version 2.8.1
 
-Notewhorth changes:
+Noteworthy changes:
 * Fixed CVE-2022-39282
 * Fixed CVE-2022-39283
 * Added missing commit for backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable)
diff --git a/channels/cliprdr/cliprdr_common.c b/channels/cliprdr/cliprdr_common.c
index 69157ad..8472fde 100644
--- a/channels/cliprdr/cliprdr_common.c
+++ b/channels/cliprdr/cliprdr_common.c
@@ -138,7 +138,7 @@ cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockCl
 	if (!unlockClipboardData)
 		return NULL;
 
-	s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4);
+	s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4);
 
 	if (!s)
 		return NULL;
diff --git a/channels/rdpdr/client/devman.c b/channels/rdpdr/client/devman.c
index af3cdd6..841e928 100644
--- a/channels/rdpdr/client/devman.c
+++ b/channels/rdpdr/client/devman.c
@@ -41,6 +41,8 @@
 
 #include "devman.h"
 
+#define TAG CHANNELS_TAG("rdpdr.client")
+
 static void devman_device_free(void* obj)
 {
 	DEVICE* device = (DEVICE*)obj;
@@ -62,7 +64,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
 
 	if (!devman)
 	{
-		WLog_INFO(TAG, "calloc failed!");
+		WLog_Print(rdpdr->log, WLOG_INFO, "calloc failed!");
 		return NULL;
 	}
 
@@ -72,7 +74,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
 
 	if (!devman->devices)
 	{
-		WLog_INFO(TAG, "ListDictionary_New failed!");
+		WLog_Print(rdpdr->log, WLOG_INFO, "ListDictionary_New failed!");
 		free(devman);
 		return NULL;
 	}
diff --git a/channels/rdpdr/client/irp.c b/channels/rdpdr/client/irp.c
index b818b67..b127810 100644
--- a/channels/rdpdr/client/irp.c
+++ b/channels/rdpdr/client/irp.c
@@ -76,13 +76,13 @@ static UINT irp_complete(IRP* irp)
 	return irp_free(irp);
 }
 
-IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
+IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error)
 {
 	IRP* irp;
 	DEVICE* device;
 	UINT32 DeviceId;
 
-	if (Stream_GetRemainingLength(s) < 20)
+	if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20))
 	{
 		if (error)
 			*error = ERROR_INVALID_DATA;
@@ -94,7 +94,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
 
 	if (!device)
 	{
-		WLog_WARN(TAG, "devman_get_device_by_id failed!");
+		WLog_Print(log, WLOG_WARN, "devman_get_device_by_id failed!");
 		if (error)
 			*error = CHANNEL_RC_OK;
 
@@ -105,7 +105,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
 
 	if (!irp)
 	{
-		WLog_ERR(TAG, "_aligned_malloc failed!");
+		WLog_Print(log, WLOG_ERROR, "_aligned_malloc failed!");
 		if (error)
 			*error = CHANNEL_RC_NO_MEMORY;
 		return NULL;
@@ -125,7 +125,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
 	irp->output = Stream_New(NULL, 256);
 	if (!irp->output)
 	{
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
 		_aligned_free(irp);
 		if (error)
 			*error = CHANNEL_RC_NO_MEMORY;
diff --git a/channels/rdpdr/client/irp.h b/channels/rdpdr/client/irp.h
index 17d75ac..46a67be 100644
--- a/channels/rdpdr/client/irp.h
+++ b/channels/rdpdr/client/irp.h
@@ -21,8 +21,9 @@
 #ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
 #define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H
 
+#include <winpr/wlog.h>
 #include "rdpdr_main.h"
 
-IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error);
+IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error);
 
 #endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
diff --git a/channels/rdpdr/client/rdpdr_capabilities.c b/channels/rdpdr/client/rdpdr_capabilities.c
index 97c876b..0e6baf8 100644
--- a/channels/rdpdr/client/rdpdr_capabilities.c
+++ b/channels/rdpdr/client/rdpdr_capabilities.c
@@ -35,6 +35,8 @@
 #include "rdpdr_main.h"
 #include "rdpdr_capabilities.h"
 
+#define RDPDR_CAPABILITY_HEADER_LENGTH 8
+
 /* Output device redirection capability set header */
 static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength,
                                       UINT32 version)
@@ -48,39 +50,59 @@ static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16
 static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
 {
 	WINPR_UNUSED(rdpdr);
-	rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, 44, GENERAL_CAPABILITY_VERSION_02);
-	Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */
-	Stream_Write_UINT32(s, 0); /* osVersion, unused and must be set to zero */
-	Stream_Write_UINT16(s, 1); /* protocolMajorVersion, must be set to 1 */
-	Stream_Write_UINT16(s, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinorVersion */
-	Stream_Write_UINT32(s, 0x0000FFFF);                  /* ioCode1 */
-	Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */
-	Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU |
-	                           RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */
-	Stream_Write_UINT32(s, ENABLE_ASYNCIO);              /* extraFlags1 */
-	Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */
+
+	const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1;
+	const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2;
+
+	rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36,
+	                          GENERAL_CAPABILITY_VERSION_02);
+	Stream_Write_UINT32(s, rdpdr->clientOsType);    /* osType, ignored on receipt */
+	Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */
+	Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */
+	Stream_Write_UINT16(s, rdpdr->clientVersionMinor); /* protocolMinorVersion */
+	Stream_Write_UINT32(s, ioCode1);                   /* ioCode1 */
+	Stream_Write_UINT32(s, ioCode2); /* ioCode2, must be set to zero, reserved for future use */
+	Stream_Write_UINT32(s, rdpdr->clientExtendedPDU); /* extendedPDU */
+	Stream_Write_UINT32(s, rdpdr->clientExtraFlags1); /* extraFlags1 */
+	Stream_Write_UINT32(
+	    s,
+	    rdpdr->clientExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
 	Stream_Write_UINT32(
-	    s, 0); /* SpecialTypeDeviceCap, number of special devices to be redirected before logon */
+	    s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
+	                                              be redirected before logon */
 }
 
 /* Process device direction general capability set */
-static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
+static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
 {
-	UINT16 capabilityLength;
 	WINPR_UNUSED(rdpdr);
 
-	if (Stream_GetRemainingLength(s) < 2)
-		return ERROR_INVALID_DATA;
-
-	Stream_Read_UINT16(s, capabilityLength);
-
-	if (capabilityLength < 4)
+	if (capabilityLength != 36)
+	{
+		WLog_Print(rdpdr->log, WLOG_ERROR,
+		           "CAP_GENERAL_TYPE::CapabilityLength expected 36, got %" PRIu32,
+		           capabilityLength);
 		return ERROR_INVALID_DATA;
+	}
 
-	if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
 		return ERROR_INVALID_DATA;
 
-	Stream_Seek(s, capabilityLength - 4U);
+	Stream_Read_UINT32(s, rdpdr->serverOsType);    /* osType, ignored on receipt */
+	Stream_Read_UINT32(s, rdpdr->serverOsVersion); /* osVersion, unused and must be set to zero */
+	Stream_Read_UINT16(s, rdpdr->serverVersionMajor); /* protocolMajorVersion, must be set to 1 */
+	Stream_Read_UINT16(s, rdpdr->serverVersionMinor); /* protocolMinorVersion */
+	Stream_Read_UINT32(s, rdpdr->serverIOCode1);      /* ioCode1 */
+	Stream_Read_UINT32(
+	    s, rdpdr->serverIOCode2); /* ioCode2, must be set to zero, reserved for future use */
+	Stream_Read_UINT32(s, rdpdr->serverExtendedPDU); /* extendedPDU */
+	Stream_Read_UINT32(s, rdpdr->serverExtraFlags1); /* extraFlags1 */
+	Stream_Read_UINT32(
+	    s,
+	    rdpdr->serverExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */
+	Stream_Read_UINT32(
+	    s, rdpdr->serverSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to
+	                                              be redirected before logon */
 	return CHANNEL_RC_OK;
 }
 
@@ -92,23 +114,14 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
 }
 
 /* Process printer direction capability set */
-static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
+static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
 {
-	UINT16 capabilityLength;
 	WINPR_UNUSED(rdpdr);
 
-	if (Stream_GetRemainingLength(s) < 2)
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
 		return ERROR_INVALID_DATA;
 
-	Stream_Read_UINT16(s, capabilityLength);
-
-	if (capabilityLength < 4)
-		return ERROR_INVALID_DATA;
-
-	if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
-		return ERROR_INVALID_DATA;
-
-	Stream_Seek(s, capabilityLength - 4U);
+	Stream_Seek(s, capabilityLength);
 	return CHANNEL_RC_OK;
 }
 
@@ -120,23 +133,14 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
 }
 
 /* Process port redirection capability set */
-static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
+static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
 {
-	UINT16 capabilityLength;
 	WINPR_UNUSED(rdpdr);
 
-	if (Stream_GetRemainingLength(s) < 2)
-		return ERROR_INVALID_DATA;
-
-	Stream_Read_UINT16(s, capabilityLength);
-
-	if (capabilityLength < 4U)
-		return ERROR_INVALID_DATA;
-
-	if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
 		return ERROR_INVALID_DATA;
 
-	Stream_Seek(s, capabilityLength - 4U);
+	Stream_Seek(s, capabilityLength);
 	return CHANNEL_RC_OK;
 }
 
@@ -148,23 +152,14 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
 }
 
 /* Process drive redirection capability set */
-static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
+static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
 {
-	UINT16 capabilityLength;
 	WINPR_UNUSED(rdpdr);
 
-	if (Stream_GetRemainingLength(s) < 2)
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
 		return ERROR_INVALID_DATA;
 
-	Stream_Read_UINT16(s, capabilityLength);
-
-	if (capabilityLength < 4)
-		return ERROR_INVALID_DATA;
-
-	if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
-		return ERROR_INVALID_DATA;
-
-	Stream_Seek(s, capabilityLength - 4U);
+	Stream_Seek(s, capabilityLength);
 	return CHANNEL_RC_OK;
 }
 
@@ -176,23 +171,14 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
 }
 
 /* Process smartcard redirection capability set */
-static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
+static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength)
 {
-	UINT16 capabilityLength;
 	WINPR_UNUSED(rdpdr);
 
-	if (Stream_GetRemainingLength(s) < 2)
-		return ERROR_INVALID_DATA;
-
-	Stream_Read_UINT16(s, capabilityLength);
-
-	if (capabilityLength < 4)
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength))
 		return ERROR_INVALID_DATA;
 
-	if (Stream_GetRemainingLength(s) < capabilityLength - 4U)
-		return ERROR_INVALID_DATA;
-
-	Stream_Seek(s, capabilityLength - 4U);
+	Stream_Seek(s, capabilityLength);
 	return CHANNEL_RC_OK;
 }
 
@@ -201,12 +187,14 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
 	UINT status = CHANNEL_RC_OK;
 	UINT16 i;
 	UINT16 numCapabilities;
-	UINT16 capabilityType;
 
 	if (!rdpdr || !s)
 		return CHANNEL_RC_NULL_DATA;
 
-	if (Stream_GetRemainingLength(s) < 4)
+	WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_SERVER_CAPS);
+	rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_CLIENT_CAPS);
+
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4))
 		return ERROR_INVALID_DATA;
 
 	Stream_Read_UINT16(s, numCapabilities);
@@ -214,31 +202,44 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
 
 	for (i = 0; i < numCapabilities; i++)
 	{
-		if (Stream_GetRemainingLength(s) < sizeof(UINT16))
+		UINT16 capabilityType;
+		UINT16 capabilityLength;
+		UINT32 version = 0;
+
+		if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s,
+		                                          2 * sizeof(UINT16) + sizeof(UINT32)))
 			return ERROR_INVALID_DATA;
 
 		Stream_Read_UINT16(s, capabilityType);
+		Stream_Read_UINT16(s, capabilityLength);
+		Stream_Read_UINT32(s, version);
+
+		if (capabilityLength < 8)
+		{
+			return ERROR_INVALID_DATA;
+		}
+		capabilityLength -= 8;
 
 		switch (capabilityType)
 		{
 			case CAP_GENERAL_TYPE:
-				status = rdpdr_process_general_capset(rdpdr, s);
+				status = rdpdr_process_general_capset(rdpdr, s, capabilityLength);
 				break;
 
 			case CAP_PRINTER_TYPE:
-				status = rdpdr_process_printer_capset(rdpdr, s);
+				status = rdpdr_process_printer_capset(rdpdr, s, capabilityLength);
 				break;
 
 			case CAP_PORT_TYPE:
-				status = rdpdr_process_port_capset(rdpdr, s);
+				status = rdpdr_process_port_capset(rdpdr, s, capabilityLength);
 				break;
 
 			case CAP_DRIVE_TYPE:
-				status = rdpdr_process_drive_capset(rdpdr, s);
+				status = rdpdr_process_drive_capset(rdpdr, s, capabilityLength);
 				break;
 
 			case CAP_SMARTCARD_TYPE:
-				status = rdpdr_process_smartcard_capset(rdpdr, s);
+				status = rdpdr_process_smartcard_capset(rdpdr, s, capabilityLength);
 				break;
 
 			default:
@@ -264,7 +265,7 @@ UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr)
 
 	if (!s)
 	{
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
diff --git a/channels/rdpdr/client/rdpdr_main.c b/channels/rdpdr/client/rdpdr_main.c
index 681ebce..a9ac6ec 100644
--- a/channels/rdpdr/client/rdpdr_main.c
+++ b/channels/rdpdr/client/rdpdr_main.c
@@ -79,12 +79,142 @@ struct _DEVICE_DRIVE_EXT
 	BOOL automount;
 };
 
+static const char* rdpdr_packetid_string(UINT16 packetid)
+{
+	switch (packetid)
+	{
+		case PAKID_CORE_SERVER_ANNOUNCE:
+			return "PAKID_CORE_SERVER_ANNOUNCE";
+		case PAKID_CORE_CLIENTID_CONFIRM:
+			return "PAKID_CORE_CLIENTID_CONFIRM";
+		case PAKID_CORE_CLIENT_NAME:
+			return "PAKID_CORE_CLIENT_NAME";
+		case PAKID_CORE_DEVICELIST_ANNOUNCE:
+			return "PAKID_CORE_DEVICELIST_ANNOUNCE";
+		case PAKID_CORE_DEVICE_REPLY:
+			return "PAKID_CORE_DEVICE_REPLY";
+		case PAKID_CORE_DEVICE_IOREQUEST:
+			return "PAKID_CORE_DEVICE_IOREQUEST";
+		case PAKID_CORE_DEVICE_IOCOMPLETION:
+			return "PAKID_CORE_DEVICE_IOCOMPLETION";
+		case PAKID_CORE_SERVER_CAPABILITY:
+			return "PAKID_CORE_SERVER_CAPABILITY";
+		case PAKID_CORE_CLIENT_CAPABILITY:
+			return "PAKID_CORE_CLIENT_CAPABILITY";
+		case PAKID_CORE_DEVICELIST_REMOVE:
+			return "PAKID_CORE_DEVICELIST_REMOVE";
+		case PAKID_CORE_USER_LOGGEDON:
+			return "PAKID_CORE_USER_LOGGEDON";
+		case PAKID_PRN_CACHE_DATA:
+			return "PAKID_PRN_CACHE_DATA";
+		case PAKID_PRN_USING_XPS:
+			return "PAKID_PRN_USING_XPS";
+		default:
+			return "UNKNOWN";
+	}
+}
+
+static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state)
+{
+	switch (state)
+	{
+		case RDPDR_CHANNEL_STATE_INITIAL:
+			return "RDPDR_CHANNEL_STATE_INITIAL";
+		case RDPDR_CHANNEL_STATE_ANNOUNCE:
+			return "RDPDR_CHANNEL_STATE_ANNOUNCE";
+		case RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY:
+			return "RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY";
+		case RDPDR_CHANNEL_STATE_NAME_REQUEST:
+			return "RDPDR_CHANNEL_STATE_NAME_REQUEST";
+		case RDPDR_CHANNEL_STATE_SERVER_CAPS:
+			return "RDPDR_CHANNEL_STATE_SERVER_CAPS";
+		case RDPDR_CHANNEL_STATE_CLIENT_CAPS:
+			return "RDPDR_CHANNEL_STATE_CLIENT_CAPS";
+		case RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM:
+			return "RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM";
+		case RDPDR_CHANNEL_STATE_READY:
+			return "RDPDR_CHANNEL_STATE_READY";
+		case RDPDR_CHANNEL_STATE_USER_LOGGEDON:
+			return "RDPDR_CHANNEL_STATE_USER_LOGGEDON";
+		default:
+			return "RDPDR_CHANNEL_STATE_UNKNOWN";
+	}
+}
+
+static const char* rdpdr_device_type_string(UINT32 type)
+{
+	switch (type)
+	{
+		case RDPDR_DTYP_SERIAL:
+			return "serial";
+		case RDPDR_DTYP_PRINT:
+			return "printer";
+		case RDPDR_DTYP_FILESYSTEM:
+			return "drive";
+		case RDPDR_DTYP_SMARTCARD:
+			return "smartcard";
+		case RDPDR_DTYP_PARALLEL:
+			return "parallel";
+		default:
+			return "UNKNOWN";
+	}
+}
+
+static const char* support_str(BOOL val)
+{
+	if (val)
+		return "supported";
+	return "not found";
+}
+
+static const char* rdpdr_caps_pdu_str(UINT32 flag)
+{
+	switch (flag)
+	{
+		case RDPDR_DEVICE_REMOVE_PDUS:
+			return "RDPDR_USER_LOGGEDON_PDU";
+		case RDPDR_CLIENT_DISPLAY_NAME_PDU:
+			return "RDPDR_CLIENT_DISPLAY_NAME_PDU";
+		case RDPDR_USER_LOGGEDON_PDU:
+			return "RDPDR_USER_LOGGEDON_PDU";
+		default:
+			return "RDPDR_UNKNONW";
+	}
+}
+
+static BOOL rdpdr_check_extended_pdu_flag(rdpdrPlugin* rdpdr, UINT32 flag)
+{
+	WINPR_ASSERT(rdpdr);
+
+	const BOOL client = (rdpdr->clientExtendedPDU & flag) != 0;
+	const BOOL server = (rdpdr->serverExtendedPDU & flag) != 0;
+
+	if (!client || !server)
+	{
+		WLog_Print(rdpdr->log, WLOG_WARN, "Checking ExtendedPDU::%s, client %s, server %s",
+		           rdpdr_caps_pdu_str(flag), support_str(client), support_str(server));
+		return FALSE;
+	}
+	return TRUE;
+}
+
+BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next)
+{
+	WINPR_ASSERT(rdpdr);
+
+	if (next != rdpdr->state)
+		WLog_Print(rdpdr->log, WLOG_DEBUG, "[RDPDR] transition from %s to %s",
+		           rdpdr_state_str(rdpdr->state), rdpdr_state_str(next));
+	rdpdr->state = next;
+	return TRUE;
+}
+
 /**
  * Function description
  *
  * @return 0 on success, otherwise a Win32 error code
  */
-static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL userLoggedOn);
+static UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr);
 
 /**
  * Function description
@@ -95,11 +225,21 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou
 {
 	UINT32 i;
 	wStream* s;
+
+	WINPR_ASSERT(rdpdr);
+	WINPR_ASSERT(ids || (count == 0));
+
+	if (count == 0)
+		return CHANNEL_RC_OK;
+
+	if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_DEVICE_REMOVE_PDUS))
+		return CHANNEL_RC_OK;
+
 	s = Stream_New(NULL, count * sizeof(UINT32) + 8);
 
 	if (!s)
 	{
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
@@ -212,7 +352,7 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 									devman_load_device_service(rdpdr->devman,
 									                           (const RDPDR_DEVICE*)&drive,
 									                           rdpdr->rdpcontext);
-									rdpdr_send_device_list_announce_request(rdpdr, TRUE);
+									rdpdr_try_send_device_list_announce_request(rdpdr);
 								}
 							}
 
@@ -261,8 +401,8 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 											         rdpdr, 1, ids)))
 											{
 												// dont end on error, just report ?
-												WLog_ERR(
-												    TAG,
+												WLog_Print(
+												    rdpdr->log, WLOG_ERROR,
 												    "rdpdr_send_device_list_remove_request failed "
 												    "with error %" PRIu32 "!",
 												    error);
@@ -358,7 +498,7 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
 	if (rdpdr->hotplug_wnd && !PostMessage(rdpdr->hotplug_wnd, WM_QUIT, 0, 0))
 	{
 		error = GetLastError();
-		WLog_ERR(TAG, "PostMessage failed with error %" PRIu32 "", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "PostMessage failed with error %" PRIu32 "", error);
 	}
 
 	return error;
@@ -475,9 +615,9 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
 
 			if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids)))
 			{
-				WLog_ERR(TAG,
-				         "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
-				         error);
+				WLog_Print(rdpdr->log, WLOG_ERROR,
+				           "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
+				           error);
 				goto cleanup;
 			}
 		}
@@ -506,7 +646,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
 			if ((error = devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE*)&drive,
 			                                        rdpdr->rdpcontext)))
 			{
-				WLog_ERR(TAG, "devman_load_device_service failed!");
+				WLog_Print(rdpdr->log, WLOG_ERROR, "devman_load_device_service failed!");
 				error = CHANNEL_RC_NO_MEMORY;
 				goto cleanup;
 			}
@@ -540,10 +680,11 @@ static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef,
 		{
 			if ((error = handle_hotplug(rdpdr)))
 			{
-				WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error);
+				WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
+				           error);
 			}
 			else
-				rdpdr_send_device_list_announce_request(rdpdr, TRUE);
+				rdpdr_try_send_device_list_announce_request(rdpdr);
 
 			return;
 		}
@@ -556,7 +697,7 @@ void first_hotplug(rdpdrPlugin* rdpdr)
 
 	if ((error = handle_hotplug(rdpdr)))
 	{
-		WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", error);
 	}
 }
 
@@ -661,14 +802,14 @@ static void handle_mountpoint(hotplug_dev* dev_array, size_t* size, const char*
 
 #ifdef __sun
 #include <sys/mnttab.h>
-static UINT handle_platform_mounts_sun(hotplug_dev* dev_array, size_t* size)
+static UINT handle_platform_mounts_sun(wLog* log, hotplug_dev* dev_array, size_t* size)
 {
 	FILE* f;
 	struct mnttab ent;
 	f = fopen("/etc/mnttab", "r");
 	if (f == NULL)
 	{
-		WLog_ERR(TAG, "fopen failed!");
+		WLog_Print(log, WLOG_ERROR, "fopen failed!");
 		return ERROR_OPEN_FAILED;
 	}
 	while (getmntent(f, &ent) == 0)
@@ -682,7 +823,7 @@ static UINT handle_platform_mounts_sun(hotplug_dev* dev_array, size_t* size)
 
 #if defined(__FreeBSD__) || defined(__OpenBSD__)
 #include <sys/mount.h>
-static UINT handle_platform_mounts_bsd(hotplug_dev* dev_array, size_t* size)
+static UINT handle_platform_mounts_bsd(wLog* log, hotplug_dev* dev_array, size_t* size)
 {
 	int mntsize;
 	size_t idx;
@@ -692,7 +833,7 @@ static UINT handle_platform_mounts_bsd(hotplug_dev* dev_array, size_t* size)
 	if (!mntsize)
 	{
 		/* TODO: handle 'errno' */
-		WLog_ERR(TAG, "getmntinfo failed!");
+		WLog_Print(log, WLOG_ERROR, "getmntinfo failed!");
 		return ERROR_OPEN_FAILED;
 	}
 	for (idx = 0; idx < (size_t)mntsize; idx++)
@@ -706,14 +847,14 @@ static UINT handle_platform_mounts_bsd(hotplug_dev* dev_array, size_t* size)
 
 #if defined(__LINUX__) || defined(__linux__)
 #include <mntent.h>
-static UINT handle_platform_mounts_linux(hotplug_dev* dev_array, size_t* size)
+static UINT handle_platform_mounts_linux(wLog* log, hotplug_dev* dev_array, size_t* size)
 {
 	FILE* f;
 	struct mntent* ent;
 	f = fopen("/proc/mounts", "r");
 	if (f == NULL)
 	{
-		WLog_ERR(TAG, "fopen failed!");
+		WLog_Print(log, WLOG_ERROR, "fopen failed!");
 		return ERROR_OPEN_FAILED;
 	}
 	while ((ent = getmntent(f)) != NULL)
@@ -725,14 +866,14 @@ static UINT handle_platform_mounts_linux(hotplug_dev* dev_array, size_t* size)
 }
 #endif
 
-static UINT handle_platform_mounts(hotplug_dev* dev_array, size_t* size)
+static UINT handle_platform_mounts(wLog* log, hotplug_dev* dev_array, size_t* size)
 {
 #ifdef __sun
-	return handle_platform_mounts_sun(dev_array, size);
+	return handle_platform_mounts_sun(log, dev_array, size);
 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
-	return handle_platform_mounts_bsd(dev_array, size);
+	return handle_platform_mounts_bsd(log, dev_array, size);
 #elif defined(__LINUX__) || defined(__linux__)
-	return handle_platform_mounts_linux(dev_array, size);
+	return handle_platform_mounts_linux(log, dev_array, size);
 #endif
 	return ERROR_CALL_NOT_IMPLEMENTED;
 }
@@ -790,7 +931,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
 	UINT32 ids[1];
 	UINT error = ERROR_SUCCESS;
 
-	error = handle_platform_mounts(dev_array, &size);
+	error = handle_platform_mounts(rdpdr->log, dev_array, &size);
 
 	/* delete removed devices */
 	count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
@@ -834,9 +975,9 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
 
 			if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids)))
 			{
-				WLog_ERR(TAG,
-				         "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
-				         error);
+				WLog_Print(rdpdr->log, WLOG_ERROR,
+				           "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
+				           error);
 				goto cleanup;
 			}
 		}
@@ -858,7 +999,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
 
 			if (!drive.Name)
 			{
-				WLog_ERR(TAG, "_strdup failed!");
+				WLog_Print(rdpdr->log, WLOG_ERROR, "_strdup failed!");
 				error = CHANNEL_RC_NO_MEMORY;
 				goto cleanup;
 			}
@@ -866,7 +1007,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
 			if ((error = devman_load_device_service(rdpdr->devman, (const RDPDR_DEVICE*)&drive,
 			                                        rdpdr->rdpcontext)))
 			{
-				WLog_ERR(TAG, "devman_load_device_service failed!");
+				WLog_Print(rdpdr->log, WLOG_ERROR, "devman_load_device_service failed!");
 				goto cleanup;
 			}
 		}
@@ -887,7 +1028,7 @@ static void first_hotplug(rdpdrPlugin* rdpdr)
 
 	if ((error = handle_hotplug(rdpdr)))
 	{
-		WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", error);
 	}
 }
 
@@ -905,7 +1046,7 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
 
 	if (mfd < 0)
 	{
-		WLog_ERR(TAG, "ERROR: Unable to open /proc/mounts.");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "ERROR: Unable to open /proc/mounts.");
 		error = ERROR_INTERNAL_ERROR;
 		goto out;
 	}
@@ -922,7 +1063,8 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
 		if (status == WAIT_FAILED)
 		{
 			error = GetLastError();
-			WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
+			WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
+			           error);
 			goto out;
 		}
 
@@ -934,11 +1076,12 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
 			/* file /proc/mounts changed, handle this */
 			if ((error = handle_hotplug(rdpdr)))
 			{
-				WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error);
+				WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
+				           error);
 				goto out;
 			}
 			else
-				rdpdr_send_device_list_announce_request(rdpdr, TRUE);
+				rdpdr_try_send_device_list_announce_request(rdpdr);
 		}
 
 		FD_ZERO(&rfds);
@@ -978,7 +1121,8 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
 		if (WaitForSingleObject(rdpdr->hotplugThread, INFINITE) == WAIT_FAILED)
 		{
 			error = GetLastError();
-			WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
+			WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
+			           error);
 			return error;
 		}
 
@@ -1007,7 +1151,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
 
 	if (!rdpdr->devman)
 	{
-		WLog_ERR(TAG, "devman_new failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "devman_new failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
@@ -1036,7 +1180,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
 
 				if (!(rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
 				{
-					WLog_ERR(TAG, "CreateEvent failed!");
+					WLog_Print(rdpdr->log, WLOG_ERROR, "CreateEvent failed!");
 					return ERROR_INTERNAL_ERROR;
 				}
 
@@ -1045,7 +1189,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
 				if (!(rdpdr->hotplugThread =
 				          CreateThread(NULL, 0, drive_hotplug_thread_func, rdpdr, 0, NULL)))
 				{
-					WLog_ERR(TAG, "CreateThread failed!");
+					WLog_Print(rdpdr->log, WLOG_ERROR, "CreateThread failed!");
 #ifndef _WIN32
 					CloseHandle(rdpdr->stopEvent);
 					rdpdr->stopEvent = NULL;
@@ -1059,7 +1203,8 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
 
 		if ((error = devman_load_device_service(rdpdr->devman, device, rdpdr->rdpcontext)))
 		{
-			WLog_ERR(TAG, "devman_load_device_service failed with error %" PRIu32 "!", error);
+			WLog_Print(rdpdr->log, WLOG_ERROR,
+			           "devman_load_device_service failed with error %" PRIu32 "!", error);
 			return error;
 		}
 	}
@@ -1072,10 +1217,19 @@ static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s
 	if (Stream_GetRemainingLength(s) < 8)
 		return ERROR_INVALID_DATA;
 
-	Stream_Read_UINT16(s, rdpdr->versionMajor);
-	Stream_Read_UINT16(s, rdpdr->versionMinor);
+	Stream_Read_UINT16(s, rdpdr->serverVersionMajor);
+	Stream_Read_UINT16(s, rdpdr->serverVersionMinor);
 	Stream_Read_UINT32(s, rdpdr->clientID);
 	rdpdr->sequenceId++;
+
+	rdpdr->clientVersionMajor = MIN(RDPDR_VERSION_MAJOR, rdpdr->serverVersionMajor);
+	rdpdr->clientVersionMinor = MIN(RDPDR_VERSION_MINOR_RDP10X, rdpdr->serverVersionMinor);
+	WLog_Print(rdpdr->log, WLOG_DEBUG,
+	           "[rdpdr] server announces version %" PRIu32 ".%" PRIu32 ", client uses %" PRIu32
+	           ".%" PRIu32,
+	           rdpdr->serverVersionMajor, rdpdr->serverVersionMinor, rdpdr->clientVersionMajor,
+	           rdpdr->clientVersionMinor);
+
 	return CHANNEL_RC_OK;
 }
 
@@ -1087,18 +1241,23 @@ static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s
 static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr)
 {
 	wStream* s;
+
+	WINPR_ASSERT(rdpdr);
+	WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE);
+	rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY);
+
 	s = Stream_New(NULL, 12);
 
 	if (!s)
 	{
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
 	Stream_Write_UINT16(s, RDPDR_CTYP_CORE);             /* Component (2 bytes) */
 	Stream_Write_UINT16(s, PAKID_CORE_CLIENTID_CONFIRM); /* PacketId (2 bytes) */
-	Stream_Write_UINT16(s, rdpdr->versionMajor);
-	Stream_Write_UINT16(s, rdpdr->versionMinor);
+	Stream_Write_UINT16(s, rdpdr->clientVersionMajor);
+	Stream_Write_UINT16(s, rdpdr->clientVersionMinor);
 	Stream_Write_UINT32(s, (UINT32)rdpdr->clientID);
 	return rdpdr_send(rdpdr, s);
 }
@@ -1114,6 +1273,10 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
 	WCHAR* computerNameW = NULL;
 	size_t computerNameLenW;
 
+	WINPR_ASSERT(rdpdr);
+	WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY);
+	rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_NAME_REQUEST);
+
 	if (!rdpdr->computerName[0])
 	{
 		DWORD size = sizeof(rdpdr->computerName) - 1;
@@ -1126,7 +1289,7 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
 	if (!s)
 	{
 		free(computerNameW);
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
@@ -1147,17 +1310,20 @@ static UINT rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s
 	UINT16 versionMinor;
 	UINT32 clientID;
 
-	if (Stream_GetRemainingLength(s) < 8)
+	WINPR_ASSERT(rdpdr);
+	WINPR_ASSERT(s);
+
+	if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 8))
 		return ERROR_INVALID_DATA;
 
 	Stream_Read_UINT16(s, versionMajor);
 	Stream_Read_UINT16(s, versionMinor);
 	Stream_Read_UINT32(s, clientID);
 
-	if (versionMajor != rdpdr->versionMajor || versionMinor != rdpdr->versionMinor)
+	if (versionMajor != rdpdr->clientVersionMajor || versionMinor != rdpdr->clientVersionMinor)
 	{
-		rdpdr->versionMajor = versionMajor;
-		rdpdr->versionMinor = versionMinor;
+		rdpdr->clientVersionMajor = versionMajor;
+		rdpdr->clientVersionMinor = versionMinor;
 	}
 
 	if (clientID != rdpdr->clientID)
@@ -1184,11 +1350,15 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
 	DEVICE* device;
 	int keyCount;
 	ULONG_PTR* pKeys = NULL;
+
+	if (userLoggedOn)
+		rdpdr->userLoggedOn = TRUE;
+
 	s = Stream_New(NULL, 256);
 
 	if (!s)
 	{
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
@@ -1211,8 +1381,8 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
 		 * 3. other devices are sent only after user_loggedon
 		 */
 
-		if ((rdpdr->versionMinor == 0x0005) || (device->type == RDPDR_DTYP_SMARTCARD) ||
-		    userLoggedOn)
+		if ((rdpdr->clientVersionMinor == RDPDR_VERSION_MINOR_RDP51) ||
+		    (device->type == RDPDR_DTYP_SMARTCARD) || userLoggedOn)
 		{
 			data_len = (device->data == NULL ? 0 : Stream_GetPosition(device->data));
 
@@ -1220,7 +1390,7 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
 			{
 				free(pKeys);
 				Stream_Free(s, TRUE);
-				WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+				WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
 				return ERROR_INVALID_DATA;
 			}
 
@@ -1244,8 +1414,9 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
 				Stream_Write(s, Stream_Buffer(device->data), data_len);
 
 			count++;
-			WLog_INFO(TAG, "registered device #%" PRIu32 ": %s (type=%" PRIu32 " id=%" PRIu32 ")",
-			          count, device->name, device->type, device->id);
+			WLog_Print(rdpdr->log, WLOG_INFO,
+			           "registered device #%" PRIu32 ": %s (type=%" PRIu32 " id=%" PRIu32 ")",
+			           count, device->name, device->type, device->id);
 		}
 	}
 
@@ -1258,6 +1429,19 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
 	return rdpdr_send(rdpdr, s);
 }
 
+UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr)
+{
+	WINPR_ASSERT(rdpdr);
+	if (rdpdr->state != RDPDR_CHANNEL_STATE_USER_LOGGEDON)
+	{
+		WLog_Print(rdpdr->log, WLOG_DEBUG,
+		           "hotplug event received, but channel [RDPDR] is not ready (state %s), ignoring.",
+		           rdpdr_state_str(rdpdr->state));
+		return CHANNEL_RC_OK;
+	}
+	return rdpdr_send_device_list_announce_request(rdpdr, TRUE);
+}
+
 static UINT dummy_irp_response(rdpdrPlugin* rdpdr, wStream* s)
 {
 
@@ -1268,7 +1452,7 @@ static UINT dummy_irp_response(rdpdrPlugin* rdpdr, wStream* s)
 	wStream* output = Stream_New(NULL, 256); // RDPDR_DEVICE_IO_RESPONSE_LENGTH
 	if (!output)
 	{
-		WLog_ERR(TAG, "Stream_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
@@ -1301,11 +1485,11 @@ static UINT rdpdr_process_irp(rdpdrPlugin* rdpdr, wStream* s)
 {
 	IRP* irp;
 	UINT error = CHANNEL_RC_OK;
-	irp = irp_new(rdpdr->devman, s, &error);
+	irp = irp_new(rdpdr->devman, s, rdpdr->log, &error);
 
 	if (!irp)
 	{
-		WLog_ERR(TAG, "irp_new failed with %" PRIu32 "!", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "irp_new failed with %" PRIu32 "!", error);
 
 		if (error == CHANNEL_RC_OK)
 		{
@@ -1317,8 +1501,11 @@ static UINT rdpdr_process_irp(rdpdrPlugin* rdpdr, wStream* s)
 
 	IFCALLRET(irp->device->IRPRequest, error, irp->device, irp);
 
-	if (error)
-		WLog_ERR(TAG, "device->IRPRequest failed with error %" PRIu32 "", error);
+	if (error != CHANNEL_RC_OK)
+	{
+		WLog_Print(rdpdr->log, WLOG_ERROR, "device->IRPRequest failed with error %" PRIu32 "",
+		           error);
+	}
 
 	return error;
 }
@@ -1360,6 +1547,8 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
 	DEVICE* device;
 	ULONG_PTR* pKeys = NULL;
 	UINT error = CHANNEL_RC_OK;
+
+	rdpdr->userLoggedOn = FALSE; /* reset possible received state */
 	pKeys = NULL;
 	keyCount = ListDictionary_GetKeys(rdpdr->devman->devices, &pKeys);
 
@@ -1370,7 +1559,7 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
 
 		if (error != CHANNEL_RC_OK)
 		{
-			WLog_ERR(TAG, "Init failed!");
+			WLog_Print(rdpdr->log, WLOG_ERROR, "Init failed!");
 			free(pKeys);
 			return error;
 		}
@@ -1380,6 +1569,97 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr)
 	return CHANNEL_RC_OK;
 }
 
+static BOOL state_match(enum RDPDR_CHANNEL_STATE state, size_t count, va_list ap)
+{
+	for (size_t x = 0; x < count; x++)
+	{
+		enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE);
+		if (state == cur)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static const char* state_str(size_t count, va_list ap, char* buffer, size_t size)
+{
+	for (size_t x = 0; x < count; x++)
+	{
+		enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE);
+		const char* curstr = rdpdr_state_str(cur);
+		winpr_str_append(curstr, buffer, size, "|");
+	}
+	return buffer;
+}
+
+static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid, enum RDPDR_CHANNEL_STATE next,
+                              size_t count, ...)
+{
+	va_list ap;
+	WINPR_ASSERT(rdpdr);
+
+	va_start(ap, count);
+	BOOL rc = state_match(rdpdr->state, count, ap);
+	va_end(ap);
+
+	if (!rc)
+	{
+		const char* strstate = rdpdr_state_str(rdpdr->state);
+		char buffer[256] = { 0 };
+
+		va_start(ap, count);
+		state_str(count, ap, buffer, sizeof(buffer));
+		va_end(ap);
+
+		WLog_Print(rdpdr->log, WLOG_ERROR,
+		           "channel [RDPDR] received %s, expected states [%s] but have state %s, aborting.",
+		           rdpdr_packetid_string(packetid), buffer, strstate);
+
+		rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
+		return FALSE;
+	}
+	return rdpdr_state_advance(rdpdr, next);
+}
+
+static BOOL rdpdr_check_channel_state(rdpdrPlugin* rdpdr, UINT16 packetid)
+{
+	WINPR_ASSERT(rdpdr);
+
+	switch (packetid)
+	{
+		case PAKID_CORE_SERVER_ANNOUNCE:
+			/* windows servers sometimes send this message.
+			 * it seems related to session login (e.g. first initialization for RDP/TLS style login,
+			 * then reinitialize the channel after login successful
+			 */
+			rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL);
+			return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_ANNOUNCE, 1,
+			                         RDPDR_CHANNEL_STATE_INITIAL);
+		case PAKID_CORE_SERVER_CAPABILITY:
+			return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_SERVER_CAPS, 6,
+			                         RDPDR_CHANNEL_STATE_NAME_REQUEST,
+			                         RDPDR_CHANNEL_STATE_SERVER_CAPS, RDPDR_CHANNEL_STATE_READY,
+			                         RDPDR_CHANNEL_STATE_CLIENT_CAPS, PAKID_CORE_CLIENTID_CONFIRM,
+			                         PAKID_CORE_USER_LOGGEDON);
+		case PAKID_CORE_CLIENTID_CONFIRM:
+			return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, 3,
+			                         RDPDR_CHANNEL_STATE_CLIENT_CAPS, RDPDR_CHANNEL_STATE_READY,
+			                         RDPDR_CHANNEL_STATE_USER_LOGGEDON);
+		case PAKID_CORE_USER_LOGGEDON:
+			if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_USER_LOGGEDON_PDU))
+				return FALSE;
+
+			return rdpdr_state_check(
+			    rdpdr, packetid, RDPDR_CHANNEL_STATE_USER_LOGGEDON, 4,
+			    RDPDR_CHANNEL_STATE_NAME_REQUEST, RDPDR_CHANNEL_STATE_CLIENT_CAPS,
+			    RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, RDPDR_CHANNEL_STATE_READY);
+		default:
+		{
+			enum RDPDR_CHANNEL_STATE state = RDPDR_CHANNEL_STATE_READY;
+			return rdpdr_state_check(rdpdr, packetid, state, 1, state);
+		}
+	}
+}
+
 /**
  * Function description
  *
@@ -1403,6 +1683,9 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 
 		if (component == RDPDR_CTYP_CORE)
 		{
+			if (!rdpdr_check_channel_state(rdpdr, packetId))
+				return CHANNEL_RC_OK;
+
 			switch (packetId)
 			{
 				case PAKID_CORE_SERVER_ANNOUNCE:
@@ -1411,19 +1694,20 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 					}
 					else if ((error = rdpdr_send_client_announce_reply(rdpdr)))
 					{
-						WLog_ERR(TAG,
-						         "rdpdr_send_client_announce_reply failed with error %" PRIu32 "",
-						         error);
+						WLog_Print(rdpdr->log, WLOG_ERROR,
+						           "rdpdr_send_client_announce_reply failed with error %" PRIu32 "",
+						           error);
 					}
 					else if ((error = rdpdr_send_client_name_request(rdpdr)))
 					{
-						WLog_ERR(TAG,
-						         "rdpdr_send_client_name_request failed with error %" PRIu32 "",
-						         error);
+						WLog_Print(rdpdr->log, WLOG_ERROR,
+						           "rdpdr_send_client_name_request failed with error %" PRIu32 "",
+						           error);
 					}
 					else if ((error = rdpdr_process_init(rdpdr)))
 					{
-						WLog_ERR(TAG, "rdpdr_process_init failed with error %" PRIu32 "", error);
+						WLog_Print(rdpdr->log, WLOG_ERROR,
+						           "rdpdr_process_init failed with error %" PRIu32 "", error);
 					}
 
 					break;
@@ -1434,9 +1718,9 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 					}
 					else if ((error = rdpdr_send_capability_response(rdpdr)))
 					{
-						WLog_ERR(TAG,
-						         "rdpdr_send_capability_response failed with error %" PRIu32 "",
-						         error);
+						WLog_Print(rdpdr->log, WLOG_ERROR,
+						           "rdpdr_send_capability_response failed with error %" PRIu32 "",
+						           error);
 					}
 
 					break;
@@ -1447,22 +1731,29 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 					}
 					else if ((error = rdpdr_send_device_list_announce_request(rdpdr, FALSE)))
 					{
-						WLog_ERR(
-						    TAG,
+						WLog_Print(
+						    rdpdr->log, WLOG_ERROR,
 						    "rdpdr_send_device_list_announce_request failed with error %" PRIu32 "",
 						    error);
 					}
-
+					else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY))
+					{
+						error = ERROR_INTERNAL_ERROR;
+					}
 					break;
 
 				case PAKID_CORE_USER_LOGGEDON:
 					if ((error = rdpdr_send_device_list_announce_request(rdpdr, TRUE)))
 					{
-						WLog_ERR(
-						    TAG,
+						WLog_Print(
+						    rdpdr->log, WLOG_ERROR,
 						    "rdpdr_send_device_list_announce_request failed with error %" PRIu32 "",
 						    error);
 					}
+					else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY))
+					{
+						error = ERROR_INTERNAL_ERROR;
+					}
 
 					break;
 
@@ -1481,7 +1772,8 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 				case PAKID_CORE_DEVICE_IOREQUEST:
 					if ((error = rdpdr_process_irp(rdpdr, s)))
 					{
-						WLog_ERR(TAG, "rdpdr_process_irp failed with error %" PRIu32 "", error);
+						WLog_Print(rdpdr->log, WLOG_ERROR,
+						           "rdpdr_process_irp failed with error %" PRIu32 "", error);
 						return error;
 					}
 					else
@@ -1490,7 +1782,8 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 					break;
 
 				default:
-					WLog_ERR(TAG, "RDPDR_CTYP_CORE unknown PacketId: 0x%04" PRIX16 "", packetId);
+					WLog_Print(rdpdr->log, WLOG_ERROR,
+					           "RDPDR_CTYP_CORE unknown PacketId: 0x%04" PRIX16 "", packetId);
 					error = ERROR_INVALID_DATA;
 					break;
 			}
@@ -1501,9 +1794,10 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
 
 			if (error != CHANNEL_RC_OK)
 			{
-				WLog_ERR(TAG,
-				         "Unknown message: Component: 0x%04" PRIX16 " PacketId: 0x%04" PRIX16 "",
-				         component, packetId);
+				WLog_Print(rdpdr->log, WLOG_ERROR,
+				           "Unknown message: Component: [0x%04" PRIX16 "] PacketId: [0x%04" PRIX16
+				           "]",
+				           component, packetId);
 			}
 		}
 	}
@@ -1543,8 +1837,8 @@ UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
 	if (status != CHANNEL_RC_OK)
 	{
 		Stream_Free(s, TRUE);
-		WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
-		         WTSErrorToString(status), status);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
+		           WTSErrorToString(status), status);
 	}
 
 	return status;
@@ -1581,7 +1875,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void*
 
 		if (!rdpdr->data_in)
 		{
-			WLog_ERR(TAG, "Stream_New failed!");
+			WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!");
 			return CHANNEL_RC_NO_MEMORY;
 		}
 	}
@@ -1590,7 +1884,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void*
 
 	if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
 	{
-		WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
 		return ERROR_INVALID_DATA;
 	}
 
@@ -1600,7 +1894,8 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void*
 	{
 		if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
 		{
-			WLog_ERR(TAG, "rdpdr_virtual_channel_event_data_received: read error");
+			WLog_Print(rdpdr->log, WLOG_ERROR,
+			           "rdpdr_virtual_channel_event_data_received: read error");
 			return ERROR_INTERNAL_ERROR;
 		}
 
@@ -1610,7 +1905,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void*
 
 		if (!MessageQueue_Post(rdpdr->queue, NULL, 0, (void*)data_in, NULL))
 		{
-			WLog_ERR(TAG, "MessageQueue_Post failed!");
+			WLog_Print(rdpdr->log, WLOG_ERROR, "MessageQueue_Post failed!");
 			return ERROR_INTERNAL_ERROR;
 		}
 	}
@@ -1631,14 +1926,15 @@ static VOID VCAPITYPE rdpdr_virtual_channel_open_event_ex(LPVOID lpUserParam, DW
 		case CHANNEL_EVENT_DATA_RECEIVED:
 			if (!rdpdr || !pData || (rdpdr->OpenHandle != openHandle))
 			{
-				WLog_ERR(TAG, "error no match");
+				WLog_Print(rdpdr->log, WLOG_ERROR, "error no match");
 				return;
 			}
 			if ((error = rdpdr_virtual_channel_event_data_received(rdpdr, pData, dataLength,
 			                                                       totalLength, dataFlags)))
-				WLog_ERR(TAG,
-				         "rdpdr_virtual_channel_event_data_received failed with error %" PRIu32 "!",
-				         error);
+				WLog_Print(rdpdr->log, WLOG_ERROR,
+				           "rdpdr_virtual_channel_event_data_received failed with error %" PRIu32
+				           "!",
+				           error);
 
 			break;
 
@@ -1676,7 +1972,8 @@ static DWORD WINAPI rdpdr_virtual_channel_client_thread(LPVOID arg)
 
 	if ((error = rdpdr_process_connect(rdpdr)))
 	{
-		WLog_ERR(TAG, "rdpdr_process_connect failed with error %" PRIu32 "!", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "rdpdr_process_connect failed with error %" PRIu32 "!",
+		           error);
 
 		if (rdpdr->rdpcontext)
 			setChannelError(rdpdr->rdpcontext, error,
@@ -1702,7 +1999,8 @@ static DWORD WINAPI rdpdr_virtual_channel_client_thread(LPVOID arg)
 
 				if ((error = rdpdr_process_receive(rdpdr, data)))
 				{
-					WLog_ERR(TAG, "rdpdr_process_receive failed with error %" PRIu32 "!", error);
+					WLog_Print(rdpdr->log, WLOG_ERROR,
+					           "rdpdr_process_receive failed with error %" PRIu32 "!", error);
 
 					if (rdpdr->rdpcontext)
 						setChannelError(rdpdr->rdpcontext, error,
@@ -1740,8 +2038,8 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa
 
 	if (status != CHANNEL_RC_OK)
 	{
-		WLog_ERR(TAG, "pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]",
-		         WTSErrorToString(status), status);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]",
+		           WTSErrorToString(status), status);
 		return status;
 	}
 
@@ -1749,7 +2047,7 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa
 
 	if (!rdpdr->queue)
 	{
-		WLog_ERR(TAG, "MessageQueue_New failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "MessageQueue_New failed!");
 		return CHANNEL_RC_NO_MEMORY;
 	}
 
@@ -1758,7 +2056,7 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa
 	if (!(rdpdr->thread =
 	          CreateThread(NULL, 0, rdpdr_virtual_channel_client_thread, (void*)rdpdr, 0, NULL)))
 	{
-		WLog_ERR(TAG, "CreateThread failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "CreateThread failed!");
 		return ERROR_INTERNAL_ERROR;
 	}
 
@@ -1781,7 +2079,8 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
 	    (WaitForSingleObject(rdpdr->thread, INFINITE) == WAIT_FAILED))
 	{
 		error = GetLastError();
-		WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!",
+		           error);
 		return error;
 	}
 
@@ -1792,7 +2091,8 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
 
 	if ((error = drive_hotplug_thread_terminate(rdpdr)))
 	{
-		WLog_ERR(TAG, "drive_hotplug_thread_terminate failed with error %" PRIu32 "!", error);
+		WLog_Print(rdpdr->log, WLOG_ERROR,
+		           "drive_hotplug_thread_terminate failed with error %" PRIu32 "!", error);
 		return error;
 	}
 
@@ -1800,8 +2100,8 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
 
 	if (CHANNEL_RC_OK != error)
 	{
-		WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
-		         WTSErrorToString(error), error);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
+		           WTSErrorToString(error), error);
 	}
 
 	rdpdr->OpenHandle = 0;
@@ -1835,7 +2135,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP
 
 	if (!rdpdr || (rdpdr->InitHandle != pInitHandle))
 	{
-		WLog_ERR(TAG, "error no match");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "error no match");
 		return;
 	}
 
@@ -1846,17 +2146,18 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP
 
 		case CHANNEL_EVENT_CONNECTED:
 			if ((error = rdpdr_virtual_channel_event_connected(rdpdr, pData, dataLength)))
-				WLog_ERR(TAG,
-				         "rdpdr_virtual_channel_event_connected failed with error %" PRIu32 "!",
-				         error);
+				WLog_Print(rdpdr->log, WLOG_ERROR,
+				           "rdpdr_virtual_channel_event_connected failed with error %" PRIu32 "!",
+				           error);
 
 			break;
 
 		case CHANNEL_EVENT_DISCONNECTED:
 			if ((error = rdpdr_virtual_channel_event_disconnected(rdpdr)))
-				WLog_ERR(TAG,
-				         "rdpdr_virtual_channel_event_disconnected failed with error %" PRIu32 "!",
-				         error);
+				WLog_Print(rdpdr->log, WLOG_ERROR,
+				           "rdpdr_virtual_channel_event_disconnected failed with error %" PRIu32
+				           "!",
+				           error);
 
 			break;
 
@@ -1867,7 +2168,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP
 		case CHANNEL_EVENT_ATTACHED:
 		case CHANNEL_EVENT_DETACHED:
 		default:
-			WLog_ERR(TAG, "unknown event %" PRIu32 "!", event);
+			WLog_Print(rdpdr->log, WLOG_ERROR, "unknown event %" PRIu32 "!", event);
 			break;
 	}
 
@@ -1877,6 +2178,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP
 }
 
 /* rdpdr is always built-in */
+#define TAG CHANNELS_TAG("rdpdr.client")
 #define VirtualChannelEntryEx rdpdr_VirtualChannelEntryEx
 
 BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle)
@@ -1884,13 +2186,30 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
 	UINT rc;
 	rdpdrPlugin* rdpdr;
 	CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
+
+	WINPR_ASSERT(pEntryPoints);
+	WINPR_ASSERT(pInitHandle);
+
 	rdpdr = (rdpdrPlugin*)calloc(1, sizeof(rdpdrPlugin));
 
 	if (!rdpdr)
 	{
-		WLog_ERR(TAG, "calloc failed!");
+		WLog_Print(rdpdr->log, WLOG_ERROR, "calloc failed!");
 		return FALSE;
 	}
+	rdpdr->log = WLog_Get(TAG);
+
+	rdpdr->clientExtendedPDU =
+	    RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | RDPDR_USER_LOGGEDON_PDU;
+	rdpdr->clientIOCode1 =
+	    RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ |
+	    RDPDR_IRP_MJ_WRITE | RDPDR_IRP_MJ_FLUSH_BUFFERS | RDPDR_IRP_MJ_SHUTDOWN |
+	    RDPDR_IRP_MJ_DEVICE_CONTROL | RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION |
+	    RDPDR_IRP_MJ_SET_VOLUME_INFORMATION | RDPDR_IRP_MJ_QUERY_INFORMATION |
+	    RDPDR_IRP_MJ_SET_INFORMATION | RDPDR_IRP_MJ_DIRECTORY_CONTROL | RDPDR_IRP_MJ_LOCK_CONTROL |
+	    RDPDR_IRP_MJ_QUERY_SECURITY | RDPDR_IRP_MJ_SET_SECURITY;
+
+	rdpdr->clientExtraFlags1 = ENABLE_ASYNCIO;
 
 	rdpdr->channelDef.options =
 	    CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
@@ -1912,8 +2231,8 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p
 
 	if (CHANNEL_RC_OK != rc)
 	{
-		WLog_ERR(TAG, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc),
-		         rc);
+		WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]",
+		           WTSErrorToString(rc), rc);
 		free(rdpdr);
 		return FALSE;
 	}
diff --git a/channels/rdpdr/client/rdpdr_main.h b/channels/rdpdr/client/rdpdr_main.h
index 4c372da..37766f1 100644
--- a/channels/rdpdr/client/rdpdr_main.h
+++ b/channels/rdpdr/client/rdpdr_main.h
@@ -42,15 +42,33 @@
 #include <CoreServices/CoreServices.h>
 #endif
 
-#define TAG CHANNELS_TAG("rdpdr.client")
+#if !defined(Stream_CheckAndLogRequiredLengthWLog)
+#define Stream_CheckAndLogRequiredLengthWLog(log, s, len)                                     \
+	Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \
+	                                       __FILE__, __LINE__)
+#endif
 
 typedef struct rdpdr_plugin rdpdrPlugin;
 
+enum RDPDR_CHANNEL_STATE
+{
+	RDPDR_CHANNEL_STATE_INITIAL = 0,
+	RDPDR_CHANNEL_STATE_ANNOUNCE,
+	RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY,
+	RDPDR_CHANNEL_STATE_NAME_REQUEST,
+	RDPDR_CHANNEL_STATE_SERVER_CAPS,
+	RDPDR_CHANNEL_STATE_CLIENT_CAPS,
+	RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM,
+	RDPDR_CHANNEL_STATE_READY,
+	RDPDR_CHANNEL_STATE_USER_LOGGEDON
+};
+
 struct rdpdr_plugin
 {
 	CHANNEL_DEF channelDef;
 	CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
 
+	enum RDPDR_CHANNEL_STATE state;
 	HANDLE thread;
 	wStream* data_in;
 	void* InitHandle;
@@ -59,12 +77,33 @@ struct rdpdr_plugin
 
 	DEVMAN* devman;
 
-	UINT16 versionMajor;
-	UINT16 versionMinor;
-	UINT16 clientID;
+	UINT32 serverOsType;
+	UINT32 serverOsVersion;
+	UINT16 serverVersionMajor;
+	UINT16 serverVersionMinor;
+	UINT32 serverExtendedPDU;
+	UINT32 serverIOCode1;
+	UINT32 serverIOCode2;
+	UINT32 serverExtraFlags1;
+	UINT32 serverExtraFlags2;
+	UINT32 serverSpecialTypeDeviceCap;
+
+	UINT32 clientOsType;
+	UINT32 clientOsVersion;
+	UINT16 clientVersionMajor;
+	UINT16 clientVersionMinor;
+	UINT32 clientExtendedPDU;
+	UINT32 clientIOCode1;
+	UINT32 clientIOCode2;
+	UINT32 clientExtraFlags1;
+	UINT32 clientExtraFlags2;
+	UINT32 clientSpecialTypeDeviceCap;
+
+	UINT32 clientID;
 	char computerName[256];
 
 	UINT32 sequenceId;
+	BOOL userLoggedOn;
 
 	/* hotplug support */
 	HANDLE hotplugThread;
@@ -78,8 +117,10 @@ struct rdpdr_plugin
 	HANDLE stopEvent;
 #endif
 	rdpContext* rdpcontext;
+	wLog* log;
 };
 
+BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next);
 UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s);
 
 #endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */
diff --git a/channels/rdpdr/server/rdpdr_main.h b/channels/rdpdr/server/rdpdr_main.h
index f3f54cc..c1ec534 100644
--- a/channels/rdpdr/server/rdpdr_main.h
+++ b/channels/rdpdr/server/rdpdr_main.h
@@ -57,13 +57,6 @@ struct _RDPDR_HEADER
 };
 typedef struct _RDPDR_HEADER RDPDR_HEADER;
 
-#define RDPDR_VERSION_MAJOR 0x0001
-
-#define RDPDR_VERSION_MINOR_RDP50 0x0002
-#define RDPDR_VERSION_MINOR_RDP51 0x0005
-#define RDPDR_VERSION_MINOR_RDP52 0x000A
-#define RDPDR_VERSION_MINOR_RDP6X 0x000C
-
 #define RDPDR_CAPABILITY_HEADER_LENGTH 8
 
 struct _RDPDR_CAPABILITY_HEADER
diff --git a/channels/rdpecam/server/camera_device_main.c b/channels/rdpecam/server/camera_device_main.c
index 00bea94..941a288 100644
--- a/channels/rdpecam/server/camera_device_main.c
+++ b/channels/rdpecam/server/camera_device_main.c
@@ -686,8 +686,6 @@ static wStream* device_server_packet_new(size_t size, BYTE version, BYTE message
 {
 	wStream* s;
 
-	WINPR_ASSERT(size > 0);
-
 	/* Allocate what we need plus header bytes */
 	s = Stream_New(NULL, size + CAM_HEADER_SIZE);
 	if (!s)
diff --git a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c
index 7a95470..2fd4d7d 100644
--- a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c
+++ b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c
@@ -23,11 +23,15 @@
 #include "config.h"
 #endif
 
+#include <errno.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 
 #include <winpr/crt.h>
+#include <winpr/assert.h>
 #include <winpr/stream.h>
 #include <winpr/cmdline.h>
 
@@ -51,8 +55,39 @@ struct rdpsnd_pulse_plugin
 	pa_stream* stream;
 	UINT32 latency;
 	UINT32 volume;
+	time_t reconnect_delay_seconds;
+	time_t reconnect_time;
 };
 
+static BOOL rdpsnd_check_pulse(rdpsndPulsePlugin* pulse, BOOL haveStream)
+{
+	BOOL rc = TRUE;
+	WINPR_ASSERT(pulse);
+
+	if (!pulse->context)
+	{
+		WLog_WARN(TAG, "pulse->context=%p", pulse->context);
+		rc = FALSE;
+	}
+
+	if (haveStream)
+	{
+		if (!pulse->stream)
+		{
+			WLog_WARN(TAG, "pulse->stream=%p", pulse->stream);
+			rc = FALSE;
+		}
+	}
+
+	if (!pulse->mainloop)
+	{
+		WLog_WARN(TAG, "pulse->mainloop=%p", pulse->mainloop);
+		rc = FALSE;
+	}
+
+	return rc;
+}
+
 static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
 
 static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol,
@@ -65,7 +100,8 @@ static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int
 	;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
 
-	if (!pulse || !c || !i)
+	WINPR_ASSERT(c);
+	if (!rdpsnd_check_pulse(pulse, FALSE) || !i)
 		return;
 
 	for (x = 0; x < i->volume.channels; x++)
@@ -97,6 +133,10 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
 {
 	pa_context_state_t state;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
+
+	WINPR_ASSERT(context);
+	WINPR_ASSERT(pulse);
+
 	state = pa_context_get_state(context);
 
 	switch (state)
@@ -106,6 +146,14 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
 			break;
 
 		case PA_CONTEXT_FAILED:
+			// Destroy context now, create new one for next connection attempt
+			pa_context_unref(pulse->context);
+			pulse->context = NULL;
+			if (pulse->reconnect_delay_seconds >= 0)
+				pulse->reconnect_time = time(NULL) + pulse->reconnect_delay_seconds;
+			pa_threaded_mainloop_signal(pulse->mainloop, 0);
+			break;
+
 		case PA_CONTEXT_TERMINATED:
 			pa_threaded_mainloop_signal(pulse->mainloop, 0);
 			break;
@@ -117,21 +165,17 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd
 
 static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
 {
+	BOOL rc;
 	pa_operation* o;
 	pa_context_state_t state;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
-	if (!pulse->context)
-		return FALSE;
-
-	if (pa_context_connect(pulse->context, NULL, 0, NULL))
-	{
+	if (!rdpsnd_check_pulse(pulse, FALSE))
 		return FALSE;
-	}
 
 	pa_threaded_mainloop_lock(pulse->mainloop);
 
-	if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
+	if (pa_context_connect(pulse->context, NULL, 0, NULL) < 0)
 	{
 		pa_threaded_mainloop_unlock(pulse->mainloop);
 		return FALSE;
@@ -157,27 +201,35 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
 	if (o)
 		pa_operation_unref(o);
 
-	pa_threaded_mainloop_unlock(pulse->mainloop);
-
 	if (state == PA_CONTEXT_READY)
 	{
-		return TRUE;
+		rc = TRUE;
 	}
 	else
 	{
 		pa_context_disconnect(pulse->context);
-		return FALSE;
+		rc = FALSE;
 	}
+
+	pa_threaded_mainloop_unlock(pulse->mainloop);
+	return rc;
 }
 
 static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
 {
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
+
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+		return;
+
 	pa_threaded_mainloop_signal(pulse->mainloop, 0);
 }
 
 static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
 {
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+		return;
+
 	if (!operation)
 		return;
 
@@ -193,6 +245,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
 {
 	pa_stream_state_t state;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
+
+	WINPR_ASSERT(stream);
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+		return;
+
 	state = pa_stream_get_state(stream);
 
 	switch (state)
@@ -203,6 +260,8 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
 
 		case PA_STREAM_FAILED:
 		case PA_STREAM_TERMINATED:
+			// Stream object is about to be destroyed, clean up our pointer
+			pulse->stream = NULL;
 			pa_threaded_mainloop_signal(pulse->mainloop, 0);
 			break;
 
@@ -214,6 +273,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
 static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
 {
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata;
+
+	WINPR_ASSERT(stream);
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+		return;
+
 	pa_threaded_mainloop_signal(pulse->mainloop, 0);
 }
 
@@ -221,15 +285,20 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
 {
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
-	if (!pulse->context || !pulse->stream)
+	WINPR_ASSERT(pulse);
+
+	if (!rdpsnd_check_pulse(pulse, FALSE))
 		return;
 
 	pa_threaded_mainloop_lock(pulse->mainloop);
-	rdpsnd_pulse_wait_for_operation(
-	    pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
-	pa_stream_disconnect(pulse->stream);
-	pa_stream_unref(pulse->stream);
-	pulse->stream = NULL;
+	if (pulse->stream)
+	{
+		rdpsnd_pulse_wait_for_operation(
+		    pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
+		pa_stream_disconnect(pulse->stream);
+		pa_stream_unref(pulse->stream);
+		pulse->stream = NULL;
+	}
 	pa_threaded_mainloop_unlock(pulse->mainloop);
 }
 
@@ -237,7 +306,9 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
 {
 	pa_sample_spec sample_spec = { 0 };
 
-	if (!pulse->context)
+	WINPR_ASSERT(format);
+
+	if (!rdpsnd_check_pulse(pulse, FALSE))
 		return FALSE;
 
 	if (!rdpsnd_pulse_format_supported(&pulse->device, format))
@@ -281,30 +352,52 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F
 	return TRUE;
 }
 
-static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
-                              UINT32 latency)
+static BOOL rdpsnd_pulse_context_connect(rdpsndDevicePlugin* device)
 {
-	pa_stream_state_t state;
-	pa_stream_flags_t flags;
-	pa_buffer_attr buffer_attr = { 0 };
-	char ss[PA_SAMPLE_SPEC_SNPRINT_MAX];
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
-	if (!pulse->context || pulse->stream)
-		return TRUE;
+	pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
 
-	if (!rdpsnd_pulse_set_format_spec(pulse, format))
+	if (!pulse->context)
 		return FALSE;
 
-	pulse->latency = latency;
+	pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
+
+	if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
+		return FALSE;
+
+	return TRUE;
+}
+
+static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device)
+{
+	pa_stream_state_t state;
+	pa_stream_flags_t flags;
+	pa_buffer_attr buffer_attr = { 0 };
+	char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = { 0 };
+	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
 	if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
 	{
 		pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec);
-		return TRUE;
+		return FALSE;
 	}
 
 	pa_threaded_mainloop_lock(pulse->mainloop);
+	if (!pulse->context)
+	{
+		pa_threaded_mainloop_unlock(pulse->mainloop);
+		if (pulse->reconnect_delay_seconds >= 0 && time(NULL) - pulse->reconnect_time >= 0)
+			rdpsnd_pulse_context_connect(device);
+		pa_threaded_mainloop_lock(pulse->mainloop);
+	}
+
+	if (!rdpsnd_check_pulse(pulse, FALSE))
+	{
+		pa_threaded_mainloop_unlock(pulse->mainloop);
+		return FALSE;
+	}
+
 	pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
 
 	if (!pulse->stream)
@@ -320,19 +413,22 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
 
 	if (pulse->latency > 0)
 	{
-		buffer_attr.maxlength = pa_usec_to_bytes(pulse->latency * 2 * 1000, &pulse->sample_spec);
+		buffer_attr.maxlength = UINT32_MAX;
 		buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec);
-		buffer_attr.prebuf = (UINT32)-1;
-		buffer_attr.minreq = (UINT32)-1;
-		buffer_attr.fragsize = (UINT32)-1;
+		buffer_attr.prebuf = UINT32_MAX;
+		buffer_attr.minreq = UINT32_MAX;
+		buffer_attr.fragsize = UINT32_MAX;
 		flags |= PA_STREAM_ADJUST_LATENCY;
 	}
 
 	if (pa_stream_connect_playback(pulse->stream, pulse->device_name,
 	                               pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
 	{
+		WLog_ERR(TAG, "error connecting playback stream");
+		pa_stream_unref(pulse->stream);
+		pulse->stream = NULL;
 		pa_threaded_mainloop_unlock(pulse->mainloop);
-		return TRUE;
+		return FALSE;
 	}
 
 	for (;;)
@@ -359,6 +455,24 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo
 	return FALSE;
 }
 
+static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
+                              UINT32 latency)
+{
+	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
+
+	WINPR_ASSERT(format);
+
+	if (!rdpsnd_check_pulse(pulse, FALSE))
+		return TRUE;
+
+	if (!rdpsnd_pulse_set_format_spec(pulse, format))
+		return FALSE;
+
+	pulse->latency = latency;
+
+	return rdpsnd_pulse_open_stream(device);
+}
+
 static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
 {
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
@@ -369,9 +483,7 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
 	rdpsnd_pulse_close(device);
 
 	if (pulse->mainloop)
-	{
 		pa_threaded_mainloop_stop(pulse->mainloop);
-	}
 
 	if (pulse->context)
 	{
@@ -394,6 +506,7 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
                                         AUDIO_FORMAT* defaultFormat)
 {
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
+
 	if (!pulse || !defaultFormat)
 		return FALSE;
 
@@ -415,6 +528,9 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_
 
 BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
 {
+	WINPR_ASSERT(device);
+	WINPR_ASSERT(format);
+
 	switch (format->wFormatTag)
 	{
 		case WAVE_FORMAT_PCM:
@@ -439,39 +555,51 @@ static UINT32 rdpsnd_pulse_get_volume(rdpsndDevicePlugin* device)
 	pa_operation* o;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
-	if (!pulse)
-		return 0;
-
-	if (!pulse->context || !pulse->mainloop)
+	if (!rdpsnd_check_pulse(pulse, FALSE))
 		return 0;
 
 	pa_threaded_mainloop_lock(pulse->mainloop);
 	o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse);
-	pa_operation_unref(o);
+	if (o)
+		pa_operation_unref(o);
 	pa_threaded_mainloop_unlock(pulse->mainloop);
 	return pulse->volume;
 }
 
+static void rdpsnd_set_volume_success_cb(pa_context* c, int success, void* userdata)
+{
+	rdpsndPulsePlugin* pulse = userdata;
+
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+		return;
+	WINPR_ASSERT(c);
+
+	WLog_INFO(TAG, "%s: %d", __FUNCTION__, success);
+}
+
 static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
 {
-	pa_cvolume cv;
+	pa_cvolume cv = { 0 };
 	pa_volume_t left;
 	pa_volume_t right;
 	pa_operation* operation;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
-	if (!pulse->context || !pulse->stream)
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+	{
+		WLog_WARN(TAG, "%s called before pulse backend was initialized");
 		return FALSE;
+	}
 
 	left = (pa_volume_t)(value & 0xFFFF);
 	right = (pa_volume_t)((value >> 16) & 0xFFFF);
 	pa_cvolume_init(&cv);
 	cv.channels = 2;
-	cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
-	cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
+	cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
+	cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM;
 	pa_threaded_mainloop_lock(pulse->mainloop);
 	operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
-	                                             &cv, NULL, NULL);
+	                                             &cv, rdpsnd_set_volume_success_cb, pulse);
 
 	if (operation)
 		pa_operation_unref(operation);
@@ -483,28 +611,38 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
 static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
 {
 	size_t length;
+	void* pa_data;
 	int status;
 	pa_usec_t latency;
 	int negative;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
 
-	if (!pulse->stream || !data)
+	if (!data)
 		return 0;
 
 	pa_threaded_mainloop_lock(pulse->mainloop);
 
+	if (!rdpsnd_check_pulse(pulse, TRUE))
+	{
+		pa_threaded_mainloop_unlock(pulse->mainloop);
+		// Discard this playback request and just attempt to reconnect the stream
+		WLog_DBG(TAG, "reconnecting playback stream");
+		rdpsnd_pulse_open_stream(device);
+		return 0;
+	}
+
 	while (size > 0)
 	{
-		while ((length = pa_stream_writable_size(pulse->stream)) == 0)
-			pa_threaded_mainloop_wait(pulse->mainloop);
+		length = size;
 
-		if (length == (size_t)-1)
+		status = pa_stream_begin_write(pulse->stream, &pa_data, &length);
+
+		if (status < 0)
 			break;
 
-		if (length > size)
-			length = size;
+		memcpy(pa_data, data, length);
 
-		status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
+		status = pa_stream_write(pulse->stream, pa_data, length, NULL, 0LL, PA_SEEK_RELATIVE);
 
 		if (status < 0)
 		{
@@ -522,22 +660,24 @@ static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size
 	return latency / 1000;
 }
 
-/**
- * Function description
- *
- * @return 0 on success, otherwise a Win32 error code
- */
 static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
 {
 	int status;
 	DWORD flags;
 	COMMAND_LINE_ARGUMENT_A* arg;
 	rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
-	COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED,
-		                                              "<device>", NULL, NULL, -1, NULL, "device" },
-		                                            { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
+	COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = {
+		{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
+		{ "reconnect_delay_seconds", COMMAND_LINE_VALUE_REQUIRED, "<reconnect_delay_seconds>", NULL,
+		  NULL, -1, NULL, "reconnect_delay_seconds" },
+		{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
+	};
 	flags =
 	    COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
+
+	WINPR_ASSERT(pulse);
+	WINPR_ASSERT(args);
+
 	status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse,
 	                                    NULL, NULL);
 
@@ -558,6 +698,15 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
 			if (!pulse->device_name)
 				return ERROR_OUTOFMEMORY;
 		}
+		CommandLineSwitchCase(arg, "reconnect_delay_seconds")
+		{
+			unsigned long val = strtoul(arg->Value, NULL, 0);
+
+			if ((errno != 0) || (val > INT32_MAX))
+				return ERROR_INVALID_DATA;
+
+			pulse->reconnect_delay_seconds = val;
+		}
 		CommandLineSwitchEnd(arg)
 	} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
 
@@ -570,16 +719,14 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
 #define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry
 #endif
 
-/**
- * Function description
- *
- * @return 0 on success, otherwise a Win32 error code
- */
 UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
 {
 	ADDIN_ARGV* args;
 	rdpsndPulsePlugin* pulse;
 	UINT ret;
+
+	WINPR_ASSERT(pEntryPoints);
+
 	pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin));
 
 	if (!pulse)
@@ -605,6 +752,8 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
 			goto error;
 		}
 	}
+	pulse->reconnect_delay_seconds = 5;
+	pulse->reconnect_time = time(NULL);
 
 	ret = CHANNEL_RC_NO_MEMORY;
 	pulse->mainloop = pa_threaded_mainloop_new();
@@ -612,15 +761,17 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
 	if (!pulse->mainloop)
 		goto error;
 
-	pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
+	pa_threaded_mainloop_lock(pulse->mainloop);
 
-	if (!pulse->context)
-		goto error;
+	if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
+	{
+		pa_threaded_mainloop_unlock(pulse->mainloop);
+		return FALSE;
+	}
 
-	pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
-	ret = ERROR_INVALID_OPERATION;
+	pa_threaded_mainloop_unlock(pulse->mainloop);
 
-	if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
+	if (!rdpsnd_pulse_context_connect((rdpsndDevicePlugin*)pulse))
 		goto error;
 
 	pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse);
diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c
index f624058..3de4cbc 100644
--- a/channels/rdpsnd/client/rdpsnd_main.c
+++ b/channels/rdpsnd/client/rdpsnd_main.c
@@ -126,6 +126,10 @@ struct rdpsnd_plugin
 	HANDLE thread;
 	wMessageQueue* queue;
 	BOOL initialized;
+
+	UINT16 wVersion;
+	UINT32 volume;
+	BOOL applyVolume;
 };
 
 static const char* rdpsnd_is_dyn_str(BOOL dynamic)
@@ -268,9 +272,9 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
 static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
 {
 	UINT16 index;
-	UINT16 wVersion;
 	UINT16 wNumberOfFormats;
 	UINT ret = ERROR_BAD_LENGTH;
+
 	audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
 	rdpsnd->NumberOfServerFormats = 0;
 	rdpsnd->ServerFormats = NULL;
@@ -285,7 +289,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
 	Stream_Seek_UINT16(s); /* wDGramPort */
 	Stream_Read_UINT16(s, wNumberOfFormats);
 	Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
-	Stream_Read_UINT16(s, wVersion);        /* wVersion */
+	Stream_Read_UINT16(s, rdpsnd->wVersion); /* wVersion */
 	Stream_Seek_UINT8(s);                   /* bPad */
 	rdpsnd->NumberOfServerFormats = wNumberOfFormats;
 
@@ -312,7 +316,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream*
 
 	if (ret == CHANNEL_RC_OK)
 	{
-		if (wVersion >= CHANNEL_VERSION_WIN_7)
+		if (rdpsnd->wVersion >= CHANNEL_VERSION_WIN_7)
 			ret = rdpsnd_send_quality_mode_pdu(rdpsnd);
 	}
 
@@ -373,6 +377,20 @@ static UINT rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s)
 	return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
 }
 
+static BOOL rdpsnd_apply_volume(rdpsndPlugin* rdpsnd)
+{
+	WINPR_ASSERT(rdpsnd);
+
+	if (rdpsnd->isOpen && rdpsnd->applyVolume && rdpsnd->device)
+	{
+		BOOL rc = IFCALLRESULT(TRUE, rdpsnd->device->SetVolume, rdpsnd->device, rdpsnd->volume);
+		if (!rc)
+			return FALSE;
+		rdpsnd->applyVolume = FALSE;
+	}
+	return TRUE;
+}
+
 static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
                                          const AUDIO_FORMAT* format)
 {
@@ -421,7 +439,7 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo,
 		rdpsnd->totalPlaySize = 0;
 	}
 
-	return TRUE;
+	return rdpsnd_apply_volume(rdpsnd);
 }
 
 /**
@@ -710,7 +728,7 @@ static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
  */
 static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
 {
-	BOOL rc = FALSE;
+	BOOL rc = TRUE;
 	UINT32 dwVolume;
 
 	if (Stream_GetRemainingLength(s) < 4)
@@ -719,8 +737,10 @@ static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
 	Stream_Read_UINT32(s, dwVolume);
 	WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "",
 	           rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume);
-	if (rdpsnd->device)
-		rc = IFCALLRESULT(FALSE, rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
+
+	rdpsnd->volume = dwVolume;
+	rdpsnd->applyVolume = TRUE;
+	rc = rdpsnd_apply_volume(rdpsnd);
 
 	if (!rc)
 	{
diff --git a/channels/rdpsnd/server/rdpsnd_main.c b/channels/rdpsnd/server/rdpsnd_main.c
index 363fac7..a26c3cf 100644
--- a/channels/rdpsnd/server/rdpsnd_main.c
+++ b/channels/rdpsnd/server/rdpsnd_main.c
@@ -523,7 +523,7 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
 	Stream_Seek(s, 3);                                       /* bPad */
 	start = Stream_GetPosition(s);
 	src = context->priv->out_buffer;
-	length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame * 1ULL;
+	length = 1ull * context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
 
 	if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
 		return ERROR_INTERNAL_ERROR;
diff --git a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
index c14877b..2153374 100644
--- a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
+++ b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c
@@ -235,8 +235,10 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP
 		}
 	}
 
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100)
 	if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED)
 		mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED;
+#endif
 
 	return TRUE;
 }
diff --git a/client/Wayland/wlf_input.c b/client/Wayland/wlf_input.c
index 22041ea..735245b 100644
--- a/client/Wayland/wlf_input.c
+++ b/client/Wayland/wlf_input.c
@@ -134,26 +134,44 @@ BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent*
 }
 
 BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
+{
+	wlfContext* context;
+	if (!instance || !instance->context || !ev)
+		return FALSE;
+
+	context = (wlfContext*)instance->context;
+	ArrayList_Add(context->events, ev);
+	return TRUE;
+}
+
+BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev)
+{
+	wlfContext* context;
+	if (!instance || !instance->context || !ev)
+		return FALSE;
+
+	context = (wlfContext*)instance->context;
+	ArrayList_Add(context->events, ev);
+	return TRUE;
+}
+
+static BOOL wlf_handle_wheel(freerdp* instance, uint32_t x, uint32_t y, uint32_t axis,
+                             int32_t value)
 {
 	rdpInput* input;
 	UINT16 flags = 0;
 	int32_t direction;
-	uint32_t x, y;
-	uint32_t i;
+	uint32_t avalue = abs(value);
 
-	if (!instance || !ev || !instance->input)
-		return FALSE;
-
-	x = ev->x;
-	y = ev->y;
+	input = instance->input;
 
 	if (!wlf_scale_coordinates(instance->context, &x, &y, TRUE))
 		return FALSE;
 
 	input = instance->input;
 
-	direction = ev->value;
-	switch (ev->axis)
+	direction = value;
+	switch (axis)
 	{
 		case WL_POINTER_AXIS_VERTICAL_SCROLL:
 			flags |= PTR_FLAGS_WHEEL;
@@ -176,16 +194,102 @@ BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev)
 	 * positive: 0 ... 0xFF  -> slow ... fast
 	 * negative: 0 ... 0xFF  -> fast ... slow
 	 */
-	for (i = 0; i < abs(direction); i++)
+
+	while (avalue > 0)
 	{
-		uint32_t cflags = flags | 0x78;
+		const uint32_t cval = avalue > 0xFF ? 0xFF : avalue;
+		uint32_t cflags = flags | cval;
 		/* Convert negative values to 9bit twos complement */
 		if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
-			cflags = (flags & 0xFF00) | (0x100 - (cflags & 0xFF));
+			cflags = (flags & 0xFF00) | (0x100 - cval);
 		if (!freerdp_input_send_mouse_event(input, cflags, (UINT16)x, (UINT16)y))
 			return FALSE;
+
+		avalue -= cval;
 	}
+	return TRUE;
+}
 
+BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev)
+{
+	BOOL success = TRUE;
+	BOOL handle = FALSE;
+	size_t x;
+	wlfContext* context;
+	enum wl_pointer_axis_source source;
+
+	if (!instance || !ev || !instance->input || !instance->context)
+		return FALSE;
+
+	context = (wlfContext*)instance->context;
+
+	for (x = 0; x < ArrayList_Count(context->events); x++)
+	{
+		UwacEvent* ev = ArrayList_GetItem(context->events, x);
+		if (!ev)
+			continue;
+		if (ev->type == UWAC_EVENT_POINTER_SOURCE)
+		{
+			handle = TRUE;
+			source = ev->mouse_source.axis_source;
+		}
+	}
+
+	/* We need source events to determine how to interpret the data */
+	if (handle)
+	{
+		for (x = 0; x < ArrayList_Count(context->events); x++)
+		{
+			UwacEvent* ev = ArrayList_GetItem(context->events, x);
+			if (!ev)
+				continue;
+
+			switch (source)
+			{
+				/* If we have a mouse wheel, just use discrete data */
+				case WL_POINTER_AXIS_SOURCE_WHEEL:
+#if defined(WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION)
+				case WL_POINTER_AXIS_SOURCE_WHEEL_TILT:
+#endif
+					if (ev->type == UWAC_EVENT_POINTER_AXIS_DISCRETE)
+					{
+						/* Get the number of steps, multiply by default step width of 120 */
+						int32_t val = ev->mouse_axis.value * 0x78;
+						/* No wheel event received, success! */
+						if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y,
+						                      ev->mouse_axis.axis, val))
+							success = FALSE;
+					}
+					break;
+					/* If we have a touch pad we get actual data, scale */
+				case WL_POINTER_AXIS_SOURCE_FINGER:
+				case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
+					if (ev->type == UWAC_EVENT_POINTER_AXIS)
+					{
+						double dval = wl_fixed_to_double(ev->mouse_axis.value);
+						int32_t val = dval * 0x78 / 10.0;
+						if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y,
+						                      ev->mouse_axis.axis, val))
+							success = FALSE;
+					}
+					break;
+				default:
+					break;
+			}
+		}
+	}
+	ArrayList_Clear(context->events);
+	return success;
+}
+
+BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev)
+{
+	wlfContext* context;
+	if (!instance || !instance->context || !ev)
+		return FALSE;
+
+	context = (wlfContext*)instance->context;
+	ArrayList_Add(context->events, ev);
 	return TRUE;
 }
 
diff --git a/client/Wayland/wlf_input.h b/client/Wayland/wlf_input.h
index 2382dd8..ff41300 100644
--- a/client/Wayland/wlf_input.h
+++ b/client/Wayland/wlf_input.h
@@ -30,6 +30,9 @@ BOOL wlf_handle_pointer_enter(freerdp* instance, const UwacPointerEnterLeaveEven
 BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev);
 BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev);
 BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev);
+BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev);
+BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev);
+BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev);
 BOOL wlf_handle_touch_up(freerdp* instance, const UwacTouchUp* ev);
 BOOL wlf_handle_touch_down(freerdp* instance, const UwacTouchDown* ev);
 BOOL wlf_handle_touch_motion(freerdp* instance, const UwacTouchMotion* ev);
diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c
index c7a9fca..65e29bc 100644
--- a/client/Wayland/wlfreerdp.c
+++ b/client/Wayland/wlfreerdp.c
@@ -362,12 +362,22 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display)
 				break;
 
 			case UWAC_EVENT_POINTER_AXIS:
+				if (!wlf_handle_pointer_axis(instance, &event.mouse_axis))
+					return FALSE;
 				break;
 
 			case UWAC_EVENT_POINTER_AXIS_DISCRETE:
-				if (!wlf_handle_pointer_axis(instance, &event.mouse_axis))
+				if (!wlf_handle_pointer_axis_discrete(instance, &event.mouse_axis))
 					return FALSE;
+				break;
 
+			case UWAC_EVENT_POINTER_FRAME:
+				if (!wlf_handle_pointer_frame(instance, &event.mouse_frame))
+					return FALSE;
+				break;
+			case UWAC_EVENT_POINTER_SOURCE:
+				if (!wlf_handle_pointer_source(instance, &event.mouse_source))
+					return FALSE;
 				break;
 
 			case UWAC_EVENT_KEY:
@@ -561,8 +571,37 @@ static int wlf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
 	return 1;
 }
 
+static void wlf_client_free(freerdp* instance, rdpContext* context)
+{
+	wlfContext* wlf = (wlfContext*)instance->context;
+
+	if (!context)
+		return;
+
+	if (wlf->display)
+		UwacCloseDisplay(&wlf->display);
+
+	if (wlf->displayHandle)
+		CloseHandle(wlf->displayHandle);
+	ArrayList_Free(wlf->events);
+	DeleteCriticalSection(&wlf->critical);
+}
+
+static void* uwac_event_clone(const void* val)
+{
+	UwacEvent* copy;
+	UwacEvent* ev = (UwacEvent*)val;
+
+	copy = calloc(1, sizeof(UwacEvent));
+	if (!copy)
+		return NULL;
+	*copy = *ev;
+	return copy;
+}
+
 static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
 {
+	wObject* obj;
 	UwacReturnCode status;
 	wlfContext* wfl = (wlfContext*)context;
 
@@ -590,24 +629,17 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
 	if (!wfl->displayHandle)
 		return FALSE;
 
-	InitializeCriticalSection(&wfl->critical);
-
-	return TRUE;
-}
-
-static void wlf_client_free(freerdp* instance, rdpContext* context)
-{
-	wlfContext* wlf = (wlfContext*)instance->context;
+	wfl->events = ArrayList_New(FALSE);
+	if (!wfl->events)
+		return FALSE;
 
-	if (!context)
-		return;
+	obj = ArrayList_Object(wfl->events);
+	obj->fnObjectNew = uwac_event_clone;
+	obj->fnObjectFree = free;
 
-	if (wlf->display)
-		UwacCloseDisplay(&wlf->display);
+	InitializeCriticalSection(&wfl->critical);
 
-	if (wlf->displayHandle)
-		CloseHandle(wlf->displayHandle);
-	DeleteCriticalSection(&wlf->critical);
+	return TRUE;
 }
 
 static int wfl_client_start(rdpContext* context)
diff --git a/client/Wayland/wlfreerdp.h b/client/Wayland/wlfreerdp.h
index d84f91c..6af13bb 100644
--- a/client/Wayland/wlfreerdp.h
+++ b/client/Wayland/wlfreerdp.h
@@ -53,6 +53,7 @@ struct wlf_context
 	wlfDispContext* disp;
 	wLog* log;
 	CRITICAL_SECTION critical;
+	wArrayList* events;
 };
 
 BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fromLocalToRDP);
diff --git a/client/X11/xf_gdi.c b/client/X11/xf_gdi.c
index 4f52853..aecd389 100644
--- a/client/X11/xf_gdi.c
+++ b/client/X11/xf_gdi.c
@@ -1066,7 +1066,7 @@ static BOOL xf_gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND*
 		case RDP_CODEC_ID_NONE:
 			pSrcData = cmd->bmp.bitmapData;
 			format = gdi_get_pixel_format(cmd->bmp.bpp);
-			size = cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format) * 1ULL;
+			size = 1ull * cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format);
 			if (size > cmd->bmp.bitmapDataLength)
 			{
 				WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz,
diff --git a/client/X11/xf_gfx.c b/client/X11/xf_gfx.c
index 97d3ad3..3863d66 100644
--- a/client/X11/xf_gfx.c
+++ b/client/X11/xf_gfx.c
@@ -288,7 +288,7 @@ static UINT xf_CreateSurface(RdpgfxClientContext* context,
 
 	surface->gdi.scanline = surface->gdi.width * GetBytesPerPixel(surface->gdi.format);
 	surface->gdi.scanline = x11_pad_scanline(surface->gdi.scanline, xfc->scanline_pad);
-	size = surface->gdi.scanline * surface->gdi.height * 1ULL;
+	size = 1ull * surface->gdi.scanline * surface->gdi.height;
 	surface->gdi.data = (BYTE*)_aligned_malloc(size, 16);
 
 	if (!surface->gdi.data)
@@ -312,7 +312,7 @@ static UINT xf_CreateSurface(RdpgfxClientContext* context,
 		UINT32 bytes = GetBytesPerPixel(gdi->dstFormat);
 		surface->stageScanline = width * bytes;
 		surface->stageScanline = x11_pad_scanline(surface->stageScanline, xfc->scanline_pad);
-		size = surface->stageScanline * surface->gdi.height * 1ULL;
+		size = 1ull * surface->stageScanline * surface->gdi.height;
 		surface->stage = (BYTE*)_aligned_malloc(size, 16);
 
 		if (!surface->stage)
diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c
index 82b0b95..5aa1fd4 100644
--- a/client/X11/xf_graphics.c
+++ b/client/X11/xf_graphics.c
@@ -311,7 +311,7 @@ static BOOL xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPo
 		ci.height = yTargetSize;
 		ci.xhot = pointer->xPos * xscale;
 		ci.yhot = pointer->yPos * yscale;
-		size = ci.height * ci.width * GetBytesPerPixel(CursorFormat) * 1ULL;
+		size = 1ull * ci.height * ci.width * GetBytesPerPixel(CursorFormat);
 
 		if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16)))
 		{
@@ -421,7 +421,7 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
 	xpointer->nCursors = 0;
 	xpointer->mCursors = 0;
 
-	size = pointer->height * pointer->width * GetBytesPerPixel(CursorFormat) * 1ULL;
+	size = 1ull * pointer->height * pointer->width * GetBytesPerPixel(CursorFormat);
 
 	if (!(xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16)))
 		goto fail;
@@ -560,7 +560,7 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
 	}
 
 	WLog_DBG(TAG, "%s: %" PRIu32 "x%" PRIu32, __func__, x, y);
-	if (xfc->remote_app && !xfc->focused)
+	if (!xfc->focused)
 		return TRUE;
 
 	xf_adjust_coordinates_to_screen(xfc, &x, &y);
@@ -583,7 +583,7 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y)
 		goto out;
 	}
 
-	rc = XWarpPointer(xfc->display, None, handle, 0, 0, 0, 0, x, y);
+	rc = XWarpPointer(xfc->display, handle, handle, 0, 0, 0, 0, x, y);
 	if (rc == 0)
 		WLog_WARN(TAG, "%s: XWarpPointer==%d", __func__, rc);
 	tmp.event_mask = current.your_event_mask;
diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c
index 090f599..6995bdd 100644
--- a/client/X11/xf_rail.c
+++ b/client/X11/xf_rail.c
@@ -544,7 +544,7 @@ static xfRailIconCache* RailIconCache_New(rdpSettings* settings)
 
 	cache->numCaches = settings->RemoteAppNumIconCaches;
 	cache->numCacheEntries = settings->RemoteAppNumIconCacheEntries;
-	cache->entries = calloc(cache->numCaches * cache->numCacheEntries * 1ULL, sizeof(xfRailIcon));
+	cache->entries = calloc(1ull * cache->numCaches * cache->numCacheEntries, sizeof(xfRailIcon));
 
 	if (!cache->entries)
 	{
@@ -614,7 +614,7 @@ static BOOL convert_rail_icon(const ICON_INFO* iconInfo, xfRailIcon* railIcon)
 	long* pixels;
 	int i;
 	int nelements;
-	argbPixels = calloc(iconInfo->width * iconInfo->height * 1ULL, 4);
+	argbPixels = calloc(1ull * iconInfo->width * iconInfo->height, 4);
 
 	if (!argbPixels)
 		goto error;
diff --git a/client/common/cmdline.c b/client/common/cmdline.c
index d2d949b..df5e8e8 100644
--- a/client/common/cmdline.c
+++ b/client/common/cmdline.c
@@ -2728,7 +2728,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
 			if (strcmp(arg->Value, "video") == 0)
 				settings->RemoteFxCodecMode = 0x00;
 			else if (strcmp(arg->Value, "image") == 0)
+			{
+				settings->RemoteFxImageCodec = TRUE;
 				settings->RemoteFxCodecMode = 0x02;
+			}
 		}
 		CommandLineSwitchCase(arg, "frame-ack")
 		{
@@ -2996,6 +2999,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
 		{
 			settings->GrabKeyboard = enable;
 		}
+		CommandLineSwitchCase(arg, "grab-mouse")
+		{
+			settings->GrabMouse = enable;
+		}
 		CommandLineSwitchCase(arg, "unmap-buttons")
 		{
 			settings->UnmapButtons = enable;
diff --git a/client/common/cmdline.h b/client/common/cmdline.h
index 92a86f9..6ecc8c5 100644
--- a/client/common/cmdline.h
+++ b/client/common/cmdline.h
@@ -184,6 +184,7 @@ static const COMMAND_LINE_ARGUMENT_A args[] = {
 	{ "gp", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Gateway password" },
 	{ "grab-keyboard", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
 	  "Grab keyboard" },
+	{ "grab-mouse", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Grab mouse" },
 	{ "gt", COMMAND_LINE_VALUE_REQUIRED, "[rpc|http[,no-websockets]|auto[,no-websockets]]", NULL,
 	  NULL, -1, NULL, "Gateway transport type" },
 	{ "gu", COMMAND_LINE_VALUE_REQUIRED, "[[<domain>\\]<user>|<user>[@<domain>]]", NULL, NULL, -1,
diff --git a/cmake/ClangDetectTool.cmake b/cmake/ClangDetectTool.cmake
new file mode 100644
index 0000000..a1ecd56
--- /dev/null
+++ b/cmake/ClangDetectTool.cmake
@@ -0,0 +1,48 @@
+function (clang_detect_tool VAR NAME OPTS)
+	set(NAMES "")
+	foreach(CNT RANGE 12 22)
+		list(APPEND NAMES "${NAME}-${CNT}")
+	endforeach()
+	list(REVERSE NAMES)
+	list(APPEND NAMES ${NAME})
+
+	find_program(${VAR}
+		NAMES ${NAMES}
+		${OPTS}
+	)
+	if (NOT ${VAR})
+		message(WARNING "clang tool ${NAME} (${VAR}) not detected, skipping")
+		unset(${VAR})
+		return()
+	endif()
+
+	execute_process(
+		COMMAND ${${VAR}} "--version"
+		OUTPUT_VARIABLE _CLANG_TOOL_VERSION
+		RESULT_VARIABLE _CLANG_TOOL_VERSION_FAILED
+	)
+
+	if (_CLANG_TOOL_VERSION_FAILED)
+		message(WARNING "A problem was encounterd with ${${VAR}}")
+		message(WARNING "${_CLANG_TOOL_VERSION_FAILED}")
+		unset(${VAR})
+		return()
+	endif()
+
+	string(REGEX MATCH "([7-9]|[1-9][0-9])\\.[0-9]\\.[0-9]" CLANG_TOOL_VERSION
+		"${_CLANG_TOOL_VERSION}")
+
+	if (NOT CLANG_TOOL_VERSION)
+		message(WARNING "problem parsing ${NAME} version for ${${VAR}}")
+		unset(${VAR})
+		return()
+	endif()
+
+	set(_CLANG_TOOL_MINIMUM_VERSION "12.0.0")
+	if (${CLANG_TOOL_VERSION} VERSION_LESS ${_CLANG_TOOL_MINIMUM_VERSION})
+		message(WARNING "clang-format version ${CLANG_TOOL_VERSION} not supported")
+		message(WARNING "Minimum version required: ${_CLANG_TOOL_MINIMUM_VERSION}")
+		unset(${VAR})
+		return()
+	endif()
+endfunction()
diff --git a/cmake/ClangFormat.cmake b/cmake/ClangFormat.cmake
index 8985ef3..b815822 100644
--- a/cmake/ClangFormat.cmake
+++ b/cmake/ClangFormat.cmake
@@ -1,43 +1,12 @@
 # get all project files
 file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.c *.h *.m *.java)
-# minimum version required
-set(_CLANG_FORMAT_MINIMUM_VERSION 7.0.0)
 
-find_program(CLANG_FORMAT
-	NAMES
-	clang-format-8
-	clang-format-7
-	clang-format
-	)
+include(ClangDetectTool)
+clang_detect_tool(CLANG_FORMAT clang-format "")
 
 if (NOT CLANG_FORMAT)
 	message(WARNING "clang-format not found in path! code format target not available.")
 else()
-	execute_process(
-		COMMAND ${CLANG_FORMAT} "--version"
-		OUTPUT_VARIABLE _CLANG_FORMAT_VERSION
-		RESULT_VARIABLE _CLANG_FORMAT_VERSION_FAILED
-		)
-
-	if (_CLANG_FORMAT_VERSION_FAILED)
-		message(WARNING "A problem was encounterd with ${CLANG_FORMAT}")
-		return()
-	endif()
-
-	string(REGEX MATCH "([7-9]|[1-9][0-9])\\.[0-9]\\.[0-9]" CLANG_FORMAT_VERSION
-		"${_CLANG_FORMAT_VERSION}")
-
-	if (NOT CLANG_FORMAT_VERSION)
-		message(WARNING "problem parsing clang-fromat version for ${CLANG_FORMAT}")
-		return()
-	endif()
-
-	if (${CLANG_FORMAT_VERSION} VERSION_LESS ${_CLANG_FORMAT_MINIMUM_VERSION})
-		message(WARNING "clang-format version ${CLANG_FORMAT_VERSION} not supported")
-		message(WARNING "Minimum version required: ${_CLANG_FORMAT_MINIMUM_VERSION}")
-		return()
-	endif()
-
 	add_custom_target(
 			clangformat
 			COMMAND ${CLANG_FORMAT}
diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake
index 1a6a359..f16a42c 100644
--- a/cmake/ConfigOptions.cmake
+++ b/cmake/ConfigOptions.cmake
@@ -47,12 +47,12 @@ if(NOT WIN32)
 	CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_ADDRESS "Compile with gcc/clang address sanitizer." OFF
 		"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_MEMORY; NOT WITH_SANITIZE_THREAD" OFF)
 	CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_MEMORY "Compile with gcc/clang memory sanitizer." OFF
-        "NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_THREAD" OFF)
+		"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_THREAD" OFF)
 	CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_THREAD "Compile with gcc/clang thread sanitizer." OFF
 		"NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_MEMORY" OFF)
 else()
 	if(NOT UWP)
-		option(WITH_MEDIA_FOUNDATION "Enable H264 media foundation decoder." ON)
+		option(WITH_MEDIA_FOUNDATION "Enable H264 media foundation decoder." OFF)
 	endif()
 endif()
 
@@ -98,7 +98,7 @@ option(WITH_SERVER_INTERFACE "Build servers as a library with an interface" ON)
 option(WITH_DEBUG_ALL "Print all debug messages." OFF)
 
 if(WITH_DEBUG_ALL)
-    message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!")
 	set(DEFAULT_DEBUG_OPTION "ON")
 else()
 	set(DEFAULT_DEBUG_OPTION "OFF")
@@ -106,7 +106,7 @@ endif()
 
 option(WITH_DEBUG_CERTIFICATE "Print certificate related debug messages." ${DEFAULT_DEBUG_OPTION})
 if(WITH_DEBUG_CERTIFICATE)
-    message(WARNING "WITH_DEBUG_CERTIFICATE=ON, the build might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_CERTIFICATE=ON, the build might leak sensitive information, do not use with release builds!")
 endif()
 option(WITH_DEBUG_CAPABILITIES "Print capability negotiation debug messages." ${DEFAULT_DEBUG_OPTION})
 option(WITH_DEBUG_CHANNELS "Print channel manager debug messages." ${DEFAULT_DEBUG_OPTION})
@@ -116,23 +116,23 @@ option(WITH_DEBUG_DVC "Print dynamic virtual channel debug messages." ${DEFAULT_
 CMAKE_DEPENDENT_OPTION(WITH_DEBUG_TSMF "Print TSMF virtual channel debug messages." ${DEFAULT_DEBUG_OPTION} "CHANNEL_TSMF" OFF)
 option(WITH_DEBUG_KBD "Print keyboard related debug messages." OFF)
 if(WITH_DEBUG_KBD)
-    message(WARNING "WITH_DEBUG_KBD=ON, the build might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_KBD=ON, the build might leak sensitive information, do not use with release builds!")
 endif()
 option(WITH_DEBUG_LICENSE "Print license debug messages." OFF)
 if(WITH_DEBUG_LICENSE)
-    message(WARNING "WITH_DEBUG_LICENSE=ON, the build might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_LICENSE=ON, the build might leak sensitive information, do not use with release builds!")
 endif()
 option(WITH_DEBUG_NEGO "Print negotiation related debug messages." OFF)
 if(WITH_DEBUG_NEGO)
-    message(WARNING "WITH_DEBUG_NEGO=ON, the build might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_NEGO=ON, the build might leak sensitive information, do not use with release builds!")
 endif()
 option(WITH_DEBUG_NLA "Print authentication related debug messages." OFF)
 if(WITH_DEBUG_NLA)
-    message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!")
 endif()
 option(WITH_DEBUG_NTLM "Print NTLM debug messages" OFF)
 if(WITH_DEBUG_NTLM)
-    message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!")
+	message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!")
 endif()
 option(WITH_DEBUG_TSG "Print Terminal Server Gateway debug messages" ${DEFAULT_DEBUG_OPTION})
 option(WITH_DEBUG_RAIL "Print RemoteApp debug messages" ${DEFAULT_DEBUG_OPTION})
@@ -163,13 +163,13 @@ option(WITH_GSSAPI "Compile support for kerberos authentication. (EXPERIMENTAL)"
 
 option(WITH_DSP_EXPERIMENTAL "Enable experimental sound encoder/decoder formats" OFF)
 if (WITH_FFMPEG)
-    option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF)
-    option(WITH_VAAPI "Use FFMPEG VAAPI (EXPERIMENTAL)" OFF)
+	option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF)
+	option(WITH_VAAPI "Use FFMPEG VAAPI (EXPERIMENTAL)" OFF)
 endif(WITH_FFMPEG)
 
 option(USE_VERSION_FROM_GIT_TAG "Extract FreeRDP version from git tag." OFF)
 
-option(WITH_CAIRO    "Use CAIRO image library for screen resizing" OFF)
+option(WITH_CAIRO	"Use CAIRO image library for screen resizing" OFF)
 option(WITH_SWSCALE  "Use SWScale image library for screen resizing" OFF)
 
 option(DEFINE_NO_DEPRECATED "Compile without legacy functions and symbols" OFF)
diff --git a/cmake/FindOpenSSL.cmake b/cmake/FindOpenSSL.cmake
index d42a50c..f630d1a 100644
--- a/cmake/FindOpenSSL.cmake
+++ b/cmake/FindOpenSSL.cmake
@@ -55,7 +55,7 @@ FIND_PATH(OPENSSL_INCLUDE_DIR
   NAMES
     openssl/ssl.h
   PATH_SUFFIXES
-	"include"
+    "include"
   HINTS
     ${_OPENSSL_INCLUDEDIR}
   ${_OPENSSL_ROOT_HINTS_AND_PATHS}
@@ -172,8 +172,8 @@ ELSEIF(WIN32 AND NOT CYGWIN)
     )
 
     set( OPENSSL_DEBUG_LIBRARIES ${SSL_EAY_DEBUG} ${LIB_EAY_DEBUG} )
-	set( OPENSSL_RELEASE_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} )
-	set( OPENSSL_LIBRARIES ${OPENSSL_RELEASE_LIBRARIES} )
+    set( OPENSSL_RELEASE_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} )
+    set( OPENSSL_LIBRARIES ${OPENSSL_RELEASE_LIBRARIES} )
 
     MARK_AS_ADVANCED(SSL_EAY_DEBUG SSL_EAY_RELEASE)
     MARK_AS_ADVANCED(LIB_EAY_DEBUG LIB_EAY_RELEASE)
@@ -313,25 +313,50 @@ if (OPENSSL_INCLUDE_DIR)
 
     string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$"
            "\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}")
-    list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
-    list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
-    from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR)
-    list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX)
-    from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX)
-    list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH)
-
-    if (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
-      from_hex("${OPENSSL_VERSION_PATCH}" _tmp)
-      # 96 is the ASCII code of 'a' minus 1
-      math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96")
-      unset(_tmp)
-      # Once anyone knows how OpenSSL would call the patch versions beyond 'z'
-      # this should be updated to handle that, too. This has not happened yet
-      # so it is simply ignored here for now.
-      string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING)
-    endif (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
-
-    set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}")
+     if (OPENSSL_VERSION_LIST)
+        list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
+        list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
+        from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR)
+        list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX)
+        from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX)
+        list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH)
+
+        if (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
+          from_hex("${OPENSSL_VERSION_PATCH}" _tmp)
+          # 96 is the ASCII code of 'a' minus 1
+          math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96")
+          unset(_tmp)
+          # Once anyone knows how OpenSSL would call the patch versions beyond 'z'
+          # this should be updated to handle that, too. This has not happened yet
+          # so it is simply ignored here for now.
+          string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING)
+        endif (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
+
+        set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}")
+    endif()
+
+    if (NOT OPENSSL_VERSION_LIST)
+      file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str_str
+        REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_STR[\t ]\"([0-9a-fA-F]+)\\.([0-9a-fA-F]+)\\.([0-9a-fA-F]+).*\".*$")
+      string(REGEX REPLACE "^.*OPENSSL_VERSION_STR[\t ]+\"([0-9a-fA-F]+)\\.([0-9a-fA-F]+)\\.([0-9a-fA-F]+).*\".*$"
+        "\\1.\\2.\\3" OPENSSL_VERSION "${openssl_version_str_str}")
+    endif()
+
+    if (NOT OPENSSL_VERSION_LIST)
+      file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_major_str
+        REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_MAJOR[\t ]+([0-9a-fA-F]+).*$")
+      string(REGEX REPLACE "^#[\t ]*define[\t ]+OPENSSL_VERSION_MAJOR[\t ]+([0-9a-fA-F]+).*$"
+          "\\1" OPENSSL_VERSION_MAJOR "${openssl_version_major_str}")
+      file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_minor_str
+        REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_MINOR[\t ]+([0-9a-fA-F]+).*$")
+      string(REGEX REPLACE "^#[\t ]*define[\t ]+OPENSSL_VERSION_MINOR[\t ]+([0-9a-fA-F]+).*$"
+        "\\1" OPENSSL_VERSION_MINOR "${openssl_version_minor_str}")
+      file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_patch_str
+        REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_PATCH[\t ]+([0-9a-fA-F]+).*$")
+      string(REGEX REPLACE "^#[\t ]*define[\t ]+OPENSSL_VERSION_PATCH[\t ]+([0-9a-fA-F]+).*$"
+          "\\1" OPENSSL_VERSION_PATCH "${openssl_version_patch_str}")
+      set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_PATCH}")
+    endif()
   endif (_OPENSSL_VERSION)
 endif (OPENSSL_INCLUDE_DIR)
 
diff --git a/debian/.gitignore b/debian/.gitignore
deleted file mode 100644
index 1f2b5a3..0000000
--- a/debian/.gitignore
+++ /dev/null
@@ -1,33 +0,0 @@
-*.log
-freerdp2-x11/
-freerdp2-x11-dbg/
-libfreerdp-client2/
-libfreerdp-server2/
-libfreerdp2-dev/
-libfreerdp2/
-libwinpr2-dev/
-libwinpr2/
-tmp/
-*.substvars
-.debhelper/
-files
-freerdp2-shadow-x11-dbg/
-freerdp2-shadow-x11/
-freerdp2-dev/
-libfreerdp-client2-dbg/
-libfreerdp-server2-dbg/
-libfreerdp2-dbg/
-libwinpr2-dbg/
-winpr-utils-dbg/
-winpr-utils/
-freerdp2-wayland-dbg/
-freerdp2-wayland/
-libfreerdp-shadow2-dbg/
-libfreerdp-shadow2/
-libuwac0-dbg/
-libuwac0-dev/
-libuwac0/
-debhelper-build-stamp
-libxfreerdp-client2
-libxfreerdp-client2-dbg
-libwinpr-tools2
diff --git a/debian/changelog b/debian/changelog
index 02eb10a..477876e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,92 @@
+freerdp2 (2.11.7+dfsg1-1) unstable; urgency=medium
+
+  [ Mike Gabriel ]
+  * New upstream release. (Closes: #1069728).
+    + CVE-2024-32041 [Low[ OutOfBound Read in zgfx_decompress_segment.
+    + CVE-2024-32039 [Moderate] Integer overflow & OutOfBound Write in
+      clear_decompress_residual_data.
+    + CVE-2024-32040 [Low] integer underflow in nsc_rle_decode.
+    + CVE-2024-32458 [Low] OutOfBound Read in planar_skip_plane_rle.
+    + CVE-2024-32459 [Low] OutOfBound Read in ncrush_decompress.
+    + CVE-2024-32460 [Low] OutOfBound Read in interleaved_decompress.
+
+  [ Nathan Pratta Teodosio ]
+  * Add autopkgtest to test whether a client can connect
+    to an XRDP server via freerdp2 and that the login screen shows up
+    (Closes: #1073156) (LP: #2060976)
+
+ -- Mike Gabriel <sunweaver@debian.org>  Mon, 15 Jul 2024 16:46:25 +0200
+
+freerdp2 (2.11.5+dfsg1-1) unstable; urgency=medium
+
+  * New upstream release.
+    - CVE-2024-22211: Fix integer overflow in progressive decoder. (Closes:
+      #1061173).
+  * Upload time_t64 changes to unstable. (Closes: #1061952).
+  * debian/watch:
+    + Adjust so we only see 2.x release.
+  * debian/control:
+    + Switch from pkg-config to pkgconf. Thanks, lintian.
+
+ -- Mike Gabriel <sunweaver@debian.org>  Mon, 25 Mar 2024 16:09:04 +0100
+
+freerdp2 (2.11.2+dfsg1-1.1~exp2) experimental; urgency=medium
+
+  * Non-maintainer upload.
+  * Rename libraries for 64-bit time_t transition.
+  * Account for additional t64 Breaks/Replaces (Closes #1061982).
+
+ -- Lukas Märdian <slyon@debian.org>  Tue, 30 Jan 2024 13:19:02 +0000
+
+freerdp2 (2.11.2+dfsg1-1) unstable; urgency=medium
+
+  * New upstream release. (Closes: #1051638).
+  * Fixed security issues since v2.11.0:
+    - CVE-2023-40589: [codec,ncrush] fix index checks properly verify all
+      offsets while decoding data.
+    - CVE-2023-40567: Fix out-of-bounds write in the
+      `clear_decompress_bands_data` function.
+    - CVE-2023-40188: Fix out-of-bounds read in the `general_LumaToYUV444`
+      function.
+    - CVE-2023-40186: Fix out-of-bounds write in the `gdi_CreateSurface`
+      function.
+    - CVE-2023-40181: Fix out-of-bounds read in the `zgfx_decompress_segment`
+      function.
+    - CVE-2023-39356: Fix out-of-bounds read in the `gdi_multi_opaque_rect`
+      function.
+    - CVE-2023-39355: Fix use-after-free in processing
+      `RDPGFX_CMDID_RESETGRAPHICS` packets.
+    - CVE-2023-39354: Fix out-of-bounds read in the `nsc_rle_decompress_data`
+      function.
+    - CVE-2023-39353: Fix missing offset validation leading to out-of-bounds
+      read in the `libfreerdp/codec/rfx.c` file.
+    - CVE-2023-39352: Fix invalid offset validation leading to out-of-bounds
+      write.
+    - CVE-2023-39351: Fix null-pointer-dereference leading a crash in the
+      RemoteFX (rfx) handling.
+    - CVE-2023-39350: Fix integer underflow leading to DOS (e.g. abort due to
+      `WINPR_ASSERT` with default compilation flags).
+  * debian/patches:
+    + Drop 0001_fix_ftbfs_1041377.patch. Applied upstream.
+  * debian/control:
+    + Add B-D: libkrb5-dev.
+  * debian/rules:
+    + Add -DWITH_KERBEROS=ON configure option. (Closes: #1036095).
+  * debian/watch:
+    + Rework file. Find all released versions of freerdp2. (Closes: #1053317).
+      Thanks to Tobias Frost for sending a patch.
+
+ -- Mike Gabriel <sunweaver@debian.org>  Sun, 01 Oct 2023 23:21:15 +0200
+
+freerdp2 (2.10.0+dfsg1-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * debian/patches/0001_fix_ftbfs_1041377.patch:
+    - include upstream fix for FTBFS with FFmpeg 6.0
+      (Closes: #1041377)
+
+ -- Héctor Orón Martínez <zumbi@debian.org>  Fri, 04 Aug 2023 04:08:40 -0400
+
 freerdp2 (2.10.0+dfsg1-1) unstable; urgency=medium
 
   * New upstream release.
diff --git a/debian/control b/debian/control
index d81ab88..9be5844 100644
--- a/debian/control
+++ b/debian/control
@@ -17,6 +17,7 @@ Build-Depends:
  libgsm1-dev,
  libicu-dev,
  libjpeg-dev,
+ libkrb5-dev,
  libpam0g-dev,
  libpcsclite-dev,
  libpulse-dev,
@@ -39,7 +40,7 @@ Build-Depends:
  libxrender-dev,
  libxtst-dev,
  libxv-dev,
- pkg-config,
+ pkgconf,
  uuid-dev,
  xmlto,
  xsltproc,
@@ -54,7 +55,7 @@ Architecture: any
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp-client2-2 (= ${binary:Version}),
+ libfreerdp-client2-2t64 (= ${binary:Version}),
 Provides:
  freerdp,
 Replaces:
@@ -80,7 +81,8 @@ Description: RDP client for Windows Terminal Services (X11 client)
  .
  This package contains the X11 based client.
 
-Package: libfreerdp2-2
+Package: libfreerdp2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -88,10 +90,12 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libwinpr2-2 (= ${binary:Version}),
+ libwinpr2-2t64 (= ${binary:Version}),
 Breaks:
+ libfreerdp2-2 (<< ${source:Version}),
  libfreerdp2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libfreerdp2-2,
  libfreerdp2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Suggests:
@@ -102,7 +106,8 @@ Description: Free Remote Desktop Protocol library (core library)
  .
  This package contains the shared library with all core functionality.
 
-Package: libfreerdp-client2-2
+Package: libfreerdp-client2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -110,10 +115,12 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp2-2 (= ${binary:Version}),
+ libfreerdp2-2t64 (= ${binary:Version}),
 Breaks:
+ libfreerdp-client2-2 (<< ${source:Version}),
  libfreerdp-client2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libfreerdp-client2-2,
  libfreerdp-client2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Description: Free Remote Desktop Protocol library (client library)
@@ -122,7 +129,8 @@ Description: Free Remote Desktop Protocol library (client library)
  .
  This package contains the shared library for common client functionality.
 
-Package: libfreerdp-server2-2
+Package: libfreerdp-server2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -130,10 +138,12 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp2-2 (= ${binary:Version}),
+ libfreerdp2-2t64 (= ${binary:Version}),
 Breaks:
+ libfreerdp-server2-2 (<< ${source:Version}),
  libfreerdp-server2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libfreerdp-server2-2,
  libfreerdp-server2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Description: Free Remote Desktop Protocol library (server library)
@@ -142,7 +152,8 @@ Description: Free Remote Desktop Protocol library (server library)
  .
  This package contains the shared library with common server functionality.
 
-Package: libwinpr2-2
+Package: libwinpr2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -151,8 +162,10 @@ Depends:
  ${misc:Depends},
  ${shlibs:Depends},
 Breaks:
+ libwinpr2-2 (<< ${source:Version}),
  libwinpr2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libwinpr2-2,
  libwinpr2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Suggests:
@@ -169,7 +182,8 @@ Description: Windows Portable Runtime library
  .
  This package contains the WinPR shared library.
 
-Package: libwinpr-tools2-2
+Package: libwinpr-tools2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -177,10 +191,12 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libwinpr2-2 (= ${binary:Version}),
+ libwinpr2-2t64 (= ${binary:Version}),
 Breaks:
+ libwinpr-tools2-2 (<< ${source:Version}),
  libwinpr-tools2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libwinpr-tools2-2,
  libwinpr-tools2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Description: Windows Portable Runtime Tools library
@@ -196,8 +212,8 @@ Architecture: any
 Multi-Arch: same
 Depends:
  libssl-dev,
- libwinpr-tools2-2 (= ${binary:Version}),
- libwinpr2-2 (= ${binary:Version}),
+ libwinpr-tools2-2t64 (= ${binary:Version}),
+ libwinpr2-2t64 (= ${binary:Version}),
  ${misc:Depends},
 Description: Windows Portable Runtime library (development files)
  WinPR is a spin-off project of FreeRDP which aims at providing a portable
@@ -215,11 +231,11 @@ Package: freerdp2-dev
 Section: devel
 Architecture: any
 Depends:
- libfreerdp-client2-2 (= ${binary:Version}),
- libfreerdp-server2-2 (= ${binary:Version}),
- libfreerdp-shadow-subsystem2-2 (= ${binary:Version}),
- libfreerdp-shadow2-2 (= ${binary:Version}),
- libfreerdp2-2 (= ${binary:Version}),
+ libfreerdp-client2-2t64 (= ${binary:Version}),
+ libfreerdp-server2-2t64 (= ${binary:Version}),
+ libfreerdp-shadow-subsystem2-2t64 (= ${binary:Version}),
+ libfreerdp-shadow2-2t64 (= ${binary:Version}),
+ libfreerdp2-2t64 (= ${binary:Version}),
  libwinpr2-dev (= ${binary:Version}),
  winpr-utils (= ${binary:Version}),
  ${misc:Depends},
@@ -236,7 +252,7 @@ Architecture: any
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libwinpr-tools2-2 (= ${binary:Version}),
+ libwinpr-tools2-2t64 (= ${binary:Version}),
 Description: Windows Portable Runtime library command line utilities
  WinPR is a spin-off project of FreeRDP which aims at providing a portable
  implementation of important portions of the Windows API. Just like FreeRDP,
@@ -249,7 +265,8 @@ Description: Windows Portable Runtime library command line utilities
  .
  This package contains WinPR command line utils (winpr-hash, winpr-makecert).
 
-Package: libfreerdp-shadow2-2
+Package: libfreerdp-shadow2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -257,11 +274,13 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp-server2-2 (= ${binary:Version}),
- libwinpr-tools2-2 (= ${binary:Version}),
+ libfreerdp-server2-2t64 (= ${binary:Version}),
+ libwinpr-tools2-2t64 (= ${binary:Version}),
 Breaks:
+ libfreerdp-shadow2-2 (<< ${source:Version}),
  libfreerdp-shadow2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libfreerdp-shadow2-2,
  libfreerdp-shadow2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Description: FreeRDP Remote Desktop Protocol shadow libraries
@@ -270,7 +289,8 @@ Description: FreeRDP Remote Desktop Protocol shadow libraries
  .
  This package contains the shadow libraries.
 
-Package: libfreerdp-shadow-subsystem2-2
+Package: libfreerdp-shadow-subsystem2-2t64
+Provides: ${t64:Provides}
 Architecture: any
 Section: libs
 Pre-Depends:
@@ -278,10 +298,12 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp-shadow2-2 (= ${binary:Version}),
+ libfreerdp-shadow2-2t64 (= ${binary:Version}),
 Breaks:
+ libfreerdp-shadow-subsystem2-2 (<< ${source:Version}),
  libfreerdp-shadow2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libfreerdp-shadow-subsystem2-2,
  libfreerdp-shadow2 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Description: FreeRDP Remote Desktop Protocol shadow subsystem libraries
@@ -295,7 +317,7 @@ Architecture: any
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp-shadow-subsystem2-2 (= ${binary:Version}),
+ libfreerdp-shadow-subsystem2-2t64 (= ${binary:Version}),
 Provides:
  freerdp,
 Description: FreeRDP x11 shadowing server
@@ -305,7 +327,8 @@ Description: FreeRDP x11 shadowing server
  This package contains a "shadowing" server that can be used to
  share an already started X11 DISPLAY.
 
-Package: libuwac0-0
+Package: libuwac0-0t64
+Provides: ${t64:Provides}
 Architecture: linux-any
 Section: libs
 Pre-Depends:
@@ -313,10 +336,12 @@ Pre-Depends:
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp2-2 (= ${binary:Version}),
+ libfreerdp2-2t64 (= ${binary:Version}),
 Breaks:
+ libuwac0-0 (<< ${source:Version}),
  libuwac0 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Replaces:
+ libuwac0-0,
  libuwac0 (<< 2.0.0~git20170725.1.1648deb+dfsg1-1~),
 Multi-Arch: same
 Description: Using wayland as a client library
@@ -330,7 +355,7 @@ Section: libdevel
 Architecture: linux-any
 Multi-Arch: same
 Depends:
- libuwac0-0 (= ${binary:Version}),
+ libuwac0-0t64 (= ${binary:Version}),
  ${misc:Depends},
 Description: Using wayland as a client (development files)
  Using wayland as a client (uwac) is a library to provide common
@@ -343,8 +368,8 @@ Architecture: linux-any
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
- libfreerdp-client2-2 (= ${binary:Version}),
- libuwac0-0 (= ${binary:Version}),
+ libfreerdp-client2-2t64 (= ${binary:Version}),
+ libuwac0-0t64 (= ${binary:Version}),
 Description: RDP client for Windows Terminal Services (wayland client)
  FreeRDP is a libre client/server implementation of the Remote
  Desktop Protocol (RDP).
diff --git a/debian/libfreerdp-client2-2.install b/debian/libfreerdp-client2-2t64.install
similarity index 100%
rename from debian/libfreerdp-client2-2.install
rename to debian/libfreerdp-client2-2t64.install
diff --git a/debian/libfreerdp-client2-2t64.lintian-overrides b/debian/libfreerdp-client2-2t64.lintian-overrides
new file mode 100644
index 0000000..efa1ab8
--- /dev/null
+++ b/debian/libfreerdp-client2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libfreerdp-client2-2t64: package-name-doesnt-match-sonames libfreerdp-client2-2
diff --git a/debian/libfreerdp-client2-2.symbols b/debian/libfreerdp-client2-2t64.symbols
similarity index 98%
rename from debian/libfreerdp-client2-2.symbols
rename to debian/libfreerdp-client2-2t64.symbols
index 7598bb1..4d36c18 100644
--- a/debian/libfreerdp-client2-2.symbols
+++ b/debian/libfreerdp-client2-2t64.symbols
@@ -1,4 +1,4 @@
-libfreerdp-client2.so.2 libfreerdp-client2-2 #MINVER#
+libfreerdp-client2.so.2 libfreerdp-client2-2t64 #MINVER#
 * Build-Depends-Package: freerdp2-dev
  add_device@Base 2.1.0+dfsg1
  client_auto_reconnect@Base 2.0.0~git20181120.1.e21b72c95+dfsg1
diff --git a/debian/libfreerdp-server2-2.install b/debian/libfreerdp-server2-2t64.install
similarity index 100%
rename from debian/libfreerdp-server2-2.install
rename to debian/libfreerdp-server2-2t64.install
diff --git a/debian/libfreerdp-server2-2t64.lintian-overrides b/debian/libfreerdp-server2-2t64.lintian-overrides
new file mode 100644
index 0000000..f359fa3
--- /dev/null
+++ b/debian/libfreerdp-server2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libfreerdp-server2-2t64: package-name-doesnt-match-sonames libfreerdp-server2-2
diff --git a/debian/libfreerdp-server2-2.symbols b/debian/libfreerdp-server2-2t64.symbols
similarity index 98%
rename from debian/libfreerdp-server2-2.symbols
rename to debian/libfreerdp-server2-2t64.symbols
index 79c8e01..a1ed5e4 100644
--- a/debian/libfreerdp-server2-2.symbols
+++ b/debian/libfreerdp-server2-2t64.symbols
@@ -1,4 +1,4 @@
-libfreerdp-server2.so.2 libfreerdp-server2-2 #MINVER#
+libfreerdp-server2.so.2 libfreerdp-server2-2t64 #MINVER#
 * Build-Depends-Package: freerdp2-dev
  ainput_server_context_free@Base 2.6.0+dfsg1
  ainput_server_context_new@Base 2.6.0+dfsg1
diff --git a/debian/libfreerdp-shadow-subsystem2-2.install b/debian/libfreerdp-shadow-subsystem2-2t64.install
similarity index 100%
rename from debian/libfreerdp-shadow-subsystem2-2.install
rename to debian/libfreerdp-shadow-subsystem2-2t64.install
diff --git a/debian/libfreerdp-shadow-subsystem2-2t64.lintian-overrides b/debian/libfreerdp-shadow-subsystem2-2t64.lintian-overrides
new file mode 100644
index 0000000..fcd5e87
--- /dev/null
+++ b/debian/libfreerdp-shadow-subsystem2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libfreerdp-shadow-subsystem2-2t64: package-name-doesnt-match-sonames libfreerdp-shadow-subsystem2-2
diff --git a/debian/libfreerdp-shadow-subsystem2-2.symbols b/debian/libfreerdp-shadow-subsystem2-2t64.symbols
similarity index 94%
rename from debian/libfreerdp-shadow-subsystem2-2.symbols
rename to debian/libfreerdp-shadow-subsystem2-2t64.symbols
index 1388462..48fe6b4 100644
--- a/debian/libfreerdp-shadow-subsystem2-2.symbols
+++ b/debian/libfreerdp-shadow-subsystem2-2t64.symbols
@@ -1,4 +1,4 @@
-libfreerdp-shadow-subsystem2.so.2 libfreerdp-shadow-subsystem2-2 #MINVER#
+libfreerdp-shadow-subsystem2.so.2 libfreerdp-shadow-subsystem2-2t64 #MINVER#
 * Build-Depends-Package: freerdp2-dev
  X11_ShadowSubsystemEntry@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
  shadow_subsystem_set_entry_builtin@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
diff --git a/debian/libfreerdp-shadow2-2.install b/debian/libfreerdp-shadow2-2t64.install
similarity index 100%
rename from debian/libfreerdp-shadow2-2.install
rename to debian/libfreerdp-shadow2-2t64.install
diff --git a/debian/libfreerdp-shadow2-2t64.lintian-overrides b/debian/libfreerdp-shadow2-2t64.lintian-overrides
new file mode 100644
index 0000000..db9a789
--- /dev/null
+++ b/debian/libfreerdp-shadow2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libfreerdp-shadow2-2t64: package-name-doesnt-match-sonames libfreerdp-shadow2-2
diff --git a/debian/libfreerdp-shadow2-2.symbols b/debian/libfreerdp-shadow2-2t64.symbols
similarity index 96%
rename from debian/libfreerdp-shadow2-2.symbols
rename to debian/libfreerdp-shadow2-2t64.symbols
index 3936ad8..03eb71c 100644
--- a/debian/libfreerdp-shadow2-2.symbols
+++ b/debian/libfreerdp-shadow2-2t64.symbols
@@ -1,4 +1,4 @@
-libfreerdp-shadow2.so.2 libfreerdp-shadow2-2 #MINVER#
+libfreerdp-shadow2.so.2 libfreerdp-shadow2-2t64 #MINVER#
 * Build-Depends-Package: freerdp2-dev
  shadow_capture_align_clip_rect@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
  shadow_capture_compare@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
diff --git a/debian/libfreerdp2-2.install b/debian/libfreerdp2-2t64.install
similarity index 100%
rename from debian/libfreerdp2-2.install
rename to debian/libfreerdp2-2t64.install
diff --git a/debian/libfreerdp2-2t64.lintian-overrides b/debian/libfreerdp2-2t64.lintian-overrides
new file mode 100644
index 0000000..e25cddf
--- /dev/null
+++ b/debian/libfreerdp2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libfreerdp2-2t64: package-name-doesnt-match-sonames libfreerdp2-2
diff --git a/debian/libfreerdp2-2.symbols b/debian/libfreerdp2-2t64.symbols
similarity index 99%
rename from debian/libfreerdp2-2.symbols
rename to debian/libfreerdp2-2t64.symbols
index f6eea21..e8cffdb 100644
--- a/debian/libfreerdp2-2.symbols
+++ b/debian/libfreerdp2-2t64.symbols
@@ -1,4 +1,4 @@
-libfreerdp2.so.2 libfreerdp2-2 #MINVER#
+libfreerdp2.so.2 libfreerdp2-2t64 #MINVER#
 * Build-Depends-Package: freerdp2-dev
  Bitmap_Alloc@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
  Bitmap_SetDimensions@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
diff --git a/debian/libuwac0-0.install b/debian/libuwac0-0t64.install
similarity index 100%
rename from debian/libuwac0-0.install
rename to debian/libuwac0-0t64.install
diff --git a/debian/libuwac0-0t64.lintian-overrides b/debian/libuwac0-0t64.lintian-overrides
new file mode 100644
index 0000000..cc395e4
--- /dev/null
+++ b/debian/libuwac0-0t64.lintian-overrides
@@ -0,0 +1 @@
+libuwac0-0t64: package-name-doesnt-match-sonames libuwac0-0
diff --git a/debian/libuwac0-0.symbols b/debian/libuwac0-0t64.symbols
similarity index 99%
rename from debian/libuwac0-0.symbols
rename to debian/libuwac0-0t64.symbols
index 1c4df89..83aa153 100644
--- a/debian/libuwac0-0.symbols
+++ b/debian/libuwac0-0t64.symbols
@@ -1,4 +1,4 @@
-libuwac0.so.0 libuwac0-0 #MINVER#
+libuwac0.so.0 libuwac0-0t64 #MINVER#
 * Build-Depends-Package: libuwac0-dev
  UwacClipboardDataGet@Base 2.0.0~git20190204.1.2693389a+dfsg1
  UwacClipboardOfferAnnounce@Base 2.0.0~git20190204.1.2693389a+dfsg1
diff --git a/debian/libwinpr-tools2-2.install b/debian/libwinpr-tools2-2t64.install
similarity index 100%
rename from debian/libwinpr-tools2-2.install
rename to debian/libwinpr-tools2-2t64.install
diff --git a/debian/libwinpr-tools2-2t64.lintian-overrides b/debian/libwinpr-tools2-2t64.lintian-overrides
new file mode 100644
index 0000000..82336d8
--- /dev/null
+++ b/debian/libwinpr-tools2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libwinpr-tools2-2t64: package-name-doesnt-match-sonames libwinpr-tools2-2
diff --git a/debian/libwinpr-tools2-2.symbols b/debian/libwinpr-tools2-2t64.symbols
similarity index 90%
rename from debian/libwinpr-tools2-2.symbols
rename to debian/libwinpr-tools2-2t64.symbols
index 034e428..bf7702b 100644
--- a/debian/libwinpr-tools2-2.symbols
+++ b/debian/libwinpr-tools2-2t64.symbols
@@ -1,4 +1,4 @@
-libwinpr-tools2.so.2 libwinpr-tools2-2 #MINVER#
+libwinpr-tools2.so.2 libwinpr-tools2-2t64 #MINVER#
 * Build-Depends-Package: libwinpr2-dev
  makecert_context_free@Base 2.0.0~git20160503.1.f828595+dfsg1
  makecert_context_new@Base 2.0.0~git20160503.1.f828595+dfsg1
diff --git a/debian/libwinpr2-2.docs b/debian/libwinpr2-2t64.docs
similarity index 100%
rename from debian/libwinpr2-2.docs
rename to debian/libwinpr2-2t64.docs
diff --git a/debian/libwinpr2-2.symbols.hurd-i386 b/debian/libwinpr2-2t64.hurd-i386
similarity index 100%
rename from debian/libwinpr2-2.symbols.hurd-i386
rename to debian/libwinpr2-2t64.hurd-i386
diff --git a/debian/libwinpr2-2.install b/debian/libwinpr2-2t64.install
similarity index 100%
rename from debian/libwinpr2-2.install
rename to debian/libwinpr2-2t64.install
diff --git a/debian/libwinpr2-2.symbols.kfreebsd-amd64 b/debian/libwinpr2-2t64.kfreebsd-amd64
similarity index 100%
rename from debian/libwinpr2-2.symbols.kfreebsd-amd64
rename to debian/libwinpr2-2t64.kfreebsd-amd64
diff --git a/debian/libwinpr2-2.symbols.kfreebsd-i386 b/debian/libwinpr2-2t64.kfreebsd-i386
similarity index 100%
rename from debian/libwinpr2-2.symbols.kfreebsd-i386
rename to debian/libwinpr2-2t64.kfreebsd-i386
diff --git a/debian/libwinpr2-2t64.lintian-overrides b/debian/libwinpr2-2t64.lintian-overrides
new file mode 100644
index 0000000..1bde098
--- /dev/null
+++ b/debian/libwinpr2-2t64.lintian-overrides
@@ -0,0 +1 @@
+libwinpr2-2t64: package-name-doesnt-match-sonames libwinpr2-2
diff --git a/debian/libwinpr2-2.symbols b/debian/libwinpr2-2t64.symbols
similarity index 99%
rename from debian/libwinpr2-2.symbols
rename to debian/libwinpr2-2t64.symbols
index 0be74f0..9089491 100644
--- a/debian/libwinpr2-2.symbols
+++ b/debian/libwinpr2-2t64.symbols
@@ -1,4 +1,4 @@
-libwinpr2.so.2 libwinpr2-2 #MINVER#
+libwinpr2.so.2 libwinpr2-2t64 #MINVER#
 * Build-Depends-Package: libwinpr2-dev
  ASN1DecSetError@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
  ASN1EncSetError@Base 2.0.0~git20160317.1.75ae3f5+dfsg1
diff --git a/debian/rules b/debian/rules
index c6950e9..19be556 100755
--- a/debian/rules
+++ b/debian/rules
@@ -31,6 +31,7 @@ DEB_CMAKE_EXTRA_FLAGS = \
     -DWITH_CUPS=ON \
     -DWITH_PCSC=ON \
     -DWITH_JPEG=ON \
+    -DWITH_KERBEROS=ON \
     $(empty)
 
 ifneq (,$(filter armel,$(DEB_HOST_ARCH)))
diff --git a/debian/tests/compare b/debian/tests/compare
new file mode 100644
index 0000000..9db5cd6
--- /dev/null
+++ b/debian/tests/compare
@@ -0,0 +1,33 @@
+#!/bin/sh
+set -x
+
+screenshot=xrdp-screenshot.png
+magickout=match-matrix
+testdir=$(dirname "$0")
+
+sudo systemctl start xrdp
+
+# Start XRDP and try to connect to it via Freerdp2, the package being tested.
+# Take a screenshot of the X virtual framebuffer, which should contain the
+# login screen.
+xvfb-run -l "$testdir/connect-and-capture" "$screenshot"
+
+# Confirm that the XRDP logo is in the log in screen (so as to confirm that the
+# login screen is really there).
+# https://stackoverflow.com/questions/31867093/how-to-search-an-image-for-subimages-using-linux-console
+# compare creates two output files, $magickout-{0,1}; The latter
+# is a grayscale pattern, in which a white pixel represents a match.
+# If the images don't even resemble each other in any way, the compare
+# will fail but we don't need to check that specifically, because so
+# will the if below.
+compare -metric RMSE -subimage-search \
+  "$screenshot" /usr/share/xrdp/xrdp_logo.bmp png:"$magickout"
+# In case of match, output will be like
+# "520,347: (255,255,255)  #FFFFFF  gray(255)"
+# in which the first field is the coordinates of the match.
+if ! convert "$magickout-1" -threshold 95% txt: | grep -qi '#FFFFFF'; then
+    2>&1 printf "%s\n" "Imagemagick did not find any occurence of the XRDP" \
+                       "logo inside the screenshot of the remote desktop." \
+                       "This test therefore failed."
+    exit 2
+fi
diff --git a/debian/tests/connect-and-capture b/debian/tests/connect-and-capture
new file mode 100644
index 0000000..9700676
--- /dev/null
+++ b/debian/tests/connect-and-capture
@@ -0,0 +1,4 @@
+#!/bin/sh
+xfreerdp /f /v:localhost:3389 /p: /u: /d: &
+sleep 12
+import -window root "$1"
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..ef320ea
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,3 @@
+Tests: compare
+Depends: @, xrdp, xvfb, imagemagick
+Restrictions: allow-stderr, needs-sudo
diff --git a/debian/watch b/debian/watch
index 50d10a6..9ac5131 100644
--- a/debian/watch
+++ b/debian/watch
@@ -3,5 +3,5 @@ opts=\
 filenamemangle=s/.*\/v?([\d\.-]+)\.tar\.gz/freerdp-$1.tar.gz/,\
 dversionmangle=s/\+dfsg1//,\
 repacksuffix=+dfsg1 \
-https://github.com/FreeRDP/FreeRDP/tags .*/archive/refs/tags/v?([\d\.]+).tar.gz \
+https://github.com/FreeRDP/FreeRDP/tags .*/archive/refs/tags/v?(2\.[\d\.]+).tar.gz \
 debian
diff --git a/include/freerdp/channels/rdpdr.h b/include/freerdp/channels/rdpdr.h
index b61a4c7..6cd283b 100644
--- a/include/freerdp/channels/rdpdr.h
+++ b/include/freerdp/channels/rdpdr.h
@@ -41,6 +41,14 @@
 #define RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH 32
 #define RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH 4
 
+#define RDPDR_VERSION_MAJOR 0x0001
+
+#define RDPDR_VERSION_MINOR_RDP50 0x0002
+#define RDPDR_VERSION_MINOR_RDP51 0x0005
+#define RDPDR_VERSION_MINOR_RDP52 0x000A
+#define RDPDR_VERSION_MINOR_RDP6X 0x000C
+#define RDPDR_VERSION_MINOR_RDP10X 0x000D
+
 /* RDPDR_HEADER.Component */
 enum RDPDR_CTYP
 {
diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h
index 4268efd..76f1951 100644
--- a/include/freerdp/settings.h
+++ b/include/freerdp/settings.h
@@ -730,6 +730,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL;
 #define FreeRDP_PercentScreenUseWidth (1556)
 #define FreeRDP_PercentScreenUseHeight (1557)
 #define FreeRDP_DynamicResolutionUpdate (1558)
+#define FreeRDP_GrabMouse (1559)
 #define FreeRDP_SoftwareGdi (1601)
 #define FreeRDP_LocalConnection (1602)
 #define FreeRDP_AuthenticationOnly (1603)
@@ -1216,7 +1217,8 @@ struct rdp_settings
 	ALIGN64 BOOL PercentScreenUseWidth;   /* 1556 */
 	ALIGN64 BOOL PercentScreenUseHeight;  /* 1557 */
 	ALIGN64 BOOL DynamicResolutionUpdate; /* 1558 */
-	UINT64 padding1601[1601 - 1559];      /* 1559 */
+	ALIGN64 BOOL GrabMouse;               /* 1559 */
+	UINT64 padding1601[1601 - 1560];      /* 1560 */
 
 	/* Miscellaneous */
 	ALIGN64 BOOL SoftwareGdi;          /* 1601 */
diff --git a/libfreerdp/cache/pointer.c b/libfreerdp/cache/pointer.c
index 010df17..42acf65 100644
--- a/libfreerdp/cache/pointer.c
+++ b/libfreerdp/cache/pointer.c
@@ -68,6 +68,9 @@ static BOOL update_pointer_position(rdpContext* context,
 	    !pointer_position)
 		return FALSE;
 
+	if (!context->settings->GrabMouse)
+		return TRUE;
+
 	pointer = context->graphics->Pointer_Prototype;
 	return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos,
 	                    pointer_position->yPos);
diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c
index fadd98e..e38fa0d 100644
--- a/libfreerdp/codec/clear.c
+++ b/libfreerdp/codec/clear.c
@@ -410,7 +410,7 @@ static BOOL clear_decompress_residual_data(CLEAR_CONTEXT* clear, wStream* s,
 			}
 		}
 
-		if ((pixelIndex + runLengthFactor) > pixelCount)
+		if ((pixelIndex >= pixelCount) || (runLengthFactor > (pixelCount - pixelIndex)))
 		{
 			WLog_ERR(TAG,
 			         "pixelIndex %" PRIu32 " + runLengthFactor %" PRIu32 " > pixelCount %" PRIu32
@@ -512,12 +512,12 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* clear, wStream* s,
 		{
 			case 0: /* Uncompressed */
 			{
-				UINT32 nSrcStep = width * GetBytesPerPixel(PIXEL_FORMAT_BGR24);
-				UINT32 nSrcSize = nSrcStep * height;
+				const UINT32 nSrcStep = width * GetBytesPerPixel(PIXEL_FORMAT_BGR24);
+				const size_t nSrcSize = 1ull * nSrcStep * height;
 
 				if (bitmapDataByteCount != nSrcSize)
 				{
-					WLog_ERR(TAG, "bitmapDataByteCount %" PRIu32 " != nSrcSize %" PRIu32 "",
+					WLog_ERR(TAG, "bitmapDataByteCount %" PRIu32 " != nSrcSize %" PRIuz "",
 					         bitmapDataByteCount, nSrcSize);
 					return FALSE;
 				}
@@ -566,7 +566,7 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry)
 		const UINT32 diffSize = (vBarEntry->count - vBarEntry->size) * bpp;
 		BYTE* tmp;
 		vBarEntry->size = vBarEntry->count;
-		tmp = (BYTE*)realloc(vBarEntry->pixels, vBarEntry->count * bpp * 1ULL);
+		tmp = (BYTE*)realloc(vBarEntry->pixels, 1ull * vBarEntry->count * bpp);
 
 		if (!tmp)
 		{
@@ -591,13 +591,9 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry)
 static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 bandsByteCount,
                                         UINT32 nWidth, UINT32 nHeight, BYTE* pDstData,
                                         UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst,
-                                        UINT32 nYDst)
+                                        UINT32 nYDst, UINT32 nDstWidth, UINT32 nDstHeight)
 {
-	UINT32 i, y;
-	UINT32 count;
-	UINT32 suboffset;
-	UINT32 nXDstRel;
-	UINT32 nYDstRel;
+	UINT32 suboffset = 0;
 
 	if (Stream_GetRemainingLength(s) < bandsByteCount)
 	{
@@ -605,22 +601,20 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 		return FALSE;
 	}
 
-	suboffset = 0;
-
 	while (suboffset < bandsByteCount)
 	{
-		BYTE r, g, b;
+		BYTE cr, cg, cb;
 		UINT16 xStart;
 		UINT16 xEnd;
 		UINT16 yStart;
 		UINT16 yEnd;
 		UINT32 colorBkg;
 		UINT16 vBarHeader;
-		UINT16 vBarYOn;
+		UINT16 vBarYOn = 0;
 		UINT16 vBarYOff;
 		UINT32 vBarCount;
 		UINT32 vBarPixelCount;
-		UINT32 vBarShortPixelCount;
+		UINT32 vBarShortPixelCount = 0;
 
 		if (Stream_GetRemainingLength(s) < 11)
 		{
@@ -632,11 +626,11 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 		Stream_Read_UINT16(s, xEnd);
 		Stream_Read_UINT16(s, yStart);
 		Stream_Read_UINT16(s, yEnd);
-		Stream_Read_UINT8(s, b);
-		Stream_Read_UINT8(s, g);
-		Stream_Read_UINT8(s, r);
+		Stream_Read_UINT8(s, cb);
+		Stream_Read_UINT8(s, cg);
+		Stream_Read_UINT8(s, cr);
 		suboffset += 11;
-		colorBkg = FreeRDPGetColor(clear->format, r, g, b, 0xFF);
+		colorBkg = FreeRDPGetColor(clear->format, cr, cg, cb, 0xFF);
 
 		if (xEnd < xStart)
 		{
@@ -652,13 +646,13 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 
 		vBarCount = (xEnd - xStart) + 1;
 
-		for (i = 0; i < vBarCount; i++)
+		for (UINT32 i = 0; i < vBarCount; i++)
 		{
 			UINT32 vBarHeight;
 			CLEAR_VBAR_ENTRY* vBarEntry = NULL;
-			CLEAR_VBAR_ENTRY* vBarShortEntry;
+			CLEAR_VBAR_ENTRY* vBarShortEntry = NULL;
 			BOOL vBarUpdate = FALSE;
-			const BYTE* pSrcPixel;
+			const BYTE* cpSrcPixel;
 
 			if (Stream_GetRemainingLength(s) < 2)
 			{
@@ -740,11 +734,11 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 				if (!resize_vbar_entry(clear, vBarShortEntry))
 					return FALSE;
 
-				for (y = 0; y < vBarShortPixelCount; y++)
+				for (UINT32 y = 0; y < vBarShortPixelCount; y++)
 				{
-					BYTE r, g, b;
+					BYTE r = 0, g = 0, b = 0;
 					BYTE* dstBuffer = &vBarShortEntry->pixels[y * GetBytesPerPixel(clear->format)];
-					UINT32 color;
+					UINT32 color = 0;
 					Stream_Read_UINT8(s, b);
 					Stream_Read_UINT8(s, g);
 					Stream_Read_UINT8(s, r);
@@ -804,8 +798,8 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 
 				dstBuffer = vBarEntry->pixels;
 				/* if (y < vBarYOn), use colorBkg */
-				y = 0;
-				count = vBarYOn;
+				UINT32 y = 0;
+				UINT32 count = vBarYOn;
 
 				if ((y + count) > vBarPixelCount)
 					count = (vBarPixelCount > y) ? (vBarPixelCount - y) : 0;
@@ -827,8 +821,15 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 					count = (vBarPixelCount > y) ? (vBarPixelCount - y) : 0;
 
 				if (count > 0)
-					pSrcPixel =
-					    &vBarShortEntry->pixels[(y - vBarYOn) * GetBytesPerPixel(clear->format)];
+				{
+					const size_t offset = (1ull * y - vBarYOn) * GetBytesPerPixel(clear->format);
+					pSrcPixel = &vBarShortEntry->pixels[offset];
+					if (offset + count > vBarShortEntry->count)
+					{
+						WLog_ERR(TAG, "offset + count > vBarShortEntry->count");
+						return FALSE;
+					}
+				}
 
 				for (x = 0; x < count; x++)
 				{
@@ -868,28 +869,31 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 					return FALSE;
 			}
 
-			nXDstRel = nXDst + xStart;
-			nYDstRel = nYDst + yStart;
-			pSrcPixel = vBarEntry->pixels;
+			const UINT32 nXDstRel = nXDst + xStart;
+			const UINT32 nYDstRel = nYDst + yStart;
+			cpSrcPixel = vBarEntry->pixels;
 
 			if (i < nWidth)
 			{
-				count = vBarEntry->count;
+				UINT32 count = vBarEntry->count;
 
 				if (count > nHeight)
 					count = nHeight;
 
-				for (y = 0; y < count; y++)
+				if (nXDstRel + i > nDstWidth)
+					return FALSE;
+
+				for (UINT32 y = 0; y < count; y++)
 				{
 					BYTE* pDstPixel8 = &pDstData[((nYDstRel + y) * nDstStep) +
 					                             ((nXDstRel + i) * GetBytesPerPixel(DstFormat))];
-					UINT32 color = ReadColor(pSrcPixel, clear->format);
+					UINT32 color = ReadColor(cpSrcPixel, clear->format);
 					color = FreeRDPConvertColor(color, clear->format, DstFormat, NULL);
 
 					if (!WriteColor(pDstPixel8, DstFormat, color))
 						return FALSE;
 
-					pSrcPixel += GetBytesPerPixel(clear->format);
+					cpSrcPixel += GetBytesPerPixel(clear->format);
 				}
 			}
 		}
@@ -980,7 +984,7 @@ static BOOL clear_decompress_glyph_data(CLEAR_CONTEXT* clear, wStream* s, UINT32
 		if (glyphEntry->count > glyphEntry->size)
 		{
 			BYTE* tmp;
-			tmp = realloc(glyphEntry->pixels, glyphEntry->count * bpp * 1ULL);
+			tmp = realloc(glyphEntry->pixels, 1ull * glyphEntry->count * bpp);
 
 			if (!tmp)
 			{
@@ -1117,7 +1121,7 @@ INT32 clear_decompress(CLEAR_CONTEXT* clear, const BYTE* pSrcData, UINT32 SrcSiz
 	if (bandsByteCount > 0)
 	{
 		if (!clear_decompress_bands_data(clear, s, bandsByteCount, nWidth, nHeight, pDstData,
-		                                 DstFormat, nDstStep, nXDst, nYDst))
+		                                 DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight))
 		{
 			WLog_ERR(TAG, "clear_decompress_bands_data failed!");
 			goto fail;
diff --git a/libfreerdp/codec/color.c b/libfreerdp/codec/color.c
index edbfcd4..f8467c2 100644
--- a/libfreerdp/codec/color.c
+++ b/libfreerdp/codec/color.c
@@ -56,7 +56,7 @@ BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* data)
 	 * means of accessing individual pixels in blitting operations
 	 */
 	scanline = (width + 7) / 8;
-	dstData = (BYTE*)_aligned_malloc(width * height * 1ULL, 16);
+	dstData = (BYTE*)_aligned_malloc(1ull * width * height, 16);
 
 	if (!dstData)
 		return NULL;
@@ -545,7 +545,7 @@ BOOL freerdp_image_copy_from_pointer_data(BYTE* pDstData, UINT32 DstFormat, UINT
 	for (y = nYDst; y < nHeight; y++)
 	{
 		BYTE* pDstLine = &pDstData[y * nDstStep + nXDst * dstBytesPerPixel];
-		memset(pDstLine, 0, dstBytesPerPixel * (nWidth - nXDst) * 1ULL);
+		memset(pDstLine, 0, 1ull * dstBytesPerPixel * (nWidth - nXDst));
 	}
 
 	switch (xorBpp)
@@ -613,6 +613,9 @@ BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32
 	if (!pDstData || !pSrcData)
 		return FALSE;
 
+	if ((nWidth == 0) || (nHeight == 0))
+		return TRUE;
+
 	if (nDstStep == 0)
 		nDstStep = nWidth * GetBytesPerPixel(DstFormat);
 
@@ -728,21 +731,22 @@ BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32
 BOOL freerdp_image_fill(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst,
                         UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 color)
 {
-	UINT32 x, y;
+	if ((nWidth == 0) || (nHeight == 0))
+		return TRUE;
 	const UINT32 bpp = GetBytesPerPixel(DstFormat);
 	BYTE* pFirstDstLine = &pDstData[nYDst * nDstStep];
 	BYTE* pFirstDstLineXOffset = &pFirstDstLine[nXDst * bpp];
 
-	for (x = 0; x < nWidth; x++)
+	for (UINT32 x = 0; x < nWidth; x++)
 	{
 		BYTE* pDst = &pFirstDstLine[(x + nXDst) * bpp];
 		WriteColor(pDst, DstFormat, color);
 	}
 
-	for (y = 1; y < nHeight; y++)
+	for (UINT32 y = 1; y < nHeight; y++)
 	{
 		BYTE* pDstLine = &pDstData[(y + nYDst) * nDstStep + nXDst * bpp];
-		memcpy(pDstLine, pFirstDstLineXOffset, nWidth * bpp * 1ULL);
+		memcpy(pDstLine, pFirstDstLineXOffset, 1ull * nWidth * bpp);
 	}
 
 	return TRUE;
diff --git a/libfreerdp/codec/dsp_ffmpeg.c b/libfreerdp/codec/dsp_ffmpeg.c
index 086c797..ef67914 100644
--- a/libfreerdp/codec/dsp_ffmpeg.c
+++ b/libfreerdp/codec/dsp_ffmpeg.c
@@ -418,9 +418,29 @@ static BOOL ffmpeg_resample_frame(AVAudioResampleContext* context, AVFrame* in,
 static BOOL ffmpeg_encode_frame(AVCodecContext* context, AVFrame* in, AVPacket* packet,
                                 wStream* out)
 {
-	int ret;
+	if (in->format == AV_SAMPLE_FMT_FLTP)
+	{
+		uint8_t** pp = in->extended_data;
+		for (int y = 0; y < in->channels; y++)
+		{
+			float* data = pp[y];
+			for (int x = 0; x < in->nb_samples; x++)
+			{
+				const float val1 = data[x];
+				if (isnan(val1))
+					data[x] = 0.0f;
+				else if (isinf(val1))
+				{
+					if (val1 < 0.0f)
+						data[x] = -1.0f;
+					else
+						data[x] = 1.0f;
+				}
+			}
+		}
+	}
 	/* send the packet with the compressed data to the encoder */
-	ret = avcodec_send_frame(context, in);
+	int ret = avcodec_send_frame(context, in);
 
 	if (ret < 0)
 	{
diff --git a/libfreerdp/codec/h264.c b/libfreerdp/codec/h264.c
index 36b5a7c..3f1ddb3 100644
--- a/libfreerdp/codec/h264.c
+++ b/libfreerdp/codec/h264.c
@@ -63,9 +63,9 @@ BOOL avc420_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT3
 		_aligned_free(h264->pYUVData[0]);
 		_aligned_free(h264->pYUVData[1]);
 		_aligned_free(h264->pYUVData[2]);
-		h264->pYUVData[0] = _aligned_malloc(h264->iStride[0] * height * 1ULL, 16);
-		h264->pYUVData[1] = _aligned_malloc(h264->iStride[1] * height * 1ULL, 16);
-		h264->pYUVData[2] = _aligned_malloc(h264->iStride[2] * height * 1ULL, 16);
+		h264->pYUVData[0] = _aligned_malloc(1ull * h264->iStride[0] * height, 16);
+		h264->pYUVData[1] = _aligned_malloc(1ull * h264->iStride[1] * height, 16);
+		h264->pYUVData[2] = _aligned_malloc(1ull * h264->iStride[2] * height, 16);
 
 		if (!h264->pYUVData[0] || !h264->pYUVData[1] || !h264->pYUVData[2])
 			return FALSE;
diff --git a/libfreerdp/codec/h264_ffmpeg.c b/libfreerdp/codec/h264_ffmpeg.c
index 4e3ba8f..9c445b8 100644
--- a/libfreerdp/codec/h264_ffmpeg.c
+++ b/libfreerdp/codec/h264_ffmpeg.c
@@ -529,10 +529,12 @@ static BOOL libavcodec_init(H264_CONTEXT* h264)
 			goto EXCEPTION;
 		}
 
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100)
 		if (sys->codecDecoder->capabilities & AV_CODEC_CAP_TRUNCATED)
 		{
 			sys->codecDecoderContext->flags |= AV_CODEC_FLAG_TRUNCATED;
 		}
+#endif
 
 #ifdef WITH_VAAPI
 
diff --git a/libfreerdp/codec/include/bitmap.c b/libfreerdp/codec/include/bitmap.c
index 38bcaa8..af83387 100644
--- a/libfreerdp/codec/include/bitmap.c
+++ b/libfreerdp/codec/include/bitmap.c
@@ -31,7 +31,10 @@ static INLINE BYTE* WRITEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, UINT32 r
 	BYTE mask = 0x01;
 
 	if (cBits > 8)
+	{
+		WLog_ERR(TAG, "cBits %d > 8", cBits);
 		return NULL;
+	}
 
 	if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits))
 		return NULL;
@@ -46,7 +49,6 @@ static INLINE BYTE* WRITEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, UINT32 r
 			data = xorPixel;
 
 		DESTWRITEPIXEL(pbDest, data);
-		DESTNEXTPIXEL(pbDest);
 		mask = mask << 1;
 	});
 	return pbDest;
@@ -62,7 +64,10 @@ static INLINE BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd,
 	BYTE mask = 0x01;
 
 	if (cBits > 8)
+	{
+		WLog_ERR(TAG, "cBits %d > 8", cBits);
 		return NULL;
+	}
 
 	if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits))
 		return NULL;
@@ -76,7 +81,6 @@ static INLINE BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd,
 			data = BLACK_PIXEL;
 
 		DESTWRITEPIXEL(pbDest, data);
-		DESTNEXTPIXEL(pbDest);
 		mask = mask << 1;
 	});
 	return pbDest;
@@ -88,6 +92,9 @@ static INLINE BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd,
 static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BYTE* pbDestBuffer,
                                  UINT32 rowDelta, UINT32 width, UINT32 height)
 {
+#if defined(WITH_DEBUG_CODECS)
+	char sbuffer[128] = { 0 };
+#endif
 	const BYTE* pbSrc = pbSrcBuffer;
 	const BYTE* pbEnd;
 	const BYTE* pbDestEnd;
@@ -100,14 +107,22 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 	PIXEL pixelA, pixelB;
 	UINT32 runLength;
 	UINT32 code;
-	UINT32 advance;
+	UINT32 advance = 0;
 	RLEEXTRA
 
 	if ((rowDelta == 0) || (rowDelta < width))
+	{
+		WLog_ERR(TAG, "Invalid arguments: rowDelta=%" PRIu32 " == 0 || < width=%" PRIu32, rowDelta,
+		         width);
 		return FALSE;
+	}
 
 	if (!pbSrcBuffer || !pbDestBuffer)
+	{
+		WLog_ERR(TAG, "Invalid arguments: pbSrcBuffer=%p, pbDestBuffer=%p", pbSrcBuffer,
+		         pbDestBuffer);
 		return FALSE;
+	}
 
 	pbEnd = pbSrcBuffer + cbSrcBuffer;
 	pbDestEnd = pbDestBuffer + rowDelta * height;
@@ -130,10 +145,17 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 		*/
 		code = ExtractCodeId(*pbSrc);
 
+#if defined(WITH_DEBUG_CODECS)
+		WLog_VRB(TAG, "pbSrc=%p code=%s, rem=%" PRIuz, pbSrc,
+		         rle_code_str_buffer(code, sbuffer, sizeof(sbuffer)), pbEnd - pbSrc);
+#endif
+
 		/* Handle Background Run Orders. */
-		if (code == REGULAR_BG_RUN || code == MEGA_MEGA_BG_RUN)
+		if ((code == REGULAR_BG_RUN) || (code == MEGA_MEGA_BG_RUN))
 		{
-			runLength = ExtractRunLength(code, pbSrc, &advance);
+			runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
+			if (advance == 0)
+				return FALSE;
 			pbSrc = pbSrc + advance;
 
 			if (fFirstLine)
@@ -144,17 +166,13 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 						return FALSE;
 
 					DESTWRITEPIXEL(pbDest, fgPel);
-					DESTNEXTPIXEL(pbDest);
 					runLength = runLength - 1;
 				}
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
 					return FALSE;
 
-				UNROLL(runLength, {
-					DESTWRITEPIXEL(pbDest, BLACK_PIXEL);
-					DESTNEXTPIXEL(pbDest);
-				});
+				UNROLL(runLength, { DESTWRITEPIXEL(pbDest, BLACK_PIXEL); });
 			}
 			else
 			{
@@ -166,7 +184,6 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 						return FALSE;
 
 					DESTWRITEPIXEL(pbDest, temp ^ fgPel);
-					DESTNEXTPIXEL(pbDest);
 					runLength--;
 				}
 
@@ -176,7 +193,6 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 				UNROLL(runLength, {
 					DESTREADPIXEL(temp, pbDest - rowDelta);
 					DESTWRITEPIXEL(pbDest, temp);
-					DESTNEXTPIXEL(pbDest);
 				});
 			}
 
@@ -196,15 +212,16 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			case MEGA_MEGA_FG_RUN:
 			case LITE_SET_FG_FG_RUN:
 			case MEGA_MEGA_SET_FG_RUN:
-				runLength = ExtractRunLength(code, pbSrc, &advance);
+				runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
+				if (advance == 0)
+					return FALSE;
 				pbSrc = pbSrc + advance;
 
 				if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN)
 				{
-					if (pbSrc >= pbEnd)
+					if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
 						return FALSE;
 					SRCREADPIXEL(fgPel, pbSrc);
-					SRCNEXTPIXEL(pbSrc);
 				}
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
@@ -212,17 +229,13 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 
 				if (fFirstLine)
 				{
-					UNROLL(runLength, {
-						DESTWRITEPIXEL(pbDest, fgPel);
-						DESTNEXTPIXEL(pbDest);
-					});
+					UNROLL(runLength, { DESTWRITEPIXEL(pbDest, fgPel); });
 				}
 				else
 				{
 					UNROLL(runLength, {
 						DESTREADPIXEL(temp, pbDest - rowDelta);
 						DESTWRITEPIXEL(pbDest, temp ^ fgPel);
-						DESTNEXTPIXEL(pbDest);
 					});
 				}
 
@@ -231,45 +244,41 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			/* Handle Dithered Run Orders. */
 			case LITE_DITHERED_RUN:
 			case MEGA_MEGA_DITHERED_RUN:
-				runLength = ExtractRunLength(code, pbSrc, &advance);
+				runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
+				if (advance == 0)
+					return FALSE;
 				pbSrc = pbSrc + advance;
-				if (pbSrc >= pbEnd)
+				if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
 					return FALSE;
 				SRCREADPIXEL(pixelA, pbSrc);
-				SRCNEXTPIXEL(pbSrc);
-				if (pbSrc >= pbEnd)
+				if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
 					return FALSE;
 				SRCREADPIXEL(pixelB, pbSrc);
-				SRCNEXTPIXEL(pbSrc);
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength * 2))
 					return FALSE;
 
 				UNROLL(runLength, {
 					DESTWRITEPIXEL(pbDest, pixelA);
-					DESTNEXTPIXEL(pbDest);
 					DESTWRITEPIXEL(pbDest, pixelB);
-					DESTNEXTPIXEL(pbDest);
 				});
 				break;
 
 			/* Handle Color Run Orders. */
 			case REGULAR_COLOR_RUN:
 			case MEGA_MEGA_COLOR_RUN:
-				runLength = ExtractRunLength(code, pbSrc, &advance);
+				runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
+				if (advance == 0)
+					return FALSE;
 				pbSrc = pbSrc + advance;
-				if (pbSrc >= pbEnd)
+				if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
 					return FALSE;
 				SRCREADPIXEL(pixelA, pbSrc);
-				SRCNEXTPIXEL(pbSrc);
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
 					return FALSE;
 
-				UNROLL(runLength, {
-					DESTWRITEPIXEL(pbDest, pixelA);
-					DESTNEXTPIXEL(pbDest);
-				});
+				UNROLL(runLength, { DESTWRITEPIXEL(pbDest, pixelA); });
 				break;
 
 			/* Handle Foreground/Background Image Orders. */
@@ -277,17 +286,20 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			case MEGA_MEGA_FGBG_IMAGE:
 			case LITE_SET_FG_FGBG_IMAGE:
 			case MEGA_MEGA_SET_FGBG_IMAGE:
-				runLength = ExtractRunLength(code, pbSrc, &advance);
+				runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
+				if (advance == 0)
+					return FALSE;
 				pbSrc = pbSrc + advance;
 
-				if (pbSrc >= pbEnd)
-					return FALSE;
 				if (code == LITE_SET_FG_FGBG_IMAGE || code == MEGA_MEGA_SET_FGBG_IMAGE)
 				{
+					if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
+						return FALSE;
 					SRCREADPIXEL(fgPel, pbSrc);
-					SRCNEXTPIXEL(pbSrc);
 				}
 
+				if (!buffer_within_range(pbSrc, runLength / 8, pbEnd))
+					return FALSE;
 				if (fFirstLine)
 				{
 					while (runLength > 8)
@@ -306,8 +318,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 				{
 					while (runLength > 8)
 					{
-						bitmask = *pbSrc;
-						pbSrc = pbSrc + 1;
+						bitmask = *pbSrc++;
+
 						pbDest = WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, bitmask, fgPel, 8);
 
 						if (!pbDest)
@@ -319,8 +331,9 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 
 				if (runLength > 0)
 				{
-					bitmask = *pbSrc;
-					pbSrc = pbSrc + 1;
+					if (!buffer_within_range(pbSrc, 1, pbEnd))
+						return FALSE;
+					bitmask = *pbSrc++;
 
 					if (fFirstLine)
 					{
@@ -342,23 +355,25 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 			/* Handle Color Image Orders. */
 			case REGULAR_COLOR_IMAGE:
 			case MEGA_MEGA_COLOR_IMAGE:
-				runLength = ExtractRunLength(code, pbSrc, &advance);
+				runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
+				if (advance == 0)
+					return FALSE;
 				pbSrc = pbSrc + advance;
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
 					return FALSE;
+				if (!ENSURE_CAPACITY(pbSrc, pbEnd, runLength))
+					return FALSE;
 
 				UNROLL(runLength, {
-					if (pbSrc >= pbEnd)
-						return FALSE;
 					SRCREADPIXEL(temp, pbSrc);
-					SRCNEXTPIXEL(pbSrc);
 					DESTWRITEPIXEL(pbDest, temp);
-					DESTNEXTPIXEL(pbDest);
 				});
 				break;
 
 			/* Handle Special Order 1. */
 			case SPECIAL_FGBG_1:
+				if (!buffer_within_range(pbSrc, 1, pbEnd))
+					return FALSE;
 				pbSrc = pbSrc + 1;
 
 				if (fFirstLine)
@@ -379,6 +394,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 
 			/* Handle Special Order 2. */
 			case SPECIAL_FGBG_2:
+				if (!buffer_within_range(pbSrc, 1, pbEnd))
+					return FALSE;
 				pbSrc = pbSrc + 1;
 
 				if (fFirstLine)
@@ -399,27 +416,31 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY
 
 			/* Handle White Order. */
 			case SPECIAL_WHITE:
+				if (!buffer_within_range(pbSrc, 1, pbEnd))
+					return FALSE;
 				pbSrc = pbSrc + 1;
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
 					return FALSE;
 
 				DESTWRITEPIXEL(pbDest, WHITE_PIXEL);
-				DESTNEXTPIXEL(pbDest);
 				break;
 
 			/* Handle Black Order. */
 			case SPECIAL_BLACK:
+				if (!buffer_within_range(pbSrc, 1, pbEnd))
+					return FALSE;
 				pbSrc = pbSrc + 1;
 
 				if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
 					return FALSE;
 
 				DESTWRITEPIXEL(pbDest, BLACK_PIXEL);
-				DESTNEXTPIXEL(pbDest);
 				break;
 
 			default:
+				WLog_ERR(TAG, "invalid code 0x%08" PRIx32 ", pbSrcBuffer=%p, pbSrc=%p, pbEnd=%p",
+				         code, pbSrcBuffer, pbSrc, pbEnd);
 				return FALSE;
 		}
 	}
diff --git a/libfreerdp/codec/interleaved.c b/libfreerdp/codec/interleaved.c
index b76fe1c..e419876 100644
--- a/libfreerdp/codec/interleaved.c
+++ b/libfreerdp/codec/interleaved.c
@@ -21,6 +21,7 @@
  * limitations under the License.
  */
 
+#include <winpr/assert.h>
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
@@ -30,15 +31,16 @@
 
 #define TAG FREERDP_TAG("codec")
 
-#define UNROLL_BODY(_exp, _count)      \
-	do                                 \
-	{                                  \
-		size_t x;                      \
-		for (x = 0; x < (_count); x++) \
-		{                              \
-			do                         \
-				_exp while (FALSE);    \
-		}                              \
+#define UNROLL_BODY(_exp, _count)             \
+	do                                        \
+	{                                         \
+		for (size_t x = 0; x < (_count); x++) \
+		{                                     \
+			do                                \
+			{                                 \
+				_exp                          \
+			} while (FALSE);                  \
+		}                                     \
 	} while (FALSE)
 
 #define UNROLL_MULTIPLE(_condition, _exp, _count) \
@@ -97,6 +99,80 @@ static const BYTE g_MaskSpecialFgBg2 = 0x05;
 static const BYTE g_MaskRegularRunLength = 0x1F;
 static const BYTE g_MaskLiteRunLength = 0x0F;
 
+static const char* rle_code_str(UINT32 code)
+{
+	switch (code)
+	{
+		case REGULAR_BG_RUN:
+			return "REGULAR_BG_RUN";
+		case MEGA_MEGA_BG_RUN:
+			return "MEGA_MEGA_BG_RUN";
+		case REGULAR_FG_RUN:
+			return "REGULAR_FG_RUN";
+		case MEGA_MEGA_FG_RUN:
+			return "MEGA_MEGA_FG_RUN";
+		case LITE_SET_FG_FG_RUN:
+			return "LITE_SET_FG_FG_RUN";
+		case MEGA_MEGA_SET_FG_RUN:
+			return "MEGA_MEGA_SET_FG_RUN";
+		case LITE_DITHERED_RUN:
+			return "LITE_DITHERED_RUN";
+		case MEGA_MEGA_DITHERED_RUN:
+			return "MEGA_MEGA_DITHERED_RUN";
+		case REGULAR_COLOR_RUN:
+			return "REGULAR_COLOR_RUN";
+		case MEGA_MEGA_COLOR_RUN:
+			return "MEGA_MEGA_COLOR_RUN";
+		case REGULAR_FGBG_IMAGE:
+			return "REGULAR_FGBG_IMAGE";
+		case MEGA_MEGA_FGBG_IMAGE:
+			return "MEGA_MEGA_FGBG_IMAGE";
+		case LITE_SET_FG_FGBG_IMAGE:
+			return "LITE_SET_FG_FGBG_IMAGE";
+		case MEGA_MEGA_SET_FGBG_IMAGE:
+			return "MEGA_MEGA_SET_FGBG_IMAGE";
+		case REGULAR_COLOR_IMAGE:
+			return "REGULAR_COLOR_IMAGE";
+		case MEGA_MEGA_COLOR_IMAGE:
+			return "MEGA_MEGA_COLOR_IMAGE";
+		case SPECIAL_FGBG_1:
+			return "SPECIAL_FGBG_1";
+		case SPECIAL_FGBG_2:
+			return "SPECIAL_FGBG_2";
+		case SPECIAL_WHITE:
+			return "SPECIAL_WHITE";
+		case SPECIAL_BLACK:
+			return "SPECIAL_BLACK";
+		default:
+			return "UNKNOWN";
+	}
+}
+
+static const char* rle_code_str_buffer(UINT32 code, char* buffer, size_t size)
+{
+	const char* str = rle_code_str(code);
+	_snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, code);
+	return buffer;
+}
+
+#define buffer_within_range(pbSrc, size, pbEnd) \
+	buffer_within_range_((pbSrc), (size), (pbEnd), __func__, __FILE__, __LINE__)
+static INLINE BOOL buffer_within_range_(const void* pbSrc, size_t size, const void* pbEnd,
+                                        const char* fkt, const char* file, size_t line)
+{
+	WINPR_UNUSED(file);
+	WINPR_ASSERT(pbSrc);
+	WINPR_ASSERT(pbEnd);
+
+	if ((const char*)pbSrc + size > (const char*)pbEnd)
+	{
+		WLog_ERR(TAG, "[%s:%" PRIuz "] pbSrc=%p + %" PRIuz " > pbEnd=%p", fkt, line, pbSrc, size,
+		         pbEnd);
+		return FALSE;
+	}
+	return TRUE;
+}
+
 /**
  * Reads the supplied order header and extracts the compression
  * order code ID.
@@ -127,71 +203,155 @@ static INLINE UINT32 ExtractCodeId(BYTE bOrderHdr)
 /**
  * Extract the run length of a compression order.
  */
-static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, UINT32* advance)
+static UINT ExtractRunLengthRegularFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance)
 {
-	UINT32 runLength;
-	UINT32 ladvance;
-	ladvance = 1;
-	runLength = 0;
+	UINT runLength = 0;
 
-	switch (code)
+	WINPR_ASSERT(pbOrderHdr);
+	WINPR_ASSERT(pbEnd);
+	WINPR_ASSERT(advance);
+
+	runLength = (*pbOrderHdr) & g_MaskRegularRunLength;
+	if (runLength == 0)
 	{
-		case REGULAR_FGBG_IMAGE:
-			runLength = (*pbOrderHdr) & g_MaskRegularRunLength;
+		if (!buffer_within_range(pbOrderHdr, 2, pbEnd))
+		{
+			*advance = 0;
+			return 0;
+		}
+		runLength = *(pbOrderHdr + 1) + 1;
+		(*advance)++;
+	}
+	else
+		runLength = runLength * 8;
 
-			if (runLength == 0)
-			{
-				runLength = (*(pbOrderHdr + 1)) + 1;
-				ladvance += 1;
-			}
-			else
-			{
-				runLength = runLength * 8;
-			}
+	return runLength;
+}
 
-			break;
+static UINT ExtractRunLengthLiteFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance)
+{
+	UINT runLength = 0;
 
-		case LITE_SET_FG_FGBG_IMAGE:
-			runLength = (*pbOrderHdr) & g_MaskLiteRunLength;
+	WINPR_ASSERT(pbOrderHdr);
+	WINPR_ASSERT(pbEnd);
+	WINPR_ASSERT(advance);
 
-			if (runLength == 0)
-			{
-				runLength = (*(pbOrderHdr + 1)) + 1;
-				ladvance += 1;
-			}
-			else
-			{
-				runLength = runLength * 8;
-			}
+	runLength = *pbOrderHdr & g_MaskLiteRunLength;
+	if (runLength == 0)
+	{
+		if (!buffer_within_range(pbOrderHdr, 2, pbEnd))
+		{
+			*advance = 0;
+			return 0;
+		}
+		runLength = *(pbOrderHdr + 1) + 1;
+		(*advance)++;
+	}
+	else
+		runLength = runLength * 8;
+
+	return runLength;
+}
+
+static UINT ExtractRunLengthRegular(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance)
+{
+	UINT runLength = 0;
+
+	WINPR_ASSERT(pbOrderHdr);
+	WINPR_ASSERT(pbEnd);
+	WINPR_ASSERT(advance);
+
+	runLength = *pbOrderHdr & g_MaskRegularRunLength;
+	if (runLength == 0)
+	{
+		if (!buffer_within_range(pbOrderHdr, 2, pbEnd))
+		{
+			*advance = 0;
+			return 0;
+		}
+		runLength = *(pbOrderHdr + 1) + 32;
+		(*advance)++;
+	}
+
+	return runLength;
+}
+
+static UINT ExtractRunLengthMegaMega(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance)
+{
+	UINT runLength = 0;
+
+	WINPR_ASSERT(pbOrderHdr);
+	WINPR_ASSERT(pbEnd);
+	WINPR_ASSERT(advance);
+
+	if (!buffer_within_range(pbOrderHdr, 3, pbEnd))
+	{
+		*advance = 0;
+		return 0;
+	}
+
+	runLength = ((UINT16)pbOrderHdr[1]) | (((UINT16)pbOrderHdr[2]) << 8);
+	(*advance) += 2;
+
+	return runLength;
+}
+
+static UINT ExtractRunLengthLite(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance)
+{
+	UINT runLength = 0;
+
+	WINPR_ASSERT(pbOrderHdr);
+	WINPR_ASSERT(pbEnd);
+	WINPR_ASSERT(advance);
+
+	runLength = *pbOrderHdr & g_MaskLiteRunLength;
+	if (runLength == 0)
+	{
+		if (!buffer_within_range(pbOrderHdr, 2, pbEnd))
+		{
+			*advance = 0;
+			return 0;
+		}
+		runLength = *(pbOrderHdr + 1) + 16;
+		(*advance)++;
+	}
+	return runLength;
+}
+
+static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, const BYTE* pbEnd,
+                                      UINT32* advance)
+{
+	UINT32 runLength = 0;
+	UINT32 ladvance = 1;
+
+	WINPR_ASSERT(pbOrderHdr);
+	WINPR_ASSERT(pbEnd);
+	WINPR_ASSERT(advance);
+
+	*advance = 0;
+	if (!buffer_within_range(pbOrderHdr, 0, pbEnd))
+		return 0;
+
+	switch (code)
+	{
+		case REGULAR_FGBG_IMAGE:
+			runLength = ExtractRunLengthRegularFgBg(pbOrderHdr, pbEnd, &ladvance);
+			break;
 
+		case LITE_SET_FG_FGBG_IMAGE:
+			runLength = ExtractRunLengthLiteFgBg(pbOrderHdr, pbEnd, &ladvance);
 			break;
 
 		case REGULAR_BG_RUN:
 		case REGULAR_FG_RUN:
 		case REGULAR_COLOR_RUN:
 		case REGULAR_COLOR_IMAGE:
-			runLength = (*pbOrderHdr) & g_MaskRegularRunLength;
-
-			if (runLength == 0)
-			{
-				/* An extended (MEGA) run. */
-				runLength = (*(pbOrderHdr + 1)) + 32;
-				ladvance += 1;
-			}
-
+			runLength = ExtractRunLengthRegular(pbOrderHdr, pbEnd, &ladvance);
 			break;
 
 		case LITE_SET_FG_FG_RUN:
 		case LITE_DITHERED_RUN:
-			runLength = (*pbOrderHdr) & g_MaskLiteRunLength;
-
-			if (runLength == 0)
-			{
-				/* An extended (MEGA) run. */
-				runLength = (*(pbOrderHdr + 1)) + 16;
-				ladvance += 1;
-			}
-
+			runLength = ExtractRunLengthLite(pbOrderHdr, pbEnd, &ladvance);
 			break;
 
 		case MEGA_MEGA_BG_RUN:
@@ -202,8 +362,12 @@ static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, UINT3
 		case MEGA_MEGA_FGBG_IMAGE:
 		case MEGA_MEGA_SET_FGBG_IMAGE:
 		case MEGA_MEGA_COLOR_IMAGE:
-			runLength = ((UINT16)pbOrderHdr[1]) | ((UINT16)(pbOrderHdr[2] << 8));
-			ladvance += 2;
+			runLength = ExtractRunLengthMegaMega(pbOrderHdr, pbEnd, &ladvance);
+			break;
+
+		default:
+			runLength = 0;
+			ladvance = 0;
 			break;
 	}
 
@@ -211,20 +375,32 @@ static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, UINT3
 	return runLength;
 }
 
-static INLINE BOOL ensure_capacity(const BYTE* start, const BYTE* end, size_t size, size_t base)
+#define ensure_capacity(start, end, size, base) \
+	ensure_capacity_((start), (end), (size), (base), __func__, __FILE__, __LINE__)
+static INLINE BOOL ensure_capacity_(const BYTE* start, const BYTE* end, size_t size, size_t base,
+                                    const char* fkt, const char* file, size_t line)
 {
 	const size_t available = (uintptr_t)end - (uintptr_t)start;
 	const BOOL rc = available >= size * base;
-	return rc && (start <= end);
+	const BOOL res = rc && (start <= end);
+
+	if (!res)
+		WLog_ERR(TAG,
+		         "[%s:%" PRIuz "] failed: start=%p <= end=%p, available=%" PRIuz " >= size=%" PRIuz
+		         " * base=%" PRIuz,
+		         fkt, line, start, end, available, size, base);
+	return res;
 }
 
 static INLINE void write_pixel_8(BYTE* _buf, BYTE _pix)
 {
+	WINPR_ASSERT(_buf);
 	*_buf = _pix;
 }
 
 static INLINE void write_pixel_24(BYTE* _buf, UINT32 _pix)
 {
+	WINPR_ASSERT(_buf);
 	(_buf)[0] = (BYTE)(_pix);
 	(_buf)[1] = (BYTE)((_pix) >> 8);
 	(_buf)[2] = (BYTE)((_pix) >> 16);
@@ -232,6 +408,7 @@ static INLINE void write_pixel_24(BYTE* _buf, UINT32 _pix)
 
 static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix)
 {
+	WINPR_ASSERT(_buf);
 	_buf[0] = _pix & 0xFF;
 	_buf[1] = (_pix >> 8) & 0xFF;
 }
@@ -239,19 +416,30 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix)
 #undef DESTWRITEPIXEL
 #undef DESTREADPIXEL
 #undef SRCREADPIXEL
-#undef DESTNEXTPIXEL
-#undef SRCNEXTPIXEL
 #undef WRITEFGBGIMAGE
 #undef WRITEFIRSTLINEFGBGIMAGE
 #undef RLEDECOMPRESS
 #undef RLEEXTRA
 #undef WHITE_PIXEL
+#undef PIXEL_SIZE
+#undef PIXEL
+#define PIXEL_SIZE 1
+#define PIXEL BYTE
 #define WHITE_PIXEL 0xFF
-#define DESTWRITEPIXEL(_buf, _pix) write_pixel_8(_buf, _pix)
+#define DESTWRITEPIXEL(_buf, _pix) \
+	do                             \
+	{                              \
+		write_pixel_8(_buf, _pix); \
+		_buf += 1;                 \
+	} while (0)
 #define DESTREADPIXEL(_pix, _buf) _pix = (_buf)[0]
-#define SRCREADPIXEL(_pix, _buf) _pix = (_buf)[0]
-#define DESTNEXTPIXEL(_buf) _buf += 1
-#define SRCNEXTPIXEL(_buf) _buf += 1
+#define SRCREADPIXEL(_pix, _buf) \
+	do                           \
+	{                            \
+		_pix = (_buf)[0];        \
+		_buf += 1;               \
+	} while (0)
+
 #define WRITEFGBGIMAGE WriteFgBgImage8to8
 #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage8to8
 #define RLEDECOMPRESS RleDecompress8to8
@@ -263,19 +451,29 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix)
 #undef DESTWRITEPIXEL
 #undef DESTREADPIXEL
 #undef SRCREADPIXEL
-#undef DESTNEXTPIXEL
-#undef SRCNEXTPIXEL
 #undef WRITEFGBGIMAGE
 #undef WRITEFIRSTLINEFGBGIMAGE
 #undef RLEDECOMPRESS
 #undef RLEEXTRA
 #undef WHITE_PIXEL
+#undef PIXEL_SIZE
+#undef PIXEL
+#define PIXEL_SIZE 2
+#define PIXEL UINT16
 #define WHITE_PIXEL 0xFFFF
-#define DESTWRITEPIXEL(_buf, _pix) write_pixel_16(_buf, _pix)
+#define DESTWRITEPIXEL(_buf, _pix)  \
+	do                              \
+	{                               \
+		write_pixel_16(_buf, _pix); \
+		_buf += 2;                  \
+	} while (0)
 #define DESTREADPIXEL(_pix, _buf) _pix = ((UINT16*)(_buf))[0]
-#define SRCREADPIXEL(_pix, _buf) _pix = (_buf)[0] | ((_buf)[1] << 8)
-#define DESTNEXTPIXEL(_buf) _buf += 2
-#define SRCNEXTPIXEL(_buf) _buf += 2
+#define SRCREADPIXEL(_pix, _buf)             \
+	do                                       \
+	{                                        \
+		_pix = (_buf)[0] | ((_buf)[1] << 8); \
+		_buf += 2;                           \
+	} while (0)
 #define WRITEFGBGIMAGE WriteFgBgImage16to16
 #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage16to16
 #define RLEDECOMPRESS RleDecompress16to16
@@ -287,19 +485,30 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix)
 #undef DESTWRITEPIXEL
 #undef DESTREADPIXEL
 #undef SRCREADPIXEL
-#undef DESTNEXTPIXEL
-#undef SRCNEXTPIXEL
 #undef WRITEFGBGIMAGE
 #undef WRITEFIRSTLINEFGBGIMAGE
 #undef RLEDECOMPRESS
 #undef RLEEXTRA
 #undef WHITE_PIXEL
-#define WHITE_PIXEL 0xFFFFFF
-#define DESTWRITEPIXEL(_buf, _pix) write_pixel_24(_buf, _pix)
+#undef PIXEL_SIZE
+#undef PIXEL
+#define PIXEL_SIZE 3
+#define PIXEL UINT32
+#define WHITE_PIXEL 0xffffff
+#define DESTWRITEPIXEL(_buf, _pix)  \
+	do                              \
+	{                               \
+		write_pixel_24(_buf, _pix); \
+		_buf += 3;                  \
+	} while (0)
 #define DESTREADPIXEL(_pix, _buf) _pix = (_buf)[0] | ((_buf)[1] << 8) | ((_buf)[2] << 16)
-#define SRCREADPIXEL(_pix, _buf) _pix = (_buf)[0] | ((_buf)[1] << 8) | ((_buf)[2] << 16)
-#define DESTNEXTPIXEL(_buf) _buf += 3
-#define SRCNEXTPIXEL(_buf) _buf += 3
+#define SRCREADPIXEL(_pix, _buf)                                 \
+	do                                                           \
+	{                                                            \
+		_pix = (_buf)[0] | ((_buf)[1] << 8) | ((_buf)[2] << 16); \
+		_buf += 3;                                               \
+	} while (0)
+
 #define WRITEFGBGIMAGE WriteFgBgImage24to24
 #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage24to24
 #define RLEDECOMPRESS RleDecompress24to24
@@ -308,18 +517,32 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix)
 #define ENSURE_CAPACITY(_start, _end, _size) ensure_capacity(_start, _end, _size, 3)
 #include "include/bitmap.c"
 
+struct S_BITMAP_INTERLEAVED_CONTEXT
+{
+	BOOL Compressor;
+
+	UINT32 TempSize;
+	BYTE* TempBuffer;
+
+	wStream* bts;
+};
+
 BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* interleaved, const BYTE* pSrcData,
                             UINT32 SrcSize, UINT32 nSrcWidth, UINT32 nSrcHeight, UINT32 bpp,
                             BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst,
                             UINT32 nYDst, UINT32 nDstWidth, UINT32 nDstHeight,
                             const gdiPalette* palette)
 {
-	UINT32 scanline;
-	UINT32 SrcFormat;
-	UINT32 BufferSize;
+	UINT32 scanline = 0;
+	UINT32 SrcFormat = 0;
+	UINT32 BufferSize = 0;
 
 	if (!interleaved || !pSrcData || !pDstData)
+	{
+		WLog_ERR(TAG, "invalid arguments: interleaved=%p, pSrcData=%p, pDstData=%p", interleaved,
+		         pSrcData, pDstData);
 		return FALSE;
+	}
 
 	switch (bpp)
 	{
@@ -352,19 +575,26 @@ BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* interleaved, const BYTE*
 
 	if (BufferSize > interleaved->TempSize)
 	{
-		interleaved->TempBuffer = _aligned_realloc(interleaved->TempBuffer, BufferSize, 16);
+		interleaved->TempBuffer =
+		    _aligned_recalloc(interleaved->TempBuffer, BufferSize, sizeof(BYTE), 16);
 		interleaved->TempSize = BufferSize;
 	}
 
 	if (!interleaved->TempBuffer)
+	{
+		WLog_ERR(TAG, "interleaved->TempBuffer=%p", interleaved->TempBuffer);
 		return FALSE;
+	}
 
 	switch (bpp)
 	{
 		case 24:
 			if (!RleDecompress24to24(pSrcData, SrcSize, interleaved->TempBuffer, scanline,
 			                         nSrcWidth, nSrcHeight))
+			{
+				WLog_ERR(TAG, "RleDecompress24to24 failed");
 				return FALSE;
+			}
 
 			break;
 
@@ -372,24 +602,36 @@ BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* interleaved, const BYTE*
 		case 15:
 			if (!RleDecompress16to16(pSrcData, SrcSize, interleaved->TempBuffer, scanline,
 			                         nSrcWidth, nSrcHeight))
+			{
+				WLog_ERR(TAG, "RleDecompress16to16 failed");
 				return FALSE;
+			}
 
 			break;
 
 		case 8:
 			if (!RleDecompress8to8(pSrcData, SrcSize, interleaved->TempBuffer, scanline, nSrcWidth,
 			                       nSrcHeight))
+			{
+				WLog_ERR(TAG, "RleDecompress8to8 failed");
 				return FALSE;
+			}
 
 			break;
 
 		default:
+			WLog_ERR(TAG, "Invalid color depth %" PRIu32 "", bpp);
 			return FALSE;
 	}
 
-	return freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight,
-	                          interleaved->TempBuffer, SrcFormat, scanline, 0, 0, palette,
-	                          FREERDP_FLIP_VERTICAL);
+	if (!freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight,
+	                        interleaved->TempBuffer, SrcFormat, scanline, 0, 0, palette,
+	                        FREERDP_FLIP_VERTICAL))
+	{
+		WLog_ERR(TAG, "freerdp_image_copy failed");
+		return FALSE;
+	}
+	return TRUE;
 }
 
 BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* interleaved, BYTE* pDstData, UINT32* pDstSize,
@@ -397,10 +639,10 @@ BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* interleaved, BYTE* pDstDat
                           UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* palette,
                           UINT32 bpp)
 {
-	BOOL status;
-	wStream* s;
+	BOOL status = 0;
+	wStream* s = NULL;
 	UINT32 DstFormat = 0;
-	const size_t maxSize = 64 * 64 * 4;
+	const UINT32 maxSize = 64 * 64 * 4;
 
 	if (!interleaved || !pDstData || !pSrcData)
 		return FALSE;
@@ -442,7 +684,7 @@ BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* interleaved, BYTE* pDstDat
 	}
 
 	if (!freerdp_image_copy(interleaved->TempBuffer, DstFormat, 0, 0, 0, nWidth, nHeight, pSrcData,
-	                        SrcFormat, nSrcStep, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE))
+	                        SrcFormat, nSrcStep, nXSrc, nYSrc, palette, 0))
 		return FALSE;
 
 	s = Stream_New(pDstData, *pDstSize);
@@ -474,33 +716,29 @@ BOOL bitmap_interleaved_context_reset(BITMAP_INTERLEAVED_CONTEXT* interleaved)
 
 BITMAP_INTERLEAVED_CONTEXT* bitmap_interleaved_context_new(BOOL Compressor)
 {
-	BITMAP_INTERLEAVED_CONTEXT* interleaved;
-	interleaved = (BITMAP_INTERLEAVED_CONTEXT*)calloc(1, sizeof(BITMAP_INTERLEAVED_CONTEXT));
+	BITMAP_INTERLEAVED_CONTEXT* interleaved = NULL;
+	interleaved = (BITMAP_INTERLEAVED_CONTEXT*)_aligned_recalloc(
+	    NULL, 1, sizeof(BITMAP_INTERLEAVED_CONTEXT), 32);
 
 	if (interleaved)
 	{
 		interleaved->TempSize = 64 * 64 * 4;
-		interleaved->TempBuffer = _aligned_malloc(interleaved->TempSize, 16);
+		interleaved->TempBuffer = _aligned_malloc(interleaved->TempSize * sizeof(BYTE), 16);
 
 		if (!interleaved->TempBuffer)
-		{
-			free(interleaved);
-			WLog_ERR(TAG, "_aligned_malloc failed!");
-			return NULL;
-		}
+			goto fail;
 
 		interleaved->bts = Stream_New(NULL, interleaved->TempSize);
 
 		if (!interleaved->bts)
-		{
-			_aligned_free(interleaved->TempBuffer);
-			free(interleaved);
-			WLog_ERR(TAG, "Stream_New failed!");
-			return NULL;
-		}
+			goto fail;
 	}
 
 	return interleaved;
+
+fail:
+	bitmap_interleaved_context_free(interleaved);
+	return NULL;
 }
 
 void bitmap_interleaved_context_free(BITMAP_INTERLEAVED_CONTEXT* interleaved)
@@ -510,5 +748,5 @@ void bitmap_interleaved_context_free(BITMAP_INTERLEAVED_CONTEXT* interleaved)
 
 	_aligned_free(interleaved->TempBuffer);
 	Stream_Free(interleaved->bts, TRUE);
-	free(interleaved);
+	_aligned_free(interleaved);
 }
diff --git a/libfreerdp/codec/ncrush.c b/libfreerdp/codec/ncrush.c
index 3d6a216..199f1ed 100644
--- a/libfreerdp/codec/ncrush.c
+++ b/libfreerdp/codec/ncrush.c
@@ -1994,15 +1994,9 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
                       UINT32* pDstSize, UINT32 flags)
 {
 	UINT32 index;
-	UINT32 bits;
-	INT32 nbits;
-	const BYTE* SrcPtr;
-	const BYTE* SrcEnd;
-	UINT16 Mask;
 	BYTE Literal;
 	UINT32 IndexLEC;
 	UINT32 BitLength;
-	UINT32 MaskedBits;
 	UINT32 CopyOffset;
 	UINT32 CopyLength;
 	UINT32 OldCopyOffset;
@@ -2010,9 +2004,6 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 	UINT32 LengthOfMatch;
 	UINT32 CopyOffsetIndex;
 	UINT32 OffsetCacheIndex;
-	BYTE* HistoryPtr;
-	BYTE* HistoryBuffer;
-	BYTE* HistoryBufferEnd;
 	UINT32 CopyOffsetBits;
 	UINT32 CopyOffsetBase;
 	UINT32 LengthOfMatchBits;
@@ -2021,8 +2012,8 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 	if (ncrush->HistoryEndOffset != 65535)
 		return -1001;
 
-	HistoryBuffer = ncrush->HistoryBuffer;
-	HistoryBufferEnd = &HistoryBuffer[ncrush->HistoryEndOffset];
+	BYTE* HistoryBuffer = ncrush->HistoryBuffer;
+	const BYTE* HistoryBufferEnd = &HistoryBuffer[ncrush->HistoryEndOffset];
 
 	if (flags & PACKET_AT_FRONT)
 	{
@@ -2041,7 +2032,7 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 		ZeroMemory(&(ncrush->OffsetCache), sizeof(ncrush->OffsetCache));
 	}
 
-	HistoryPtr = ncrush->HistoryPtr;
+	BYTE* HistoryPtr = ncrush->HistoryPtr;
 
 	if (!(flags & PACKET_COMPRESSED))
 	{
@@ -2050,17 +2041,25 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 		return 1;
 	}
 
-	SrcEnd = &pSrcData[SrcSize];
-	nbits = 32;
-	bits = get_dword(pSrcData);
-	SrcPtr = pSrcData + 4;
+	if (SrcSize < 4)
+	{
+		WLog_ERR(TAG, "Input size short: SrcSize %" PRIu32 " < 4", SrcSize);
+		return -1;
+	}
+
+	const BYTE* SrcEnd = &pSrcData[SrcSize];
+	const BYTE* SrcPtr = pSrcData + 4;
 
+	INT32 nbits = 32;
+	UINT32 bits = get_dword(pSrcData);
 	while (1)
 	{
 		while (1)
 		{
-			Mask = get_word(&HuffTableMask[29]);
-			MaskedBits = bits & Mask;
+			const UINT16 Mask = get_word(&HuffTableMask[29]);
+			const UINT32 MaskedBits = bits & Mask;
+			if (MaskedBits >= ARRAYSIZE(HuffTableLEC))
+				return -1;
 			IndexLEC = HuffTableLEC[MaskedBits] & 0xFFF;
 			BitLength = HuffTableLEC[MaskedBits] >> 12;
 			bits >>= BitLength;
@@ -2096,8 +2095,10 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 				return -1004;
 
 			CopyOffset = ncrush->OffsetCache[OffsetCacheIndex];
-			Mask = get_word(&HuffTableMask[21]);
-			MaskedBits = bits & Mask;
+			const UINT16 Mask = get_word(&HuffTableMask[21]);
+			const UINT32 MaskedBits = bits & Mask;
+			if (MaskedBits > ARRAYSIZE(HuffTableLOM))
+				return -1;
 			LengthOfMatch = HuffTableLOM[MaskedBits] & 0xFFF;
 			BitLength = HuffTableLOM[MaskedBits] >> 12;
 			bits >>= BitLength;
@@ -2106,13 +2107,23 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 			if (!NCrushFetchBits(&SrcPtr, &SrcEnd, &nbits, &bits))
 				return -1;
 
+			if (LengthOfMatch >= ARRAYSIZE(LOMBitsLUT))
+				return -1;
+
 			LengthOfMatchBits = LOMBitsLUT[LengthOfMatch];
+
+			if (LengthOfMatch >= ARRAYSIZE(LOMBaseLUT))
+				return -1;
 			LengthOfMatchBase = LOMBaseLUT[LengthOfMatch];
 
 			if (LengthOfMatchBits)
 			{
-				Mask = get_word(&HuffTableMask[(2 * LengthOfMatchBits) + 3]);
-				MaskedBits = bits & Mask;
+				const size_t idx = (2ull * LengthOfMatchBits) + 3ull;
+				if (idx >= ARRAYSIZE(HuffTableMask))
+					return -1;
+
+				const UINT16 Mask = get_word(&HuffTableMask[idx]);
+				const UINT32 MaskedBits = bits & Mask;
 				bits >>= LengthOfMatchBits;
 				nbits -= LengthOfMatchBits;
 				LengthOfMatchBase += MaskedBits;
@@ -2127,15 +2138,28 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 		}
 		else
 		{
+			if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBitsLUT))
+				return -1;
+
 			CopyOffsetBits = CopyOffsetBitsLUT[CopyOffsetIndex];
+
+			if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBaseLUT))
+				return -1;
 			CopyOffsetBase = CopyOffsetBaseLUT[CopyOffsetIndex];
 			CopyOffset = CopyOffsetBase - 1;
 
 			if (CopyOffsetBits)
 			{
-				Mask = get_word(&HuffTableMask[(2 * CopyOffsetBits) + 3]);
-				MaskedBits = bits & Mask;
-				CopyOffset = CopyOffsetBase + MaskedBits - 1;
+				const size_t idx = (2ull * CopyOffsetBits) + 3ull;
+				if (idx >= ARRAYSIZE(HuffTableMask))
+					return -1;
+
+				const UINT16 Mask = get_word(&HuffTableMask[idx]);
+				const UINT32 MaskedBits = bits & Mask;
+				const UINT32 tmp = CopyOffsetBase + MaskedBits;
+				if (tmp < 1)
+					return -1;
+				CopyOffset = tmp - 1;
 				bits >>= CopyOffsetBits;
 				nbits -= CopyOffsetBits;
 
@@ -2143,8 +2167,11 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 					return -1;
 			}
 
-			Mask = get_word(&HuffTableMask[21]);
-			MaskedBits = bits & Mask;
+			const UINT16 Mask = get_word(&HuffTableMask[21]);
+			const UINT32 MaskedBits = bits & Mask;
+			if (MaskedBits >= ARRAYSIZE(HuffTableLOM))
+				return -1;
+
 			LengthOfMatch = HuffTableLOM[MaskedBits] & 0xFFF;
 			BitLength = HuffTableLOM[MaskedBits] >> 12;
 			bits >>= BitLength;
@@ -2153,13 +2180,23 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY
 			if (!NCrushFetchBits(&SrcPtr, &SrcEnd, &nbits, &bits))
 				return -1;
 
+			if (LengthOfMatch >= ARRAYSIZE(LOMBitsLUT))
+				return -1;
+
 			LengthOfMatchBits = LOMBitsLUT[LengthOfMatch];
+
+			if (LengthOfMatch >= ARRAYSIZE(LOMBaseLUT))
+				return -1;
 			LengthOfMatchBase = LOMBaseLUT[LengthOfMatch];
 
 			if (LengthOfMatchBits)
 			{
-				Mask = get_word(&HuffTableMask[(2 * LengthOfMatchBits) + 3]);
-				MaskedBits = bits & Mask;
+				const size_t idx = (2ull * LengthOfMatchBits) + 3ull;
+				if (idx >= ARRAYSIZE(HuffTableMask))
+					return -1;
+
+				const UINT16 Mask = get_word(&HuffTableMask[idx]);
+				const UINT32 MaskedBits = bits & Mask;
 				bits >>= LengthOfMatchBits;
 				nbits -= LengthOfMatchBits;
 				LengthOfMatchBase += MaskedBits;
@@ -2583,7 +2620,12 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE
 			}
 
 			IndexLEC = Literal;
+			if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
+				return -1;
 			BitLength = HuffLengthLEC[IndexLEC];
+
+			if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
+				return -1;
 			CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]);
 
 			if (BitLength > 15)
@@ -2666,9 +2708,18 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE
 					bits = CopyOffset;
 
 				CopyOffsetIndex = ncrush->HuffTableCopyOffset[bits + 2];
+
+				if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBitsLUT))
+					return -1;
+
 				CopyOffsetBits = CopyOffsetBitsLUT[CopyOffsetIndex];
 				IndexLEC = 257 + CopyOffsetIndex;
+				if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
+					return -1;
 				BitLength = HuffLengthLEC[IndexLEC];
+
+				if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
+					return -1;
 				CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]);
 
 				if (BitLength > 15)
@@ -2687,13 +2738,23 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE
 				else
 					IndexCO = ncrush->HuffTableLOM[MatchLength];
 
+				if (IndexCO >= ARRAYSIZE(HuffLengthLOM))
+					return -1;
 				BitLength = HuffLengthLOM[IndexCO];
+
+				if (IndexCO >= ARRAYSIZE(LOMBitsLUT))
+					return -1;
 				IndexLOM = LOMBitsLUT[IndexCO];
+
+				if (IndexCO >= ARRAYSIZE(HuffCodeLOM))
+					return -1;
 				NCrushWriteBits(&DstPtr, &accumulator, &offset, HuffCodeLOM[IndexCO], BitLength);
 				Mask = ((1 << IndexLOM) - 1);
 				MaskedBits = (MatchLength - 2) & Mask;
 				NCrushWriteBits(&DstPtr, &accumulator, &offset, MaskedBits, IndexLOM);
 
+				if (IndexCO >= ARRAYSIZE(LOMBaseLUT))
+					return -1;
 				if ((MaskedBits + LOMBaseLUT[IndexCO]) != MatchLength)
 					return -1010;
 			}
@@ -2701,7 +2762,11 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE
 			{
 				/* CopyOffset in OffsetCache */
 				IndexLEC = 289 + OffsetCacheIndex;
+				if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
+					return -1;
 				BitLength = HuffLengthLEC[IndexLEC];
+				if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
+					return -1;
 				CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]);
 
 				if (BitLength >= 15)
@@ -2714,13 +2779,24 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE
 				else
 					IndexCO = ncrush->HuffTableLOM[MatchLength];
 
+				if (IndexCO >= ARRAYSIZE(HuffLengthLOM))
+					return -1;
+
 				BitLength = HuffLengthLOM[IndexCO];
+
+				if (IndexCO >= ARRAYSIZE(LOMBitsLUT))
+					return -1;
 				IndexLOM = LOMBitsLUT[IndexCO];
+
+				if (IndexCO >= ARRAYSIZE(HuffCodeLOM))
+					return -1;
 				NCrushWriteBits(&DstPtr, &accumulator, &offset, HuffCodeLOM[IndexCO], BitLength);
 				Mask = ((1 << IndexLOM) - 1);
 				MaskedBits = (MatchLength - 2) & Mask;
 				NCrushWriteBits(&DstPtr, &accumulator, &offset, MaskedBits, IndexLOM);
 
+				if (IndexCO >= ARRAYSIZE(LOMBaseLUT))
+					return -1;
 				if ((MaskedBits + LOMBaseLUT[IndexCO]) != MatchLength)
 					return -1012;
 			}
@@ -2745,6 +2821,10 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE
 		Literal = *SrcPtr++;
 		HistoryPtr++;
 		IndexLEC = Literal;
+		if (IndexLEC >= ARRAYSIZE(HuffLengthLEC))
+			return -1;
+		if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC))
+			return -1;
 		BitLength = HuffLengthLEC[IndexLEC];
 		CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]);
 
@@ -2817,6 +2897,11 @@ static int ncrush_generate_tables(NCRUSH_CONTEXT* context)
 		else
 			i = context->HuffTableLOM[k];
 
+		if (i >= ARRAYSIZE(LOMBitsLUT))
+			return -1;
+		if (i >= ARRAYSIZE(LOMBaseLUT))
+			return -1;
+
 		if (((((1 << LOMBitsLUT[i]) - 1) & (k - 2)) + LOMBaseLUT[i]) != k)
 			return -1;
 	}
diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c
index dc3a978..5dd2646 100644
--- a/libfreerdp/codec/nsc.c
+++ b/libfreerdp/codec/nsc.c
@@ -29,6 +29,8 @@
 #include <string.h>
 
 #include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/stream.h>
 
 #include <freerdp/codec/nsc.h>
 #include <freerdp/codec/color.h>
@@ -38,6 +40,12 @@
 
 #include "nsc_sse2.h"
 
+#if !defined(Stream_CheckAndLogRequiredLengthWLog)
+#define Stream_CheckAndLogRequiredLengthWLog(log, s, len)                                     \
+	Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \
+	                                       __FILE__, __LINE__)
+#endif
+
 #ifndef NSC_INIT_SIMD
 #define NSC_INIT_SIMD(_nsc_context) \
 	do                              \
@@ -87,8 +95,8 @@ static BOOL nsc_decode(NSC_CONTEXT* context)
 		for (x = 0; x < context->width; x++)
 		{
 			INT16 y_val = (INT16)*yplane;
-			INT16 co_val = (INT16)(INT8)(*coplane << shift);
-			INT16 cg_val = (INT16)(INT8)(*cgplane << shift);
+			INT16 co_val = (INT16)(INT8)(((INT16)*coplane) << shift);
+			INT16 cg_val = (INT16)(INT8)(((INT16)*cgplane) << shift);
 			INT16 r_val = y_val + co_val - cg_val;
 			INT16 g_val = y_val + cg_val;
 			INT16 b_val = y_val - co_val - cg_val;
@@ -111,12 +119,17 @@ static BOOL nsc_decode(NSC_CONTEXT* context)
 	return TRUE;
 }
 
-static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalSize)
+static BOOL nsc_rle_decode(const BYTE* in, size_t inSize, BYTE* out, UINT32 outSize,
+                           UINT32 originalSize)
 {
 	UINT32 left = originalSize;
 
 	while (left > 4)
 	{
+		if (inSize < 1)
+			return FALSE;
+		inSize--;
+
 		const BYTE value = *in++;
 		UINT32 len = 0;
 
@@ -129,17 +142,26 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS
 			*out++ = value;
 			left--;
 		}
+		else if (inSize < 1)
+			return FALSE;
 		else if (value == *in)
 		{
+			inSize--;
 			in++;
 
-			if (*in < 0xFF)
+			if (inSize < 1)
+				return FALSE;
+			else if (*in < 0xFF)
 			{
+				inSize--;
 				len = (UINT32)*in++;
 				len += 2;
 			}
 			else
 			{
+				if (inSize < 5)
+					return FALSE;
+				inSize -= 5;
 				in++;
 				len = ((UINT32)(*in++));
 				len |= ((UINT32)(*in++)) << 8U;
@@ -147,7 +169,7 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS
 				len |= ((UINT32)(*in++)) << 24U;
 			}
 
-			if (outSize < len)
+			if ((outSize < len) || (left < len))
 				return FALSE;
 
 			outSize -= len;
@@ -169,26 +191,28 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS
 	if ((outSize < 4) || (left < 4))
 		return FALSE;
 
+	if (inSize < 4)
+		return FALSE;
 	memcpy(out, in, 4);
 	return TRUE;
 }
 
 static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 {
-	UINT16 i;
-	BYTE* rle;
-	UINT32 planeSize;
-	UINT32 originalSize;
-
 	if (!context)
 		return FALSE;
 
-	rle = context->Planes;
+	const BYTE* rle = context->Planes;
+	size_t rleSize = context->PlanesSize;
+	WINPR_ASSERT(rle);
 
-	for (i = 0; i < 4; i++)
+	for (size_t i = 0; i < 4; i++)
 	{
-		originalSize = context->OrgByteCount[i];
-		planeSize = context->PlaneByteCount[i];
+		const UINT32 originalSize = context->OrgByteCount[i];
+		const UINT32 planeSize = context->PlaneByteCount[i];
+
+		if (rleSize < planeSize)
+			return FALSE;
 
 		if (planeSize == 0)
 		{
@@ -199,7 +223,7 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 		}
 		else if (planeSize < originalSize)
 		{
-			if (!nsc_rle_decode(rle, context->priv->PlaneBuffers[i],
+			if (!nsc_rle_decode(rle, rleSize, context->priv->PlaneBuffers[i],
 			                    context->priv->PlaneBuffersLength, originalSize))
 				return FALSE;
 		}
@@ -208,6 +232,9 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 			if (context->priv->PlaneBuffersLength < originalSize)
 				return FALSE;
 
+			if (rleSize < originalSize)
+				return FALSE;
+
 			CopyMemory(context->priv->PlaneBuffers[i], rle, originalSize);
 		}
 
@@ -219,64 +246,71 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
 
 static BOOL nsc_stream_initialize(NSC_CONTEXT* context, wStream* s)
 {
-	int i;
-
-	if (Stream_GetRemainingLength(s) < 20)
+	WINPR_ASSERT(context);
+	WINPR_ASSERT(context->priv);
+	if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 20))
 		return FALSE;
 
-	for (i = 0; i < 4; i++)
+	size_t total = 0;
+	for (size_t i = 0; i < 4; i++)
+	{
 		Stream_Read_UINT32(s, context->PlaneByteCount[i]);
+		total += context->PlaneByteCount[i];
+	}
 
 	Stream_Read_UINT8(s, context->ColorLossLevel);         /* ColorLossLevel (1 byte) */
+	if ((context->ColorLossLevel < 1) || (context->ColorLossLevel > 7))
+	{
+		WLog_Print(context->priv->log, WLOG_ERROR,
+		           "ColorLossLevel=%" PRIu8 " out of range, must be [1,7] inclusive",
+		           context->ColorLossLevel);
+		return FALSE;
+	}
 	Stream_Read_UINT8(s, context->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */
 	Stream_Seek(s, 2);                                     /* Reserved (2 bytes) */
 	context->Planes = Stream_Pointer(s);
-	return TRUE;
+	context->PlanesSize = total;
+	return Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, total);
 }
 
 static BOOL nsc_context_initialize(NSC_CONTEXT* context, wStream* s)
 {
-	int i;
-	UINT32 length;
-	UINT32 tempWidth;
-	UINT32 tempHeight;
-
 	if (!nsc_stream_initialize(context, s))
 		return FALSE;
 
-	length = context->width * context->height * 4;
+	const size_t blength = context->width * context->height * 4ull;
 
 	if (!context->BitmapData)
 	{
-		context->BitmapData = calloc(1, length + 16);
+		context->BitmapData = calloc(1, blength + 16);
 
 		if (!context->BitmapData)
 			return FALSE;
 
-		context->BitmapDataLength = length;
+		context->BitmapDataLength = blength;
 	}
-	else if (length > context->BitmapDataLength)
+	else if (blength > context->BitmapDataLength)
 	{
 		void* tmp;
-		tmp = realloc(context->BitmapData, length + 16);
+		tmp = realloc(context->BitmapData, blength + 16);
 
 		if (!tmp)
 			return FALSE;
 
 		context->BitmapData = tmp;
-		context->BitmapDataLength = length;
+		context->BitmapDataLength = blength;
 	}
 
-	tempWidth = ROUND_UP_TO(context->width, 8);
-	tempHeight = ROUND_UP_TO(context->height, 2);
+	const UINT32 tempWidth = ROUND_UP_TO(context->width, 8);
+	const UINT32 tempHeight = ROUND_UP_TO(context->height, 2);
 	/* The maximum length a decoded plane can reach in all cases */
-	length = tempWidth * tempHeight;
+	const size_t plength = 1ull * tempWidth * tempHeight;
 
-	if (length > context->priv->PlaneBuffersLength)
+	if (plength > context->priv->PlaneBuffersLength)
 	{
-		for (i = 0; i < 4; i++)
+		for (size_t i = 0; i < 4; i++)
 		{
-			void* tmp = (BYTE*)realloc(context->priv->PlaneBuffers[i], length);
+			void* tmp = (BYTE*)realloc(context->priv->PlaneBuffers[i], plength);
 
 			if (!tmp)
 				return FALSE;
@@ -284,13 +318,11 @@ static BOOL nsc_context_initialize(NSC_CONTEXT* context, wStream* s)
 			context->priv->PlaneBuffers[i] = tmp;
 		}
 
-		context->priv->PlaneBuffersLength = length;
+		context->priv->PlaneBuffersLength = plength;
 	}
 
-	for (i = 0; i < 4; i++)
-	{
+	for (size_t i = 0; i < 4; i++)
 		context->OrgByteCount[i] = context->width * context->height;
-	}
 
 	if (context->ChromaSubsamplingLevel)
 	{
diff --git a/libfreerdp/codec/nsc_encode.c b/libfreerdp/codec/nsc_encode.c
index fd9626a..477318c 100644
--- a/libfreerdp/codec/nsc_encode.c
+++ b/libfreerdp/codec/nsc_encode.c
@@ -119,6 +119,8 @@ static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UI
 	UINT16 rw;
 	BYTE ccl;
 	const BYTE* src;
+	const UINT32* src_32;
+	const UINT16* src_16;
 	BYTE* yplane = NULL;
 	BYTE* coplane = NULL;
 	BYTE* cgplane = NULL;
@@ -140,69 +142,85 @@ static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UI
 		coplane = context->priv->PlaneBuffers[1] + y * rw;
 		cgplane = context->priv->PlaneBuffers[2] + y * rw;
 		aplane = context->priv->PlaneBuffers[3] + y * context->width;
+		src_32 = (UINT32*)src;
+		src_16 = (UINT16*)src;
 
 		for (x = 0; x < context->width; x++)
 		{
 			switch (context->format)
 			{
 				case PIXEL_FORMAT_BGRX32:
-					b_val = *src++;
-					g_val = *src++;
-					r_val = *src++;
-					src++;
+					b_val = (INT16)(*src_32 & 0xFF);
+					g_val = (INT16)((*src_32 >> 8) & 0xFF);
+					r_val = (INT16)((*src_32 >> 16) & 0xFF);
 					a_val = 0xFF;
+					src_32++;
 					break;
 
 				case PIXEL_FORMAT_BGRA32:
-					b_val = *src++;
-					g_val = *src++;
-					r_val = *src++;
-					a_val = *src++;
+					b_val = (INT16)(*src_32 & 0xFF);
+					g_val = (INT16)((*src_32 >> 8) & 0xFF);
+					r_val = (INT16)((*src_32 >> 16) & 0xFF);
+					a_val = (INT16)((*src_32 >> 24) & 0xFF);
+					src_32++;
 					break;
 
 				case PIXEL_FORMAT_RGBX32:
-					r_val = *src++;
-					g_val = *src++;
-					b_val = *src++;
-					src++;
+					r_val = (INT16)(*src_32 & 0xFF);
+					g_val = (INT16)((*src_32 >> 8) & 0xFF);
+					b_val = (INT16)((*src_32 >> 16) & 0xFF);
 					a_val = 0xFF;
+					src_32++;
 					break;
 
 				case PIXEL_FORMAT_RGBA32:
-					r_val = *src++;
-					g_val = *src++;
-					b_val = *src++;
-					a_val = *src++;
+					r_val = (INT16)(*src_32 & 0xFF);
+					g_val = (INT16)((*src_32 >> 8) & 0xFF);
+					b_val = (INT16)((*src_32 >> 16) & 0xFF);
+					a_val = (INT16)((*src_32 >> 24) & 0xFF);
+					src_32++;
 					break;
 
 				case PIXEL_FORMAT_BGR24:
+#ifdef __LITTLE_ENDIAN__
 					b_val = *src++;
 					g_val = *src++;
 					r_val = *src++;
+#else
+					r_val = *src++;
+					g_val = *src++;
+					b_val = *src++;
+#endif
 					a_val = 0xFF;
 					break;
 
 				case PIXEL_FORMAT_RGB24:
+#ifdef __LITTLE_ENDIAN__
 					r_val = *src++;
 					g_val = *src++;
 					b_val = *src++;
+#else
+					b_val = *src++;
+					g_val = *src++;
+					r_val = *src++;
+#endif
 					a_val = 0xFF;
 					break;
 
 				case PIXEL_FORMAT_BGR16:
-					b_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
-					g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
-					r_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
+					b_val = (INT16)((*src_16) & 0x1F);
+					g_val = (INT16)((*src_16 >> 5) & 0x3F);
+					r_val = (INT16)((*src_16 >> 11) & 0x1F);
 					a_val = 0xFF;
-					src += 2;
+					src_16++;
 					break;
 
 				case PIXEL_FORMAT_RGB16:
-					r_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
-					g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
-					b_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
+					r_val = (INT16)((*src_16) & 0x1F);
+					g_val = (INT16)((*src_16 >> 5) & 0x3F);
+					b_val = (INT16)((*src_16 >> 11) & 0x1F);
 					a_val = 0xFF;
-					src += 2;
+					src_16++;
 					break;
 
 				case PIXEL_FORMAT_A4:
@@ -210,17 +228,17 @@ static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UI
 					int shift;
 					BYTE idx;
 					shift = (7 - (x % 8));
-					idx = ((*src) >> shift) & 1;
-					idx |= (((*(src + 1)) >> shift) & 1) << 1;
-					idx |= (((*(src + 2)) >> shift) & 1) << 2;
-					idx |= (((*(src + 3)) >> shift) & 1) << 3;
+					idx = (BYTE)(((*src_32 & 0xFF) >> shift) & 1);
+					idx |= (BYTE)(((((*src_32 >> 8) & 0xFF) >> shift) & 1) << 1);
+					idx |= (BYTE)(((((*src_32 >> 16) & 0xFF) >> shift) & 1) << 2);
+					idx |= (BYTE)(((((*src_32 >> 24) & 0xFF) >> shift) & 1) << 3);
 					idx *= 3;
 					r_val = (INT16)context->palette[idx];
 					g_val = (INT16)context->palette[idx + 1];
 					b_val = (INT16)context->palette[idx + 2];
 
 					if (shift == 0)
-						src += 4;
+						src_32++;
 				}
 
 					a_val = 0xFF;
diff --git a/libfreerdp/codec/nsc_types.h b/libfreerdp/codec/nsc_types.h
index 14c1a94..5a6506b 100644
--- a/libfreerdp/codec/nsc_types.h
+++ b/libfreerdp/codec/nsc_types.h
@@ -61,6 +61,7 @@ struct _NSC_CONTEXT
 	UINT32 BitmapDataLength;
 
 	BYTE* Planes;
+	size_t PlanesSize;
 	UINT32 PlaneByteCount[4];
 	UINT32 ColorLossLevel;
 	UINT32 ChromaSubsamplingLevel;
diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c
index 12b9f6d..fe27011 100644
--- a/libfreerdp/codec/planar.c
+++ b/libfreerdp/codec/planar.c
@@ -33,7 +33,8 @@
 
 #define TAG FREERDP_TAG("codec")
 
-#define ALIGN(val, align) ((val) % (align) == 0) ? (val) : ((val) + (align) - (val) % (align))
+#define PLANAR_ALIGN(val, align) \
+	((val) % (align) == 0) ? (val) : ((val) + (align) - (val) % (align))
 
 static INLINE UINT32 planar_invert_format(BITMAP_PLANAR_CONTEXT* planar, BOOL alpha,
                                           UINT32 DstFormat)
@@ -612,11 +613,20 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
 	const UINT32 h = MIN(nSrcHeight, nDstHeight);
 	const primitives_t* prims = primitives_get();
 
+	WINPR_ASSERT(planar);
+	WINPR_ASSERT(prims);
+
 	if (nDstStep <= 0)
 		nDstStep = nDstWidth * GetBytesPerPixel(DstFormat);
 
 	srcp = pSrcData;
 
+	if (!pSrcData)
+	{
+		WLog_ERR(TAG, "Invalid argument pSrcData=NULL");
+		return FALSE;
+	}
+
 	if (!pDstData)
 	{
 		WLog_ERR(TAG, "Invalid argument pDstData=NULL");
@@ -679,6 +689,13 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
 		rawHeights[3] = nSrcHeight;
 	}
 
+	const size_t diff = srcp - pSrcData;
+	if (SrcSize < diff)
+	{
+		WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff);
+		return FALSE;
+	}
+
 	if (!rle) /* RAW */
 	{
 		UINT32 base = planeSize * 3;
@@ -687,8 +704,12 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
 
 		if (alpha)
 		{
-			if ((SrcSize - (srcp - pSrcData)) < (planeSize + base))
+			if ((SrcSize - diff) < (planeSize + base))
+			{
+				WLog_ERR(TAG, "Alpha plane size mismatch %" PRIuz " < %" PRIu32, SrcSize - diff,
+				         (planeSize + base));
 				return FALSE;
+			}
 
 			planes[3] = srcp;                    /* AlphaPlane */
 			planes[0] = planes[3] + rawSizes[3]; /* LumaOrRedPlane */
@@ -700,8 +721,11 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
 		}
 		else
 		{
-			if ((SrcSize - (srcp - pSrcData)) < base)
+			if ((SrcSize - diff) < base)
+			{
+				WLog_ERR(TAG, "plane size mismatch %" PRIu32 " < %" PRIu32, SrcSize - diff, base);
 				return FALSE;
+			}
 
 			planes[0] = srcp;                    /* LumaOrRedPlane */
 			planes[1] = planes[0] + rawSizes[0]; /* OrangeChromaOrGreenPlane */
@@ -716,8 +740,8 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
 		if (alpha)
 		{
 			planes[3] = srcp;
-			rleSizes[3] = planar_skip_plane_rle(planes[3], SrcSize - (planes[3] - pSrcData),
-			                                    rawWidths[3], rawHeights[3]); /* AlphaPlane */
+			rleSizes[3] = planar_skip_plane_rle(planes[3], SrcSize - diff, rawWidths[3],
+			                                    rawHeights[3]); /* AlphaPlane */
 
 			if (rleSizes[3] < 0)
 				return FALSE;
@@ -727,22 +751,41 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT
 		else
 			planes[0] = srcp;
 
-		rleSizes[0] = planar_skip_plane_rle(planes[0], SrcSize - (planes[0] - pSrcData),
-		                                    rawWidths[0], rawHeights[0]); /* RedPlane */
+		const size_t diff0 = (planes[0] - pSrcData);
+		if (SrcSize < diff0)
+		{
+			WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff0);
+			return FALSE;
+		}
+		rleSizes[0] = planar_skip_plane_rle(planes[0], SrcSize - diff0, rawWidths[0],
+		                                    rawHeights[0]); /* RedPlane */
 
 		if (rleSizes[0] < 0)
 			return FALSE;
 
 		planes[1] = planes[0] + rleSizes[0];
-		rleSizes[1] = planar_skip_plane_rle(planes[1], SrcSize - (planes[1] - pSrcData),
-		                                    rawWidths[1], rawHeights[1]); /* GreenPlane */
+
+		const size_t diff1 = (planes[1] - pSrcData);
+		if (SrcSize < diff1)
+		{
+			WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff1);
+			return FALSE;
+		}
+		rleSizes[1] = planar_skip_plane_rle(planes[1], SrcSize - diff1, rawWidths[1],
+		                                    rawHeights[1]); /* GreenPlane */
 
 		if (rleSizes[1] < 1)
 			return FALSE;
 
 		planes[2] = planes[1] + rleSizes[1];
-		rleSizes[2] = planar_skip_plane_rle(planes[2], SrcSize - (planes[2] - pSrcData),
-		                                    rawWidths[2], rawHeights[2]); /* BluePlane */
+		const size_t diff2 = (planes[2] - pSrcData);
+		if (SrcSize < diff2)
+		{
+			WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff);
+			return FALSE;
+		}
+		rleSizes[2] = planar_skip_plane_rle(planes[2], SrcSize - diff2, rawWidths[2],
+		                                    rawHeights[2]); /* BluePlane */
 
 		if (rleSizes[2] < 1)
 			return FALSE;
@@ -1484,9 +1527,15 @@ BOOL freerdp_bitmap_planar_context_reset(BITMAP_PLANAR_CONTEXT* context, UINT32
 		return FALSE;
 
 	context->bgr = FALSE;
-	context->maxWidth = ALIGN(width, 4);
-	context->maxHeight = ALIGN(height, 4);
-	context->maxPlaneSize = context->maxWidth * context->maxHeight;
+	context->maxWidth = PLANAR_ALIGN(width, 4);
+	context->maxHeight = PLANAR_ALIGN(height, 4);
+	const UINT64 tmp = (UINT64)context->maxWidth * context->maxHeight;
+	if (tmp > UINT32_MAX)
+		return FALSE;
+	context->maxPlaneSize = tmp;
+
+	if (context->maxWidth > UINT32_MAX / 4)
+		return FALSE;
 	context->nTempStep = context->maxWidth * 4;
 	free(context->planesBuffer);
 	free(context->pTempData);
diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c
index e95db2d..8894b35 100644
--- a/libfreerdp/codec/progressive.c
+++ b/libfreerdp/codec/progressive.c
@@ -415,7 +415,7 @@ static INLINE BOOL progressive_tile_allocate(RFX_PROGRESSIVE_TILE* tile)
 	tile->stride = 4 * tile->width;
 
 	{
-		size_t dataLen = tile->stride * tile->height * 1ULL;
+		size_t dataLen = 1ull * tile->stride * tile->height;
 		tile->data = (BYTE*)_aligned_malloc(dataLen, 16);
 	}
 
@@ -1796,13 +1796,13 @@ static INLINE int progressive_process_tiles(PROGRESSIVE_CONTEXT* progressive, wS
 	for (index = 0; index < region->numTiles; index++)
 	{
 		RFX_PROGRESSIVE_TILE* tile = region->tiles[index];
-		params[index].progressive = progressive;
-		params[index].region = region;
-		params[index].context = context;
-		params[index].tile = tile;
 
 		if (progressive->rfx_context->priv->UseThreads)
 		{
+			params[index].progressive = progressive;
+			params[index].region = region;
+			params[index].context = context;
+			params[index].tile = tile;
 			if (!(work_objects[index] = CreateThreadpoolWork(
 			          progressive_process_tiles_tile_work_callback, (void*)&params[index],
 			          &progressive->rfx_context->priv->ThreadPoolEnv)))
@@ -1817,7 +1817,10 @@ static INLINE int progressive_process_tiles(PROGRESSIVE_CONTEXT* progressive, wS
 		}
 		else
 		{
-			progressive_process_tiles_tile_work_callback(0, &params[index], 0);
+			PROGRESSIVE_TILE_PROCESS_WORK_PARAM param = {
+				.progressive = progressive, .region = region, .context = context, .tile = tile
+			};
+			progressive_process_tiles_tile_work_callback(0, &param, 0);
 		}
 
 		if (status < 0)
@@ -2422,11 +2425,17 @@ INT32 progressive_decompress_ex(PROGRESSIVE_CONTEXT* progressive, const BYTE* pS
 		for (j = 0; j < nbUpdateRects; j++)
 		{
 			const RECTANGLE_16* rect = &updateRects[j];
-			const UINT32 nXSrc = rect->left - (nXDst + tile->x);
-			const UINT32 nYSrc = rect->top - (nYDst + tile->y);
+			if (rect->left < updateRect.left)
+				goto fail;
+			const UINT32 nXSrc = rect->left - updateRect.left;
+			const UINT32 nYSrc = rect->top - updateRect.top;
 			const UINT32 width = rect->right - rect->left;
 			const UINT32 height = rect->bottom - rect->top;
 
+			if (rect->left + width > surface->width)
+				goto fail;
+			if (rect->top + height > surface->height)
+				goto fail;
 			if (!freerdp_image_copy(pDstData, DstFormat, nDstStep, rect->left, rect->top, width,
 			                        height, tile->data, progressive->format, tile->stride, nXSrc,
 			                        nYSrc, NULL, FREERDP_FLIP_NONE))
diff --git a/libfreerdp/codec/rfx.c b/libfreerdp/codec/rfx.c
index b41c157..ccbc5af 100644
--- a/libfreerdp/codec/rfx.c
+++ b/libfreerdp/codec/rfx.c
@@ -936,6 +936,27 @@ static BOOL rfx_process_message_tileset(RFX_CONTEXT* context, RFX_MESSAGE* messa
 		Stream_Read_UINT8(&sub, tile->quantIdxY);  /* quantIdxY (1 byte) */
 		Stream_Read_UINT8(&sub, tile->quantIdxCb); /* quantIdxCb (1 byte) */
 		Stream_Read_UINT8(&sub, tile->quantIdxCr); /* quantIdxCr (1 byte) */
+		if (tile->quantIdxY >= context->numQuant)
+		{
+			WLog_Print(context->priv->log, WLOG_ERROR, "quantIdxY %" PRIu8 " >= numQuant %" PRIu8,
+			           tile->quantIdxY, context->numQuant);
+			rc = FALSE;
+			break;
+		}
+		else if (tile->quantIdxCb >= context->numQuant)
+		{
+			WLog_Print(context->priv->log, WLOG_ERROR, "quantIdxCb %" PRIu8 " >= numQuant %" PRIu8,
+			           tile->quantIdxCb, context->numQuant);
+			rc = FALSE;
+			break;
+		}
+		else if (tile->quantIdxCr >= context->numQuant)
+		{
+			WLog_Print(context->priv->log, WLOG_ERROR, "quantIdxCr %" PRIu8 " >= numQuant %" PRIu8,
+			           tile->quantIdxCr, context->numQuant);
+			rc = FALSE;
+			break;
+		}
 		Stream_Read_UINT16(&sub, tile->xIdx);      /* xIdx (2 bytes) */
 		Stream_Read_UINT16(&sub, tile->yIdx);      /* yIdx (2 bytes) */
 		Stream_Read_UINT16(&sub, tile->YLen);      /* YLen (2 bytes) */
@@ -1109,8 +1130,18 @@ BOOL rfx_process_message(RFX_CONTEXT* context, const BYTE* data, UINT32 length,
 			}
 		}
 
-		Stream_StaticInit(&subStream, Stream_Pointer(s), blockLen - (6 + extraBlockLen));
-		Stream_Seek(s, blockLen - (6 + extraBlockLen));
+		const size_t blockLenNoHeader = blockLen - 6;
+		if (blockLenNoHeader < extraBlockLen)
+		{
+			WLog_Print(context->priv->log, WLOG_ERROR,
+			           "blockLen too small(%" PRIu32 "), must be >= 6 + %" PRIu16, blockLen,
+			           extraBlockLen);
+			return FALSE;
+		}
+
+		const size_t subStreamLen = blockLenNoHeader - extraBlockLen;
+		Stream_StaticInit(&subStream, Stream_Pointer(s), subStreamLen);
+		Stream_Seek(s, subStreamLen);
 
 		switch (blockType)
 		{
@@ -1231,6 +1262,11 @@ BOOL rfx_process_message(RFX_CONTEXT* context, const BYTE* data, UINT32 length,
 		region16_uninit(&clippingRects);
 		return TRUE;
 	}
+	else
+	{
+		rfx_message_free(context, message);
+		context->currentMessage.freeArray = TRUE;
+	}
 
 	WLog_ERR(TAG, "%s failed", __FUNCTION__);
 	return FALSE;
diff --git a/libfreerdp/codec/rfx_encode.c b/libfreerdp/codec/rfx_encode.c
index b4f3936..355f743 100644
--- a/libfreerdp/codec/rfx_encode.c
+++ b/libfreerdp/codec/rfx_encode.c
@@ -49,6 +49,8 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 	int x_exceed;
 	int y_exceed;
 	const BYTE* src;
+	const UINT32* src_32;
+	const UINT16* src_16;
 	INT16 r, g, b;
 	INT16 *r_last, *g_last, *b_last;
 	x_exceed = 64 - width;
@@ -57,6 +59,8 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 	for (y = 0; y < height; y++)
 	{
 		src = rgb_data + y * rowstride;
+		src_32 = (UINT32*)src;
+		src_16 = (UINT16*)src;
 
 		switch (pixel_format)
 		{
@@ -64,10 +68,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_BGRA32:
 				for (x = 0; x < width; x++)
 				{
-					*b_buf++ = (INT16)(*src++);
-					*g_buf++ = (INT16)(*src++);
-					*r_buf++ = (INT16)(*src++);
-					src++;
+					*b_buf++ = (INT16)(*src_32 & 0xFF);
+					*g_buf++ = (INT16)((*src_32 >> 8) & 0xFF);
+					*r_buf++ = (INT16)((*src_32 >> 16) & 0xFF);
+					src_32++;
 				}
 
 				break;
@@ -76,10 +80,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_ABGR32:
 				for (x = 0; x < width; x++)
 				{
-					src++;
-					*b_buf++ = (INT16)(*src++);
-					*g_buf++ = (INT16)(*src++);
-					*r_buf++ = (INT16)(*src++);
+					*b_buf++ = (INT16)((*src_32 >> 8) & 0xFF);
+					*g_buf++ = (INT16)((*src_32 >> 16) & 0xFF);
+					*r_buf++ = (INT16)((*src_32 >> 24) & 0xFF);
+					src_32++;
 				}
 
 				break;
@@ -88,10 +92,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_RGBA32:
 				for (x = 0; x < width; x++)
 				{
-					*r_buf++ = (INT16)(*src++);
-					*g_buf++ = (INT16)(*src++);
-					*b_buf++ = (INT16)(*src++);
-					src++;
+					*r_buf++ = (INT16)(*src_32 & 0xFF);
+					*g_buf++ = (INT16)((*src_32 >> 8) & 0xFF);
+					*b_buf++ = (INT16)((*src_32 >> 16) & 0xFF);
+					src_32++;
 				}
 
 				break;
@@ -100,10 +104,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_ARGB32:
 				for (x = 0; x < width; x++)
 				{
-					src++;
-					*r_buf++ = (INT16)(*src++);
-					*g_buf++ = (INT16)(*src++);
-					*b_buf++ = (INT16)(*src++);
+					*r_buf++ = (INT16)((*src_32 >> 8) & 0xFF);
+					*g_buf++ = (INT16)((*src_32 >> 16) & 0xFF);
+					*b_buf++ = (INT16)((*src_32 >> 24) & 0xFF);
+					src_32++;
 				}
 
 				break;
@@ -111,9 +115,15 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_BGR24:
 				for (x = 0; x < width; x++)
 				{
+#ifdef __LITTLE_ENDIAN__
 					*b_buf++ = (INT16)(*src++);
 					*g_buf++ = (INT16)(*src++);
 					*r_buf++ = (INT16)(*src++);
+#else
+					*r_buf++ = (INT16)(*src++);
+					*g_buf++ = (INT16)(*src++);
+					*b_buf++ = (INT16)(*src++);
+#endif
 				}
 
 				break;
@@ -121,9 +131,15 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_RGB24:
 				for (x = 0; x < width; x++)
 				{
+#ifdef __LITTLE_ENDIAN__
 					*r_buf++ = (INT16)(*src++);
 					*g_buf++ = (INT16)(*src++);
 					*b_buf++ = (INT16)(*src++);
+#else
+					*b_buf++ = (INT16)(*src++);
+					*g_buf++ = (INT16)(*src++);
+					*r_buf++ = (INT16)(*src++);
+#endif
 				}
 
 				break;
@@ -131,10 +147,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_BGR16:
 				for (x = 0; x < width; x++)
 				{
-					*b_buf++ = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
-					*g_buf++ = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
-					*r_buf++ = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
-					src += 2;
+					*b_buf++ = (INT16)((*src_16) & 0x1F);
+					*g_buf++ = (INT16)((*src_16 >> 5) & 0x3F);
+					*r_buf++ = (INT16)((*src_16 >> 11) & 0x1F);
+					src_16++;
 				}
 
 				break;
@@ -142,10 +158,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 			case PIXEL_FORMAT_RGB16:
 				for (x = 0; x < width; x++)
 				{
-					*r_buf++ = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
-					*g_buf++ = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
-					*b_buf++ = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
-					src += 2;
+					*r_buf++ = (INT16)((*src_16 & 0x1F));
+					*g_buf++ = (INT16)((*src_16 >> 5) & 0x3F);
+					*b_buf++ = (INT16)((*src_16 >> 11) & 0x1F);
+					src_16++;
 				}
 
 				break;
@@ -159,17 +175,17 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i
 					int shift;
 					BYTE idx;
 					shift = (7 - (x % 8));
-					idx = ((*src) >> shift) & 1;
-					idx |= (((*(src + 1)) >> shift) & 1) << 1;
-					idx |= (((*(src + 2)) >> shift) & 1) << 2;
-					idx |= (((*(src + 3)) >> shift) & 1) << 3;
+					idx = (BYTE)(((*src_32 & 0xFF) >> shift) & 1);
+					idx |= (BYTE)(((((*src_32 >> 8) & 0xFF) >> shift) & 1) << 1);
+					idx |= (BYTE)(((((*src_32 >> 16) & 0xFF) >> shift) & 1) << 2);
+					idx |= (BYTE)(((((*src_32 >> 24) & 0xFF) >> shift) & 1) << 3);
 					idx *= 3;
 					*r_buf++ = (INT16)palette[idx];
 					*g_buf++ = (INT16)palette[idx + 1];
 					*b_buf++ = (INT16)palette[idx + 2];
 
 					if (shift == 0)
-						src += 4;
+						src_32++;
 				}
 
 				break;
diff --git a/libfreerdp/codec/xcrush.c b/libfreerdp/codec/xcrush.c
index 350801d..56bf649 100644
--- a/libfreerdp/codec/xcrush.c
+++ b/libfreerdp/codec/xcrush.c
@@ -674,7 +674,7 @@ static int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, BYTE* OutputBuffer, UI
 	if (&OutputBuffer[2] >= &OutputBuffer[OutputSize])
 		return -6001; /* error */
 
-	*((UINT16*)OutputBuffer) = MatchCount;
+	Data_Write_UINT16(OutputBuffer, MatchCount);
 	MatchDetails = (RDP61_MATCH_DETAILS*)&OutputBuffer[2];
 	Literals = (BYTE*)&MatchDetails[MatchCount];
 
@@ -683,12 +683,12 @@ static int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, BYTE* OutputBuffer, UI
 
 	for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++)
 	{
-		MatchDetails[MatchIndex].MatchLength =
-		    (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchLength);
-		MatchDetails[MatchIndex].MatchOutputOffset =
-		    (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset);
-		MatchDetails[MatchIndex].MatchHistoryOffset =
-		    xcrush->OptimizedMatches[MatchIndex].ChunkOffset;
+		Data_Write_UINT16(&MatchDetails[MatchIndex].MatchLength,
+		                  xcrush->OptimizedMatches[MatchIndex].MatchLength);
+		Data_Write_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset,
+		                  xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset);
+		Data_Write_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset,
+		                  xcrush->OptimizedMatches[MatchIndex].ChunkOffset);
 	}
 
 	CurrentOffset = HistoryOffset;
diff --git a/libfreerdp/codec/zgfx.c b/libfreerdp/codec/zgfx.c
index e260aa6..c632294 100644
--- a/libfreerdp/codec/zgfx.c
+++ b/libfreerdp/codec/zgfx.c
@@ -230,7 +230,10 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
 	BYTE* pbSegment;
 	size_t cbSegment;
 
-	if (!zgfx || !stream || (segmentSize < 2))
+	WINPR_ASSERT(zgfx);
+	WINPR_ASSERT(stream);
+
+	if (segmentSize < 2)
 		return FALSE;
 
 	cbSegment = segmentSize - 1;
@@ -259,7 +262,11 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
 	zgfx->pbInputCurrent = pbSegment;
 	zgfx->pbInputEnd = &pbSegment[cbSegment - 1];
 	/* NumberOfBitsToDecode = ((NumberOfBytesToDecode - 1) * 8) - ValueOfLastByte */
-	zgfx->cBitsRemaining = 8 * (cbSegment - 1) - *zgfx->pbInputEnd;
+	const UINT32 bits = 8u * (cbSegment - 1u);
+	if (bits < *zgfx->pbInputEnd)
+		return FALSE;
+
+	zgfx->cBitsRemaining = bits - *zgfx->pbInputEnd;
 	zgfx->cBitsCurrent = 0;
 	zgfx->BitsCurrent = 0;
 
@@ -345,8 +352,9 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t
 
 						if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount)
 							return FALSE;
-
-						if (count > zgfx->cBitsRemaining / 8)
+						else if (count > zgfx->cBitsRemaining / 8)
+							return FALSE;
+						else if (zgfx->pbInputCurrent + count > zgfx->pbInputEnd)
 							return FALSE;
 
 						CopyMemory(&(zgfx->OutputBuffer[zgfx->OutputCount]), zgfx->pbInputCurrent,
@@ -377,15 +385,45 @@ static BYTE* aligned_zgfx_malloc(size_t size)
 	return malloc(size + 64);
 }
 
+static BOOL zgfx_append(ZGFX_CONTEXT* zgfx, BYTE** ppConcatenated, size_t uncompressedSize,
+                        size_t* pUsed)
+{
+	WINPR_ASSERT(zgfx);
+	WINPR_ASSERT(ppConcatenated);
+	WINPR_ASSERT(pUsed);
+
+	const size_t used = *pUsed;
+	if (zgfx->OutputCount > UINT32_MAX - used)
+		return FALSE;
+
+	if (used + zgfx->OutputCount > uncompressedSize)
+		return FALSE;
+
+	BYTE* tmp = realloc(*ppConcatenated, used + zgfx->OutputCount + 64ull);
+	if (!tmp)
+		return FALSE;
+	*ppConcatenated = tmp;
+	CopyMemory(&tmp[used], zgfx->OutputBuffer, zgfx->OutputCount);
+	*pUsed = used + zgfx->OutputCount;
+	return TRUE;
+}
+
 int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData,
                     UINT32* pDstSize, UINT32 flags)
 {
 	int status = -1;
-	BYTE descriptor;
+	BYTE descriptor = 0;
+	size_t used = 0;
+	BYTE* pConcatenated = NULL;
 	wStream* stream = Stream_New((BYTE*)pSrcData, SrcSize);
 
-	if (!stream)
-		return -1;
+	WINPR_ASSERT(zgfx);
+	WINPR_ASSERT(stream);
+	WINPR_ASSERT(ppDstData);
+	WINPR_ASSERT(pDstSize);
+
+	*ppDstData = NULL;
+	*pDstSize = 0;
 
 	if (Stream_GetRemainingLength(stream) < 1)
 		goto fail;
@@ -397,16 +435,15 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
 		if (!zgfx_decompress_segment(zgfx, stream, Stream_GetRemainingLength(stream)))
 			goto fail;
 
-		*ppDstData = NULL;
-
 		if (zgfx->OutputCount > 0)
-			*ppDstData = aligned_zgfx_malloc(zgfx->OutputCount);
-
-		if (!*ppDstData)
-			goto fail;
-
-		*pDstSize = zgfx->OutputCount;
-		CopyMemory(*ppDstData, zgfx->OutputBuffer, zgfx->OutputCount);
+		{
+			if (!zgfx_append(zgfx, &pConcatenated, zgfx->OutputCount, &used))
+				goto fail;
+			if (used != zgfx->OutputCount)
+				goto fail;
+			*ppDstData = pConcatenated;
+			*pDstSize = zgfx->OutputCount;
+		}
 	}
 	else if (descriptor == ZGFX_SEGMENTED_MULTIPART)
 	{
@@ -414,8 +451,6 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
 		UINT16 segmentNumber;
 		UINT16 segmentCount;
 		UINT32 uncompressedSize;
-		BYTE* pConcatenated;
-		size_t used = 0;
 
 		if (Stream_GetRemainingLength(stream) < 6)
 			goto fail;
@@ -423,17 +458,6 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
 		Stream_Read_UINT16(stream, segmentCount);     /* segmentCount (2 bytes) */
 		Stream_Read_UINT32(stream, uncompressedSize); /* uncompressedSize (4 bytes) */
 
-		if (Stream_GetRemainingLength(stream) < segmentCount * sizeof(UINT32))
-			goto fail;
-
-		pConcatenated = aligned_zgfx_malloc(uncompressedSize);
-
-		if (!pConcatenated)
-			goto fail;
-
-		*ppDstData = pConcatenated;
-		*pDstSize = uncompressedSize;
-
 		for (segmentNumber = 0; segmentNumber < segmentCount; segmentNumber++)
 		{
 			if (Stream_GetRemainingLength(stream) < sizeof(UINT32))
@@ -444,16 +468,15 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
 			if (!zgfx_decompress_segment(zgfx, stream, segmentSize))
 				goto fail;
 
-			if (zgfx->OutputCount > UINT32_MAX - used)
+			if (!zgfx_append(zgfx, &pConcatenated, uncompressedSize, &used))
 				goto fail;
+		}
 
-			if (used + zgfx->OutputCount > uncompressedSize)
-				goto fail;
+		if (used != uncompressedSize)
+			goto fail;
 
-			CopyMemory(pConcatenated, zgfx->OutputBuffer, zgfx->OutputCount);
-			pConcatenated += zgfx->OutputCount;
-			used += zgfx->OutputCount;
-		}
+		*ppDstData = pConcatenated;
+		*pDstSize = uncompressedSize;
 	}
 	else
 	{
@@ -462,6 +485,8 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY
 
 	status = 1;
 fail:
+	if (status < 0)
+		free(pConcatenated);
 	Stream_Free(stream, FALSE);
 	return status;
 }
diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c
index 76f6646..8df8957 100644
--- a/libfreerdp/common/settings_getters.c
+++ b/libfreerdp/common/settings_getters.c
@@ -231,6 +231,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id)
 		case FreeRDP_GrabKeyboard:
 			return settings->GrabKeyboard;
 
+		case FreeRDP_GrabMouse:
+			return settings->GrabMouse;
+
 		case FreeRDP_HasExtendedMouseEvent:
 			return settings->HasExtendedMouseEvent;
 
@@ -809,6 +812,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val)
 			settings->GrabKeyboard = val;
 			break;
 
+		case FreeRDP_GrabMouse:
+			settings->GrabMouse = val;
+			break;
+
 		case FreeRDP_HasExtendedMouseEvent:
 			settings->HasExtendedMouseEvent = val;
 			break;
diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c
index d4e20ae..6fa11c0 100644
--- a/libfreerdp/common/settings_str.c
+++ b/libfreerdp/common/settings_str.c
@@ -87,6 +87,7 @@ static const struct settings_str_entry settings_map[] = {
 	{ FreeRDP_GfxSmallCache, 0, "FreeRDP_GfxSmallCache" },
 	{ FreeRDP_GfxThinClient, 0, "FreeRDP_GfxThinClient" },
 	{ FreeRDP_GrabKeyboard, 0, "FreeRDP_GrabKeyboard" },
+	{ FreeRDP_GrabMouse, 0, "FreeRDP_GrabMouse" },
 	{ FreeRDP_HasExtendedMouseEvent, 0, "FreeRDP_HasExtendedMouseEvent" },
 	{ FreeRDP_HasHorizontalWheel, 0, "FreeRDP_HasHorizontalWheel" },
 	{ FreeRDP_HasMonitorAttributes, 0, "FreeRDP_HasMonitorAttributes" },
diff --git a/libfreerdp/core/orders.c b/libfreerdp/core/orders.c
index 34ccbb7..d8bf58d 100644
--- a/libfreerdp/core/orders.c
+++ b/libfreerdp/core/orders.c
@@ -27,6 +27,7 @@
 
 #include <winpr/wtypes.h>
 #include <winpr/crt.h>
+#include <winpr/assert.h>
 
 #include <freerdp/api.h>
 #include <freerdp/log.h>
@@ -796,6 +797,7 @@ static INLINE BOOL update_write_4byte_unsigned(wStream* s, UINT32 value)
 static INLINE BOOL update_read_delta(wStream* s, INT32* value)
 {
 	BYTE byte;
+	UINT32 uvalue = 0;
 
 	if (Stream_GetRemainingLength(s) < 1)
 	{
@@ -806,9 +808,9 @@ static INLINE BOOL update_read_delta(wStream* s, INT32* value)
 	Stream_Read_UINT8(s, byte);
 
 	if (byte & 0x40)
-		*value = (byte | ~0x3F);
+		uvalue = (byte | ~0x3F);
 	else
-		*value = (byte & 0x3F);
+		uvalue = (byte & 0x3F);
 
 	if (byte & 0x80)
 	{
@@ -819,8 +821,9 @@ static INLINE BOOL update_read_delta(wStream* s, INT32* value)
 		}
 
 		Stream_Read_UINT8(s, byte);
-		*value = (*value << 8) | byte;
+		uvalue = (uvalue << 8) | byte;
 	}
+	*value = (INT32)uvalue;
 
 	return TRUE;
 }
@@ -1373,12 +1376,13 @@ static BOOL update_read_draw_nine_grid_order(wStream* s, const ORDER_INFO* order
 static BOOL update_read_multi_dstblt_order(wStream* s, const ORDER_INFO* orderInfo,
                                            MULTI_DSTBLT_ORDER* multi_dstblt)
 {
+	UINT32 numRectangles = multi_dstblt->numRectangles;
 	if (!read_order_field_coord(orderInfo, s, 1, &multi_dstblt->nLeftRect, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 2, &multi_dstblt->nTopRect, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 3, &multi_dstblt->nWidth, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 4, &multi_dstblt->nHeight, FALSE) ||
 	    !read_order_field_byte(orderInfo, s, 5, &multi_dstblt->bRop, TRUE) ||
-	    !read_order_field_byte(orderInfo, s, 6, &multi_dstblt->numRectangles, TRUE))
+	    !read_order_field_byte(orderInfo, s, 6, &numRectangles, TRUE))
 		return FALSE;
 
 	if ((orderInfo->fieldFlags & ORDER_FIELD_07) != 0)
@@ -1386,12 +1390,21 @@ static BOOL update_read_multi_dstblt_order(wStream* s, const ORDER_INFO* orderIn
 		if (Stream_GetRemainingLength(s) < 2)
 			return FALSE;
 
+		multi_dstblt->numRectangles = numRectangles;
 		Stream_Read_UINT16(s, multi_dstblt->cbData);
 		return update_read_delta_rects(s, multi_dstblt->rectangles, &multi_dstblt->numRectangles);
 	}
-
+	if (numRectangles > multi_dstblt->numRectangles)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles,
+		         multi_dstblt->numRectangles);
+		return FALSE;
+	}
+	multi_dstblt->numRectangles = numRectangles;
 	return TRUE;
 }
+
 static BOOL update_read_multi_patblt_order(wStream* s, const ORDER_INFO* orderInfo,
                                            MULTI_PATBLT_ORDER* multi_patblt)
 {
@@ -1407,7 +1420,8 @@ static BOOL update_read_multi_patblt_order(wStream* s, const ORDER_INFO* orderIn
 	if (!update_read_brush(s, &multi_patblt->brush, orderInfo->fieldFlags >> 7))
 		return FALSE;
 
-	if (!read_order_field_byte(orderInfo, s, 13, &multi_patblt->numRectangles, TRUE))
+	UINT32 numRectangles = multi_patblt->numRectangles;
+	if (!read_order_field_byte(orderInfo, s, 13, &numRectangles, TRUE))
 		return FALSE;
 
 	if ((orderInfo->fieldFlags & ORDER_FIELD_14) != 0)
@@ -1415,17 +1429,31 @@ static BOOL update_read_multi_patblt_order(wStream* s, const ORDER_INFO* orderIn
 		if (Stream_GetRemainingLength(s) < 2)
 			return FALSE;
 
+		multi_patblt->numRectangles = numRectangles;
 		Stream_Read_UINT16(s, multi_patblt->cbData);
 
 		if (!update_read_delta_rects(s, multi_patblt->rectangles, &multi_patblt->numRectangles))
 			return FALSE;
 	}
 
+	if (numRectangles > multi_patblt->numRectangles)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles,
+		         multi_patblt->numRectangles);
+		return FALSE;
+	}
+	multi_patblt->numRectangles = numRectangles;
+
 	return TRUE;
 }
 static BOOL update_read_multi_scrblt_order(wStream* s, const ORDER_INFO* orderInfo,
                                            MULTI_SCRBLT_ORDER* multi_scrblt)
 {
+	WINPR_ASSERT(orderInfo);
+	WINPR_ASSERT(multi_scrblt);
+
+	UINT32 numRectangles = multi_scrblt->numRectangles;
 	if (!read_order_field_coord(orderInfo, s, 1, &multi_scrblt->nLeftRect, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 2, &multi_scrblt->nTopRect, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 3, &multi_scrblt->nWidth, FALSE) ||
@@ -1433,7 +1461,7 @@ static BOOL update_read_multi_scrblt_order(wStream* s, const ORDER_INFO* orderIn
 	    !read_order_field_byte(orderInfo, s, 5, &multi_scrblt->bRop, TRUE) ||
 	    !read_order_field_coord(orderInfo, s, 6, &multi_scrblt->nXSrc, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 7, &multi_scrblt->nYSrc, FALSE) ||
-	    !read_order_field_byte(orderInfo, s, 8, &multi_scrblt->numRectangles, TRUE))
+	    !read_order_field_byte(orderInfo, s, 8, &numRectangles, TRUE))
 		return FALSE;
 
 	if ((orderInfo->fieldFlags & ORDER_FIELD_09) != 0)
@@ -1441,10 +1469,20 @@ static BOOL update_read_multi_scrblt_order(wStream* s, const ORDER_INFO* orderIn
 		if (Stream_GetRemainingLength(s) < 2)
 			return FALSE;
 
+		multi_scrblt->numRectangles = numRectangles;
 		Stream_Read_UINT16(s, multi_scrblt->cbData);
 		return update_read_delta_rects(s, multi_scrblt->rectangles, &multi_scrblt->numRectangles);
 	}
 
+	if (numRectangles > multi_scrblt->numRectangles)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles,
+		         multi_scrblt->numRectangles);
+		return FALSE;
+	}
+	multi_scrblt->numRectangles = numRectangles;
+
 	return TRUE;
 }
 static BOOL update_read_multi_opaque_rect_order(wStream* s, const ORDER_INFO* orderInfo,
@@ -1484,7 +1522,8 @@ static BOOL update_read_multi_opaque_rect_order(wStream* s, const ORDER_INFO* or
 		multi_opaque_rect->color = (multi_opaque_rect->color & 0x0000FFFF) | ((UINT32)byte << 16);
 	}
 
-	if (!read_order_field_byte(orderInfo, s, 8, &multi_opaque_rect->numRectangles, TRUE))
+	UINT32 numRectangles = multi_opaque_rect->numRectangles;
+	if (!read_order_field_byte(orderInfo, s, 8, &numRectangles, TRUE))
 		return FALSE;
 
 	if ((orderInfo->fieldFlags & ORDER_FIELD_09) != 0)
@@ -1492,22 +1531,32 @@ static BOOL update_read_multi_opaque_rect_order(wStream* s, const ORDER_INFO* or
 		if (Stream_GetRemainingLength(s) < 2)
 			return FALSE;
 
+		multi_opaque_rect->numRectangles = numRectangles;
 		Stream_Read_UINT16(s, multi_opaque_rect->cbData);
 		return update_read_delta_rects(s, multi_opaque_rect->rectangles,
 		                               &multi_opaque_rect->numRectangles);
 	}
+	if (numRectangles > multi_opaque_rect->numRectangles)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles,
+		         multi_opaque_rect->numRectangles);
+		return FALSE;
+	}
+	multi_opaque_rect->numRectangles = numRectangles;
 
 	return TRUE;
 }
 static BOOL update_read_multi_draw_nine_grid_order(wStream* s, const ORDER_INFO* orderInfo,
                                                    MULTI_DRAW_NINE_GRID_ORDER* multi_draw_nine_grid)
 {
+	UINT32 nDeltaEntries = multi_draw_nine_grid->nDeltaEntries;
 	if (!read_order_field_coord(orderInfo, s, 1, &multi_draw_nine_grid->srcLeft, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 2, &multi_draw_nine_grid->srcTop, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 3, &multi_draw_nine_grid->srcRight, FALSE) ||
 	    !read_order_field_coord(orderInfo, s, 4, &multi_draw_nine_grid->srcBottom, FALSE) ||
 	    !read_order_field_uint16(orderInfo, s, 5, &multi_draw_nine_grid->bitmapId, TRUE) ||
-	    !read_order_field_byte(orderInfo, s, 6, &multi_draw_nine_grid->nDeltaEntries, TRUE))
+	    !read_order_field_byte(orderInfo, s, 6, &nDeltaEntries, TRUE))
 		return FALSE;
 
 	if ((orderInfo->fieldFlags & ORDER_FIELD_07) != 0)
@@ -1515,11 +1564,21 @@ static BOOL update_read_multi_draw_nine_grid_order(wStream* s, const ORDER_INFO*
 		if (Stream_GetRemainingLength(s) < 2)
 			return FALSE;
 
+		multi_draw_nine_grid->nDeltaEntries = nDeltaEntries;
 		Stream_Read_UINT16(s, multi_draw_nine_grid->cbData);
 		return update_read_delta_rects(s, multi_draw_nine_grid->rectangles,
 		                               &multi_draw_nine_grid->nDeltaEntries);
 	}
 
+	if (nDeltaEntries > multi_draw_nine_grid->nDeltaEntries)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s nDeltaEntries %" PRIu32 " > %" PRIu32, orderName, nDeltaEntries,
+		         multi_draw_nine_grid->nDeltaEntries);
+		return FALSE;
+	}
+	multi_draw_nine_grid->nDeltaEntries = nDeltaEntries;
+
 	return TRUE;
 }
 static BOOL update_read_line_to_order(wStream* s, const ORDER_INFO* orderInfo,
@@ -1610,6 +1669,14 @@ static BOOL update_read_polyline_order(wStream* s, const ORDER_INFO* orderInfo,
 		return update_read_delta_points(s, polyline->points, polyline->numDeltaEntries,
 		                                polyline->xStart, polyline->yStart);
 	}
+	if (new_num > polyline->numDeltaEntries)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numDeltaEntries %" PRIu32 " > %" PRIu32, orderName, new_num,
+		         polyline->numDeltaEntries);
+		return FALSE;
+	}
+	polyline->numDeltaEntries = new_num;
 
 	return TRUE;
 }
@@ -1942,6 +2009,13 @@ static BOOL update_read_polygon_sc_order(wStream* s, const ORDER_INFO* orderInfo
 		return update_read_delta_points(s, polygon_sc->points, polygon_sc->numPoints,
 		                                polygon_sc->xStart, polygon_sc->yStart);
 	}
+	if (num > polygon_sc->numPoints)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numPoints %" PRIu32 " > %" PRIu32, orderName, num, polygon_sc->numPoints);
+		return FALSE;
+	}
+	polygon_sc->numPoints = num;
 
 	return TRUE;
 }
@@ -1987,6 +2061,14 @@ static BOOL update_read_polygon_cb_order(wStream* s, const ORDER_INFO* orderInfo
 			return FALSE;
 	}
 
+	if (num > polygon_cb->numPoints)
+	{
+		const char* orderName = __func__;
+		WLog_ERR(TAG, "%s numPoints %" PRIu32 " > %" PRIu32, orderName, num, polygon_cb->numPoints);
+		return FALSE;
+	}
+	polygon_cb->numPoints = num;
+
 	polygon_cb->backMode = (polygon_cb->bRop2 & 0x80) ? BACKMODE_TRANSPARENT : BACKMODE_OPAQUE;
 	polygon_cb->bRop2 = (polygon_cb->bRop2 & 0x1F);
 	return TRUE;
diff --git a/libfreerdp/core/server.c b/libfreerdp/core/server.c
index a786cbd..b8298a2 100644
--- a/libfreerdp/core/server.c
+++ b/libfreerdp/core/server.c
@@ -493,7 +493,7 @@ BOOL WTSVirtualChannelManagerCheckFileDescriptor(HANDLE hServer)
 		{
 			ULONG written;
 			vcm->drdynvc_channel = channel;
-			dynvc_caps = 0x00010050; /* DYNVC_CAPS_VERSION1 (4 bytes) */
+			Data_Write_UINT32(&dynvc_caps, 0x00010050); /* DYNVC_CAPS_VERSION1 (4 bytes) */
 
 			if (!WTSVirtualChannelWrite(channel, (PCHAR)&dynvc_caps, sizeof(dynvc_caps), &written))
 				return FALSE;
diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h
index 69f7bce..a85c2ff 100644
--- a/libfreerdp/core/test/settings_property_lists.h
+++ b/libfreerdp/core/test/settings_property_lists.h
@@ -76,6 +76,7 @@ static const size_t bool_list_indices[] = {
 	FreeRDP_GfxSmallCache,
 	FreeRDP_GfxThinClient,
 	FreeRDP_GrabKeyboard,
+	FreeRDP_GrabMouse,
 	FreeRDP_HasExtendedMouseEvent,
 	FreeRDP_HasHorizontalWheel,
 	FreeRDP_HasMonitorAttributes,
diff --git a/libfreerdp/core/window.c b/libfreerdp/core/window.c
index 51411c8..cb0acb9 100644
--- a/libfreerdp/core/window.c
+++ b/libfreerdp/core/window.c
@@ -359,33 +359,31 @@ static BOOL update_read_window_state_order(wStream* s, WINDOW_ORDER_INFO* orderI
 
 		Stream_Read_UINT16(s, windowState->numWindowRects); /* numWindowRects (2 bytes) */
 
-		if (windowState->numWindowRects == 0)
+		if (windowState->numWindowRects > 0)
 		{
-			return TRUE;
-		}
+			size = sizeof(RECTANGLE_16) * windowState->numWindowRects;
+			newRect = (RECTANGLE_16*)realloc(windowState->windowRects, size);
 
-		size = sizeof(RECTANGLE_16) * windowState->numWindowRects;
-		newRect = (RECTANGLE_16*)realloc(windowState->windowRects, size);
-
-		if (!newRect)
-		{
-			free(windowState->windowRects);
-			windowState->windowRects = NULL;
-			return FALSE;
-		}
+			if (!newRect)
+			{
+				free(windowState->windowRects);
+				windowState->windowRects = NULL;
+				return FALSE;
+			}
 
-		windowState->windowRects = newRect;
+			windowState->windowRects = newRect;
 
-		if (Stream_GetRemainingLength(s) < 8 * windowState->numWindowRects)
-			return FALSE;
+			if (Stream_GetRemainingLength(s) < 8 * windowState->numWindowRects)
+				return FALSE;
 
-		/* windowRects */
-		for (i = 0; i < windowState->numWindowRects; i++)
-		{
-			Stream_Read_UINT16(s, windowState->windowRects[i].left);   /* left (2 bytes) */
-			Stream_Read_UINT16(s, windowState->windowRects[i].top);    /* top (2 bytes) */
-			Stream_Read_UINT16(s, windowState->windowRects[i].right);  /* right (2 bytes) */
-			Stream_Read_UINT16(s, windowState->windowRects[i].bottom); /* bottom (2 bytes) */
+			/* windowRects */
+			for (i = 0; i < windowState->numWindowRects; i++)
+			{
+				Stream_Read_UINT16(s, windowState->windowRects[i].left);   /* left (2 bytes) */
+				Stream_Read_UINT16(s, windowState->windowRects[i].top);    /* top (2 bytes) */
+				Stream_Read_UINT16(s, windowState->windowRects[i].right);  /* right (2 bytes) */
+				Stream_Read_UINT16(s, windowState->windowRects[i].bottom); /* bottom (2 bytes) */
+			}
 		}
 	}
 
diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c
index 943b4df..69255c9 100644
--- a/libfreerdp/crypto/crypto.c
+++ b/libfreerdp/crypto/crypto.c
@@ -950,10 +950,13 @@ WINPR_MD_TYPE crypto_cert_get_signature_alg(X509* xcert)
 {
 	WINPR_ASSERT(xcert);
 
-	const int nid = X509_get_signature_nid(xcert);
+	EVP_PKEY* evp = X509_get_pubkey(xcert);
+	WINPR_ASSERT(evp);
 
 	int hash_nid = 0;
-	if (OBJ_find_sigid_algs(nid, &hash_nid, NULL) != 1)
+	const int res = EVP_PKEY_get_default_digest_nid(evp, &hash_nid);
+	EVP_PKEY_free(evp);
+	if (res <= 0)
 		return WINPR_MD_NONE;
 
 	switch (hash_nid)
@@ -976,7 +979,7 @@ WINPR_MD_TYPE crypto_cert_get_signature_alg(X509* xcert)
 			return WINPR_MD_SHA512;
 		case NID_ripemd160:
 			return WINPR_MD_RIPEMD160;
-#if (OPENSSL_VERSION_NUMBER >= 0x1010101fL) || defined(LIBRESSL_VERSION_NUMBER)
+#if (OPENSSL_VERSION_NUMBER >= 0x1010101fL) && !defined(LIBRESSL_VERSION_NUMBER)
 		case NID_sha3_224:
 			return WINPR_MD_SHA3_224;
 		case NID_sha3_256:
@@ -985,11 +988,11 @@ WINPR_MD_TYPE crypto_cert_get_signature_alg(X509* xcert)
 			return WINPR_MD_SHA3_384;
 		case NID_sha3_512:
 			return WINPR_MD_SHA3_512;
-#endif
 		case NID_shake128:
 			return WINPR_MD_SHAKE128;
 		case NID_shake256:
 			return WINPR_MD_SHAKE256;
+#endif
 		case NID_undef:
 		default:
 			return WINPR_MD_NONE;
diff --git a/libfreerdp/gdi/bitmap.c b/libfreerdp/gdi/bitmap.c
index 8fde465..cac8158 100644
--- a/libfreerdp/gdi/bitmap.c
+++ b/libfreerdp/gdi/bitmap.c
@@ -148,7 +148,7 @@ HGDI_BITMAP gdi_CreateCompatibleBitmap(HGDI_DC hdc, UINT32 nWidth, UINT32 nHeigh
 	hBitmap->width = nWidth;
 	hBitmap->height = nHeight;
 	hBitmap->data =
-	    _aligned_malloc(nWidth * nHeight * GetBytesPerPixel(hBitmap->format) * 1ULL, 16);
+	    _aligned_malloc(1ull * nWidth * nHeight * GetBytesPerPixel(hBitmap->format), 16);
 	hBitmap->free = _aligned_free;
 
 	if (!hBitmap->data)
diff --git a/libfreerdp/gdi/gdi.c b/libfreerdp/gdi/gdi.c
index 5ca905b..37a0104 100644
--- a/libfreerdp/gdi/gdi.c
+++ b/libfreerdp/gdi/gdi.c
@@ -997,16 +997,46 @@ static BOOL gdi_surface_frame_marker(rdpContext* context,
 	return TRUE;
 }
 
+static BOOL intersect_rect(const rdpGdi* gdi, const SURFACE_BITS_COMMAND* cmd, RECTANGLE_16* prect)
+{
+	const UINT32 w = (const UINT32)gdi->width;
+	const UINT32 h = (const UINT32)gdi->height;
+
+	if (cmd->destLeft > w)
+		return FALSE;
+	if (cmd->destRight > w)
+		return FALSE;
+	if (cmd->destLeft > cmd->destRight)
+		return FALSE;
+	if (cmd->destRight > UINT16_MAX)
+		return FALSE;
+
+	if (cmd->destTop > h)
+		return FALSE;
+	if (cmd->destBottom > h)
+		return FALSE;
+	if (cmd->destTop > cmd->destBottom)
+		return FALSE;
+	if (cmd->destBottom > UINT16_MAX)
+		return FALSE;
+
+	prect->left = (const UINT16)cmd->destLeft;
+	prect->top = (const UINT16)cmd->destTop;
+	prect->right = MIN((UINT16)cmd->destRight, prect->left + cmd->bmp.width);
+	prect->bottom = MIN((UINT16)cmd->destBottom, prect->top + cmd->bmp.height);
+	return TRUE;
+}
+
 static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cmd)
 {
 	BOOL result = FALSE;
 	DWORD format;
-	rdpGdi* gdi;
+	rdpGdi* gdi = NULL;
 	size_t size;
 	REGION16 region;
-	RECTANGLE_16 cmdRect;
-	UINT32 i, nbRects;
-	const RECTANGLE_16* rects;
+	RECTANGLE_16 cmdRect = { 0 };
+	UINT32 nbRects;
+	const RECTANGLE_16* rects = NULL;
 
 	if (!context || !cmd)
 		return FALSE;
@@ -1020,16 +1050,15 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm
 	    cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, cmd->bmp.bpp, cmd->bmp.flags,
 	    cmd->bmp.codecID, cmd->bmp.width, cmd->bmp.height, cmd->bmp.bitmapDataLength);
 	region16_init(&region);
-	cmdRect.left = cmd->destLeft;
-	cmdRect.top = cmd->destTop;
-	cmdRect.right = cmdRect.left + cmd->bmp.width;
-	cmdRect.bottom = cmdRect.top + cmd->bmp.height;
+
+	if (!intersect_rect(gdi, cmd, &cmdRect))
+		goto out;
 
 	switch (cmd->bmp.codecID)
 	{
 		case RDP_CODEC_ID_REMOTEFX:
 			if (!rfx_process_message(context->codecs->rfx, cmd->bmp.bitmapData,
-			                         cmd->bmp.bitmapDataLength, cmd->destLeft, cmd->destTop,
+			                         cmd->bmp.bitmapDataLength, cmdRect.left, cmdRect.top,
 			                         gdi->primary_buffer, gdi->dstFormat, gdi->stride, gdi->height,
 			                         &region))
 			{
@@ -1042,11 +1071,11 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm
 		case RDP_CODEC_ID_NSCODEC:
 			format = gdi->dstFormat;
 
-			if (!nsc_process_message(context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width,
-			                         cmd->bmp.height, cmd->bmp.bitmapData,
-			                         cmd->bmp.bitmapDataLength, gdi->primary_buffer, format,
-			                         gdi->stride, cmd->destLeft, cmd->destTop, cmd->bmp.width,
-			                         cmd->bmp.height, FREERDP_FLIP_VERTICAL))
+			if (!nsc_process_message(
+			        context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height,
+			        cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format,
+			        gdi->stride, cmdRect.left, cmdRect.top, cmdRect.right - cmdRect.left,
+			        cmdRect.bottom - cmdRect.top, FREERDP_FLIP_VERTICAL))
 			{
 				WLog_ERR(TAG, "Failed to process NSCodec message");
 				goto out;
@@ -1057,17 +1086,18 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm
 
 		case RDP_CODEC_ID_NONE:
 			format = gdi_get_pixel_format(cmd->bmp.bpp);
-			size = cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format) * 1ULL;
+			size = 1ull * cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format);
 			if (size > cmd->bmp.bitmapDataLength)
 			{
 				WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz,
 				         cmd->bmp.bitmapDataLength, size);
 				goto out;
 			}
-			if (!freerdp_image_copy(gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmd->destLeft,
-			                        cmd->destTop, cmd->bmp.width, cmd->bmp.height,
-			                        cmd->bmp.bitmapData, format, 0, 0, 0, &gdi->palette,
-			                        FREERDP_FLIP_VERTICAL))
+
+			if (!freerdp_image_copy(gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmdRect.left,
+			                        cmdRect.top, cmdRect.right - cmdRect.left,
+			                        cmdRect.bottom - cmdRect.top, cmd->bmp.bitmapData, format, 0, 0,
+			                        0, &gdi->palette, FREERDP_FLIP_VERTICAL))
 			{
 				WLog_ERR(TAG, "Failed to process nocodec message");
 				goto out;
@@ -1084,7 +1114,7 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm
 	if (!(rects = region16_rects(&region, &nbRects)))
 		goto out;
 
-	for (i = 0; i < nbRects; i++)
+	for (UINT32 i = 0; i < nbRects; i++)
 	{
 		UINT32 left = rects[i].left;
 		UINT32 top = rects[i].top;
diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c
index a3b7505..3970715 100644
--- a/libfreerdp/gdi/gfx.c
+++ b/libfreerdp/gdi/gfx.c
@@ -726,7 +726,7 @@ static UINT gdi_SurfaceCommand_Alpha(rdpGdi* gdi, RdpgfxClientContext* context,
 	{
 		UINT32 x, y;
 
-		if (Stream_GetRemainingLength(&s) < cmd->height * cmd->width * 1ULL)
+		if (Stream_GetRemainingLength(&s) < 1ull * cmd->height * cmd->width)
 			return ERROR_INVALID_DATA;
 
 		for (y = cmd->top; y < cmd->top + cmd->height; y++)
@@ -1025,7 +1025,7 @@ static UINT gdi_CreateSurface(RdpgfxClientContext* context,
 	}
 
 	surface->scanline = gfx_align_scanline(surface->width * 4UL, 16);
-	surface->data = (BYTE*)_aligned_malloc(surface->scanline * surface->height * 1ULL, 16);
+	surface->data = (BYTE*)_aligned_malloc(1ull * surface->scanline * surface->height, 16);
 
 	if (!surface->data)
 	{
@@ -1079,6 +1079,28 @@ static UINT gdi_DeleteSurface(RdpgfxClientContext* context,
 	return rc;
 }
 
+static BOOL intersect_rect(const RECTANGLE_16* rect, const gdiGfxSurface* surface,
+                           RECTANGLE_16* prect)
+{
+	WINPR_ASSERT(rect);
+	WINPR_ASSERT(surface);
+	WINPR_ASSERT(prect);
+
+	if (rect->left > rect->right)
+		return FALSE;
+	if (rect->left > surface->width)
+		return FALSE;
+	if (rect->top > rect->bottom)
+		return FALSE;
+	if (rect->top > surface->height)
+		return FALSE;
+	prect->left = rect->left;
+	prect->top = rect->top;
+	prect->right = MIN(rect->right, surface->width);
+	prect->bottom = MIN(rect->bottom, surface->height);
+	return TRUE;
+}
+
 /**
  * Function description
  *
@@ -1087,40 +1109,36 @@ static UINT gdi_DeleteSurface(RdpgfxClientContext* context,
 static UINT gdi_SolidFill(RdpgfxClientContext* context, const RDPGFX_SOLID_FILL_PDU* solidFill)
 {
 	UINT status = ERROR_INTERNAL_ERROR;
-	UINT16 index;
-	UINT32 color;
-	BYTE a, r, g, b;
-	UINT32 nWidth, nHeight;
-	RECTANGLE_16* rect;
-	gdiGfxSurface* surface;
-	RECTANGLE_16 invalidRect;
+	BYTE a = 0;
+	RECTANGLE_16 invalidRect = { 0 };
 	rdpGdi* gdi = (rdpGdi*)context->custom;
+
 	EnterCriticalSection(&context->mux);
-	surface = (gdiGfxSurface*)context->GetSurfaceData(context, solidFill->surfaceId);
+
+	WINPR_ASSERT(context->GetSurfaceData);
+	gdiGfxSurface* surface = (gdiGfxSurface*)context->GetSurfaceData(context, solidFill->surfaceId);
 
 	if (!surface)
 		goto fail;
 
-	b = solidFill->fillPixel.B;
-	g = solidFill->fillPixel.G;
-	r = solidFill->fillPixel.R;
-	/* a = solidFill->fillPixel.XA;
-	 * Ignore alpha channel, this is a solid fill. */
+	const BYTE b = solidFill->fillPixel.B;
+	const BYTE g = solidFill->fillPixel.G;
+	const BYTE r = solidFill->fillPixel.R;
 	a = 0xFF;
-	color = FreeRDPGetColor(surface->format, r, g, b, a);
+	const UINT32 color = FreeRDPGetColor(surface->format, r, g, b, a);
 
-	for (index = 0; index < solidFill->fillRectCount; index++)
+	for (UINT16 index = 0; index < solidFill->fillRectCount; index++)
 	{
-		rect = &(solidFill->fillRects[index]);
-		nWidth = rect->right - rect->left;
-		nHeight = rect->bottom - rect->top;
-		invalidRect.left = rect->left;
-		invalidRect.top = rect->top;
-		invalidRect.right = rect->right;
-		invalidRect.bottom = rect->bottom;
-
-		if (!freerdp_image_fill(surface->data, surface->format, surface->scanline, rect->left,
-		                        rect->top, nWidth, nHeight, color))
+		const RECTANGLE_16* rect = &(solidFill->fillRects[index]);
+
+		if (!intersect_rect(rect, surface, &invalidRect))
+			goto fail;
+
+		const UINT32 nWidth = invalidRect.right - invalidRect.left;
+		const UINT32 nHeight = invalidRect.bottom - invalidRect.top;
+
+		if (!freerdp_image_fill(surface->data, surface->format, surface->scanline, invalidRect.left,
+		                        invalidRect.top, nWidth, nHeight, color))
 			goto fail;
 
 		region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c
index c8b5fc9..479eac9 100644
--- a/libfreerdp/gdi/graphics.c
+++ b/libfreerdp/gdi/graphics.c
@@ -52,7 +52,7 @@ HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32
 		return NULL;
 
 	nDstStep = nWidth * GetBytesPerPixel(gdi->dstFormat);
-	pDstData = _aligned_malloc(nHeight * nDstStep * 1ULL, 16);
+	pDstData = _aligned_malloc(1ull * nHeight * nDstStep, 16);
 
 	if (!pDstData)
 		return NULL;
diff --git a/libfreerdp/gdi/shape.c b/libfreerdp/gdi/shape.c
index 6c45e0c..51a8a36 100644
--- a/libfreerdp/gdi/shape.c
+++ b/libfreerdp/gdi/shape.c
@@ -158,7 +158,7 @@ BOOL gdi_FillRect(HGDI_DC hdc, const HGDI_RECT rect, HGDI_BRUSH hbr)
 			for (y = 1; y < nHeight; y++)
 			{
 				BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest, nYDest + y);
-				memcpy(dstp, srcp, nWidth * formatSize * 1ULL);
+				memcpy(dstp, srcp, 1ull * nWidth * formatSize);
 			}
 
 			break;
diff --git a/libfreerdp/gdi/video.c b/libfreerdp/gdi/video.c
index 0e12c52..8c74f0a 100644
--- a/libfreerdp/gdi/video.c
+++ b/libfreerdp/gdi/video.c
@@ -66,7 +66,7 @@ static VideoSurface* gdiVideoCreateSurface(VideoClientContext* video, BYTE* data
 	ret->base.w = width;
 	ret->base.h = height;
 	ret->scanline = width * bpp;
-	ret->image = _aligned_malloc(ret->scanline * height * 1ULL, 16);
+	ret->image = _aligned_malloc(1ull * ret->scanline * height, 16);
 
 	if (!ret->image)
 	{
diff --git a/libfreerdp/locale/xkb_layout_ids.c b/libfreerdp/locale/xkb_layout_ids.c
index 223f7d1..9192219 100644
--- a/libfreerdp/locale/xkb_layout_ids.c
+++ b/libfreerdp/locale/xkb_layout_ids.c
@@ -806,7 +806,7 @@ static const XKB_LAYOUT xkbLayouts[] = {
 	{ "sk", KBD_SLOVAK, sk_variants },                           /* Slovakia */
 	{ "es", KBD_SPANISH, es_variants },                          /* Spain */
 	{ "se", KBD_SWEDISH, se_variants },                          /* Sweden */
-	{ "ch", KBD_SWISS_FRENCH, ch_variants },                     /* Switzerland */
+	{ "ch", KBD_SWISS_GERMAN, ch_variants },                     /* Switzerland */
 	{ "sy", KBD_SYRIAC, sy_variants },                           /* Syria */
 	{ "tj", 0, tj_variants },                                    /* Tajikistan */
 	{ "lk", 0, lk_variants },                                    /* Sri Lanka */
diff --git a/libfreerdp/primitives/prim_copy.c b/libfreerdp/primitives/prim_copy.c
index ae841d0..30c9cb1 100644
--- a/libfreerdp/primitives/prim_copy.c
+++ b/libfreerdp/primitives/prim_copy.c
@@ -60,14 +60,14 @@ static BOOL memory_regions_overlap_2d(const BYTE* p1, int p1Step, int p1Size, co
 
 	if (p1m <= p2m)
 	{
-		ULONG_PTR p1mEnd = p1m + (height - 1) * p1Step * 1ULL + width * p1Size * 1ULL;
+		ULONG_PTR p1mEnd = p1m + 1ull * (height - 1) * p1Step + 1ull * width * p1Size;
 
 		if (p1mEnd > p2m)
 			return TRUE;
 	}
 	else
 	{
-		ULONG_PTR p2mEnd = p2m + (height - 1) * p2Step * 1ULL + width * p2Size * 1ULL;
+		ULONG_PTR p2mEnd = p2m + 1ull * (height - 1) * p2Step + 1ull * width * p2Size;
 
 		if (p2mEnd > p1m)
 			return TRUE;
diff --git a/libfreerdp/primitives/primitives.c b/libfreerdp/primitives/primitives.c
index 0a4303d..6820a58 100644
--- a/libfreerdp/primitives/primitives.c
+++ b/libfreerdp/primitives/primitives.c
@@ -157,7 +157,7 @@ static primitives_YUV_benchmark* primitives_YUV_benchmark_init(primitives_YUV_be
 		if (!buf)
 			goto fail;
 
-		winpr_RAND(buf, roi->width * roi->height * 1ULL);
+		winpr_RAND(buf, 1ull * roi->width * roi->height);
 		ret->steps[i] = roi->width;
 	}
 
diff --git a/uwac/CMakeLists.txt b/uwac/CMakeLists.txt
index eaee928..c97cd4f 100644
--- a/uwac/CMakeLists.txt
+++ b/uwac/CMakeLists.txt
@@ -17,8 +17,8 @@
 
 # Soname versioning
 set(UWAC_VERSION_MAJOR "0")
-set(UWAC_VERSION_MINOR "1")
-set(UWAC_VERSION_REVISION "1")
+set(UWAC_VERSION_MINOR "2")
+set(UWAC_VERSION_REVISION "0")
 set(UWAC_VERSION "${UWAC_VERSION_MAJOR}.${UWAC_VERSION_MINOR}.${UWAC_VERSION_REVISION}")
 set(UWAC_VERSION_FULL "${UWAC_VERSION}")
 set(UWAC_API_VERSION "${UWAC_VERSION_MAJOR}")
diff --git a/uwac/include/uwac/uwac.h b/uwac/include/uwac/uwac.h
index c3ad12c..a86b48b 100644
--- a/uwac/include/uwac/uwac.h
+++ b/uwac/include/uwac/uwac.h
@@ -107,7 +107,9 @@ enum
 	UWAC_EVENT_CLIPBOARD_OFFER,
 	UWAC_EVENT_OUTPUT_GEOMETRY,
 	UWAC_EVENT_KEYBOARD_MODIFIERS,
-	UWAC_EVENT_POINTER_AXIS_DISCRETE
+	UWAC_EVENT_POINTER_AXIS_DISCRETE,
+	UWAC_EVENT_POINTER_FRAME,
+	UWAC_EVENT_POINTER_SOURCE
 };
 
 /** @brief window states */
@@ -195,6 +197,23 @@ struct uwac_pointer_axis_event
 };
 typedef struct uwac_pointer_axis_event UwacPointerAxisEvent;
 
+struct uwac_pointer_frame_event
+{
+	int type;
+	UwacWindow* window;
+	UwacSeat* seat;
+};
+typedef struct uwac_pointer_frame_event UwacPointerFrameEvent;
+
+struct uwac_pointer_source_event
+{
+	int type;
+	UwacWindow* window;
+	UwacSeat* seat;
+	enum wl_pointer_axis_source axis_source;
+};
+typedef struct uwac_pointer_source_event UwacPointerSourceEvent;
+
 struct uwac_touch_frame_event
 {
 	int type;
@@ -286,6 +305,8 @@ union uwac_event {
 	UwacPointerMotionEvent mouse_motion;
 	UwacPointerButtonEvent mouse_button;
 	UwacPointerAxisEvent mouse_axis;
+	UwacPointerFrameEvent mouse_frame;
+	UwacPointerSourceEvent mouse_source;
 	UwacKeyboardEnterLeaveEvent keyboard_enter_leave;
 	UwacKeyboardModifiersEvent keyboard_modifiers;
 	UwacClipboardEvent clipboard;
diff --git a/uwac/libuwac/uwac-input.c b/uwac/libuwac/uwac-input.c
index 6854092..0d448c3 100644
--- a/uwac/libuwac/uwac-input.c
+++ b/uwac/libuwac/uwac-input.c
@@ -846,12 +846,37 @@ static void pointer_handle_axis(void* data, struct wl_pointer* pointer, uint32_t
 
 static void pointer_frame(void* data, struct wl_pointer* wl_pointer)
 {
-	/*UwacSeat *seat = data;*/
+	UwacPointerFrameEvent* event;
+	UwacSeat* seat = data;
+	UwacWindow* window = seat->pointer_focus;
+
+	if (!window)
+		return;
+
+	event = (UwacPointerFrameEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_POINTER_FRAME);
+	if (!event)
+		return;
+
+	event->seat = seat;
+	event->window = window;
 }
 
 static void pointer_axis_source(void* data, struct wl_pointer* wl_pointer, uint32_t axis_source)
 {
-	/*UwacSeat *seat = data;*/
+	UwacPointerSourceEvent* event;
+	UwacSeat* seat = data;
+	UwacWindow* window = seat->pointer_focus;
+
+	if (!window)
+		return;
+
+	event = (UwacPointerSourceEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_POINTER_SOURCE);
+	if (!event)
+		return;
+
+	event->seat = seat;
+	event->window = window;
+	event->axis_source = axis_source;
 }
 
 static void pointer_axis_stop(void* data, struct wl_pointer* wl_pointer, uint32_t time,
diff --git a/uwac/libuwac/uwac-window.c b/uwac/libuwac/uwac-window.c
index d503755..c1e2b79 100644
--- a/uwac/libuwac/uwac-window.c
+++ b/uwac/libuwac/uwac-window.c
@@ -320,14 +320,14 @@ int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32
 
 	w->buffers = newBuffers;
 	memset(w->buffers + w->nbuffers, 0, sizeof(UwacBuffer) * nbuffers);
-	fd = uwac_create_anonymous_file(allocSize * nbuffers * 1ULL);
+	fd = uwac_create_anonymous_file(1ull * allocSize * nbuffers);
 
 	if (fd < 0)
 	{
 		return UWAC_ERROR_INTERNAL;
 	}
 
-	data = mmap(NULL, allocSize * nbuffers * 1ULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	data = mmap(NULL, 1ull * allocSize * nbuffers, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
 
 	if (data == MAP_FAILED)
 	{
@@ -339,7 +339,7 @@ int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32
 
 	if (!pool)
 	{
-		munmap(data, allocSize * nbuffers * 1ULL);
+		munmap(data, 1ull * allocSize * nbuffers);
 		ret = UWAC_ERROR_NOMEMORY;
 		goto error_mmap;
 	}
@@ -764,7 +764,7 @@ UwacReturnCode UwacWindowSubmitBuffer(UwacWindow* window, bool copyContentForNex
 
 	if (copyContentForNextFrame)
 		memcpy(nextDrawingBuffer->data, pendingBuffer->data,
-		       window->stride * window->height * 1ULL);
+		       1ull * window->stride * window->height);
 
 	UwacSubmitBufferPtr(window, pendingBuffer);
 	return UWAC_SUCCESS;
diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt
index 7b3ef1a..748582b 100644
--- a/winpr/CMakeLists.txt
+++ b/winpr/CMakeLists.txt
@@ -57,7 +57,7 @@ if (NOT WIN32)
 endif()
 
 # Soname versioning
-set(RAW_VERSION_STRING "2.10.0")
+set(RAW_VERSION_STRING "2.11.7")
 if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag")
 	file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING)
 elseif(USE_VERSION_FROM_GIT_TAG)
diff --git a/winpr/include/winpr/wtypes.h.in b/winpr/include/winpr/wtypes.h.in
index d264890..c868095 100644
--- a/winpr/include/winpr/wtypes.h.in
+++ b/winpr/include/winpr/wtypes.h.in
@@ -161,8 +161,9 @@ typedef void* PVOID, *LPVOID, *PVOID64, *LPVOID64;
 #ifndef __APPLE__
 typedef __int32 BOOL;
 #else /* __APPLE__ */
+#include <TargetConditionals.h>
 /* ensure compatibility with objc libraries */
-#if (TARGET_OS_IPHONE && __LP64__)  ||  TARGET_OS_WATCH
+#if (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0) && defined(__LP64__))  || (defined(TARGET_OS_WATCH) && (TARGET_OS_WATCH != 0))
 typedef bool BOOL;
 #else
 typedef signed char BOOL;
diff --git a/winpr/libwinpr/credui/CMakeLists.txt b/winpr/libwinpr/credui/CMakeLists.txt
index b01be1d..886fe12 100644
--- a/winpr/libwinpr/credui/CMakeLists.txt
+++ b/winpr/libwinpr/credui/CMakeLists.txt
@@ -19,8 +19,8 @@ winpr_module_add(credui.c)
 
 if(WIN32)
 	winpr_library_add_public(credui)
-endif()
-
-if(BUILD_TESTING)
-	add_subdirectory(test)
+else()
+    if(BUILD_TESTING)
+        add_subdirectory(test)
+    endif()
 endif()
diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt
index c1645b9..5ee4bbd 100644
--- a/winpr/libwinpr/crt/CMakeLists.txt
+++ b/winpr/libwinpr/crt/CMakeLists.txt
@@ -29,7 +29,7 @@ if (NOT WITH_ICU)
 endif(NOT WITH_ICU)
 
 if (WITH_ICU)
-	find_package(ICU REQUIRED i18n uc io)
+	find_package(ICU REQUIRED i18n uc io data)
 	winpr_include_directory_add(${ICU_INCLUDE_DIRS})
 	winpr_library_add_private(${ICU_LIBRARIES})
 endif (WITH_ICU)
diff --git a/winpr/libwinpr/crt/unicode.c b/winpr/libwinpr/crt/unicode.c
index a491979..dc3533a 100644
--- a/winpr/libwinpr/crt/unicode.c
+++ b/winpr/libwinpr/crt/unicode.c
@@ -40,6 +40,8 @@
 #include "../log.h"
 #define TAG WINPR_TAG("unicode")
 
+#define UCNV_CONVERT 1
+
 /**
  * Notes on cross-platform Unicode portability:
  *
@@ -201,6 +203,26 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int
 		targetCapacity = cchWideChar;
 		error = U_ZERO_ERROR;
 
+#if defined(UCNV_CONVERT)
+		if (cchWideChar == 0)
+		{
+			targetLength =
+			    ucnv_convert("UTF-16LE", "UTF-8", NULL, 0, lpMultiByteStr, cbMultiByte, &error);
+			if (targetLength > 0)
+				targetLength /= sizeof(WCHAR);
+			cchWideChar = targetLength;
+		}
+		else
+		{
+			targetLength =
+			    ucnv_convert("UTF-16LE", "UTF-8", targetStart, targetCapacity * sizeof(WCHAR),
+			                 lpMultiByteStr, cbMultiByte, &error);
+			if (targetLength > 0)
+				targetLength /= sizeof(WCHAR);
+			cchWideChar = U_SUCCESS(error) ? targetLength : 0;
+		}
+
+#else
 		if (cchWideChar == 0)
 		{
 			u_strFromUTF8(NULL, 0, &targetLength, lpMultiByteStr, cbMultiByte, &error);
@@ -212,6 +234,7 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int
 			              &error);
 			cchWideChar = U_SUCCESS(error) ? targetLength : 0;
 		}
+#endif
 	}
 #else
 
@@ -327,6 +350,21 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int
 		targetCapacity = cbMultiByte;
 		error = U_ZERO_ERROR;
 
+#if defined(UCNV_CONVERT)
+		if (cbMultiByte == 0)
+		{
+			targetLength = ucnv_convert("UTF-8", "UTF-16LE", NULL, 0, lpWideCharStr,
+			                            cchWideChar * sizeof(WCHAR), &error);
+			cbMultiByte = targetLength;
+		}
+		else
+		{
+			targetLength = ucnv_convert("UTF-8", "UTF-16LE", targetStart, targetCapacity,
+			                            lpWideCharStr, cchWideChar * sizeof(WCHAR), &error);
+			cbMultiByte = U_SUCCESS(error) ? targetLength : 0;
+		}
+
+#else
 		if (cbMultiByte == 0)
 		{
 			u_strToUTF8(NULL, 0, &targetLength, lpWideCharStr, cchWideChar, &error);
@@ -338,6 +376,7 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int
 			            &error);
 			cbMultiByte = U_SUCCESS(error) ? targetLength : 0;
 		}
+#endif
 	}
 #else
 
diff --git a/winpr/libwinpr/crypto/cipher.c b/winpr/libwinpr/crypto/cipher.c
index 41725ab..0fffc6d 100644
--- a/winpr/libwinpr/crypto/cipher.c
+++ b/winpr/libwinpr/crypto/cipher.c
@@ -66,7 +66,11 @@ static WINPR_RC4_CTX* winpr_RC4_New_Internal(const BYTE* key, size_t keylen, BOO
 	if (!evp)
 		return NULL;
 
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+	EVP_CIPHER_CTX_reset((EVP_CIPHER_CTX*)ctx);
+#else
 	EVP_CIPHER_CTX_init((EVP_CIPHER_CTX*)ctx);
+#endif
 	if (EVP_EncryptInit_ex((EVP_CIPHER_CTX*)ctx, evp, NULL, NULL, NULL) != 1)
 	{
 		EVP_CIPHER_CTX_free ((EVP_CIPHER_CTX*)ctx);
diff --git a/winpr/libwinpr/utils/collections/ArrayList.c b/winpr/libwinpr/utils/collections/ArrayList.c
index f8fc3df..cb7e39a 100644
--- a/winpr/libwinpr/utils/collections/ArrayList.c
+++ b/winpr/libwinpr/utils/collections/ArrayList.c
@@ -233,6 +233,18 @@ BOOL ArrayList_Contains(wArrayList* arrayList, void* obj)
  * Adds an object to the end of the ArrayList.
  */
 
+static int insert(wArrayList* arrayList, size_t index, void* obj)
+{
+	if (arrayList->object.fnObjectNew)
+	{
+		obj = arrayList->object.fnObjectNew(obj);
+		arrayList->array[index] = obj;
+	}
+	else
+		arrayList->array[index] = obj;
+	return index + 1;
+}
+
 int ArrayList_Add(wArrayList* arrayList, void* obj)
 {
 	int index = -1;
@@ -253,8 +265,7 @@ int ArrayList_Add(wArrayList* arrayList, void* obj)
 		arrayList->capacity = newCapacity;
 	}
 
-	arrayList->array[arrayList->size++] = obj;
-	index = arrayList->size;
+	index = arrayList->size = insert(arrayList, arrayList->size, obj);
 out:
 
 	if (arrayList->synchronized)
@@ -282,7 +293,7 @@ BOOL ArrayList_Insert(wArrayList* arrayList, int index, void* obj)
 		}
 		else
 		{
-			arrayList->array[index] = obj;
+			insert(arrayList, index, obj);
 		}
 	}
 
diff --git a/winpr/libwinpr/utils/lodepng/lodepng.c b/winpr/libwinpr/utils/lodepng/lodepng.c
index 2dd526a..b5c2307 100644
--- a/winpr/libwinpr/utils/lodepng/lodepng.c
+++ b/winpr/libwinpr/utils/lodepng/lodepng.c
@@ -3815,7 +3815,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, LodePNGCol
 {
 	size_t i;
 	ColorTree tree;
-	size_t numpixels = w * h * 1ULL;
+	size_t numpixels = 1ull * w * h;
 
 	if (lodepng_color_mode_equal(mode_out, mode_in))
 	{
@@ -3918,7 +3918,7 @@ unsigned get_color_profile(LodePNGColorProfile* profile, const unsigned char* in
 	unsigned error = 0;
 	size_t i;
 	ColorTree tree;
-	size_t numpixels = w * h * 1ULL;
+	size_t numpixels = 1ull * w * h;
 
 	unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0;
 	unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1;
@@ -4539,7 +4539,7 @@ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsi
 		if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8)
 		{
 			CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
-			removePaddingBits(out, in, w * bpp * 1ULL, ((w * bpp + 7ULL) / 8ULL) * 8ULL, h);
+			removePaddingBits(out, in, 1ull * w * bpp, ((w * bpp + 7ULL) / 8ULL) * 8ULL, h);
 		}
 		/*we can immediatly filter into the out buffer, no other steps needed*/
 		else
@@ -4565,7 +4565,7 @@ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsi
 				bits between the different reduced images: each reduced image still starts nicely at
 				a byte*/
 				removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]],
-				                  passw[i] * bpp * 1ULL, ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL,
+				                  1ull * passw[i] * bpp, ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL,
 				                  passh[i]);
 			}
 		}
@@ -6056,7 +6056,7 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const
 					error = 83; /*alloc fail*/
 				if (!error)
 				{
-					addPaddingBits(padded, in, ((w * bpp + 7ULL) / 8ULL) * 8ULL, w * bpp * 1ULL, h);
+					addPaddingBits(padded, in, ((w * bpp + 7ULL) / 8ULL) * 8ULL, 1ull * w * bpp, h);
 					error = filter(*out, padded, w, h, &info_png->color, settings);
 				}
 				free(padded);
@@ -6100,8 +6100,8 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const
 					if (!padded)
 						ERROR_BREAK(83); /*alloc fail*/
 					addPaddingBits(padded, &adam7[passstart[i]],
-					               ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL, passw[i] * bpp * 1ULL,
-					               passh[i] * 1ULL);
+					               ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL, 1ull * passw[i] * bpp,
+					               1ull * passh[i]);
 					error = filter(&(*out)[filter_passstart[i]], padded, passw[i], passh[i],
 					               &info_png->color, settings);
 					free(padded);

Reply to: