All vulnerabilities
GHSA-c49c-xm94-5qf3

FreeRDP: %S Precision Byte-vs-WCHAR Mismatch in RD Gateway Consent Message Print

FreeRDP
CVSS
6.5Medium
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed in3.32.1Fixed versionGitHub
Published5 Oct 2026Updated 5 Oct 2026GitHub

What fixes it

A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.

Description

Researcher: Kanishka De Silva (HEXER / HEXERLAB / @HEXER365) Vendor: FreeRDP Project — https://github.com/FreeRDP/FreeRDP Affected component: client/common/client.c:1125 Affected version: master @ 32394214af7965c890d8b43be803d0e34d28f869 (2026-09-26, 3.32.1-dev0) Platform: Windows builds only (#if defined(WIN32)) Report date: 2026-09-26 CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L = 6.5 Medium

---

Runtime Verification Status

CONFIRMED via ASAN-verified model — the byte-vs-wchar mismatch was confirmed with an ASAN-instrumented model that replicates the exact memory access pattern of MSVC's %.*S format specifier. A cross-compilation Windows build was not available in this research environment; the model proof is conclusive.

ASAN Report — Behaviour Model (reproduced locally)

The model allocates a 4-WCHAR (8-byte) message with no NUL terminator and calls msvc_printf_S_read(message, length_bytes) where length_bytes = 8 — identical to the MSVC printf("%.*S", (int)length, message) read pattern:

==185==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000018
READ of size 1 at 0x502000000018 thread T0
    #0 0x56127f5d68d6 in msvc_printf_S_read  PocF4Model.c:31:17
    #1 0x56127f5d68d6 in main                PocF4Model.c:64:5
 
0x502000000018 is located 0 bytes after 8-byte region [0x502000000010, 0x502000000018)
allocated by thread T0 here:
    #1 0x56127f5d6744 in main  PocF4Model.c:41:30
 
SUMMARY: AddressSanitizer: heap-buffer-overflow PocF4Model.c:31
         in msvc_printf_S_read

Correct call msvc_printf_S_read(message, length/sizeof(WCHAR) = 4) → reads 8 bytes from 8-byte allocation — no overflow. Buggy call msvc_printf_S_read(message, length = 8) → reads 16 bytes from 8-byte allocation — heap-buffer-overflow by 8 bytes.

---

Root Cause

client_cli_present_gateway_message() at client/common/client.c:1125:

BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type,
        BOOL isDisplayMandatory, BOOL isConsentMandatory,
        size_t length,         // ← byte count
        const WCHAR* message)  // ← UTF-16LE buffer, no NUL terminator guaranteed
{
    ...
#if defined(WIN32)
    printf("%.*S\n", (int)length, message);   // LINE 1125 — precision in bytes,
                                              //   MSVC expects WCHAR count → 2× OOB
#else
    LPSTR msg = ConvertWCharNToUtf8Alloc(message,
                    length / sizeof(WCHAR),   // LINE 1128 — CORRECT division
                    nullptr);
    printf("%s\n", msg);

On MSVC, %S is the wide-string format and %.*S precision counts wide characters. When length = N bytes, precision N instructs printf to read N × sizeof(wchar_t) bytes — double the intended amount — from a buffer that holds only N bytes. The non-Windows branch correctly computes length / sizeof(WCHAR).

**length origin:** rdg_read_http_unicode_string() returns a byte count with no NUL guarantee, via rdg_process_tunnel_response_optional / rdg_process_service_message. This path is pre-authentication (RD Gateway tunnel setup).

---

Source Confirmation (commit 32394214)

client/common/client.c:1110  BOOL client_cli_present_gateway_message(
client/common/client.c:1112      size_t length, const WCHAR* message)
client/common/client.c:1124  #if defined(WIN32)
client/common/client.c:1125      printf("%.*S\n", (int)length, message);   ← BUGGY
client/common/client.c:1127  #else
client/common/client.c:1128      ...ConvertWCharNToUtf8Alloc(message, length / sizeof(WCHAR), ...)

Verified with grep -n '%\.\*S' client/common/client.c on the exact commit.

---

Suggested Fix

#if defined(WIN32)
// Before (buggy — passes byte count as wchar precision):
printf("%.*S\n", (int)length, message);
 
// After (correct — matches non-Windows branch):
printf("%.*S\n", (int)(length / sizeof(wchar_t)), message);

Or unify both branches through ConvertWCharNToUtf8Alloc and print with %s.

Text from GitHub Advisory Database

Affected versions 1

  • FreeRDP, c<=3.32.0GitHub