-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
libobs: Fix crash with non-ASCII characters in formatted filenames #12964
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -695,6 +695,41 @@ static void erase_ch(struct dstr *str, size_t pos) | |
| *str = new_str; | ||
| } | ||
|
|
||
| size_t os_strftime_utf8(char *dst, size_t dst_size, const char *format, const struct tm *tm) | ||
| { | ||
| if (!dst || !dst_size || !format || !tm) | ||
| return 0; | ||
|
|
||
| #ifdef _WIN32 | ||
| /* | ||
| * On Windows, strftime() returns strings encoded in the system's | ||
| * ANSI codepage, not UTF-8. This causes issues when locale-specific | ||
| * format specifiers (like %Z for timezone) contain non-ASCII | ||
| * characters (e.g., German "Mitteleuropäische Zeit"). | ||
| * | ||
| * Use wcsftime() to get proper wide characters, then convert to UTF-8. | ||
| */ | ||
| wchar_t wformat[64]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is good practice to use As the format string is UTF-8 encoded by convention, you cannot make any simple assumptions about the required size for the UTF-16 string (as both are variable length). Users are free to combine the format string with any text that is valid UTF-8 with the actual format modifiers, so this has to be reactive to that. |
||
| wchar_t wdst[256]; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The maximum size of the intermediary buffer should depend on the provided size of the destination buffer in The constant Assuming "2 bytes for every byte available in the output" seems reasonable to me: 256 bytes of UTF-16 code points of the ASCII range can take as little as 128 bytes of UTF-8, any characters beyond that will take 2-3 bytes, and characters beyond So a 2x intermediary buffer will be able to hold at most as much ASCII data as the destination buffer can hold and will easily have enough capacity for any code points that might not even fit into the output buffer after conversion. |
||
|
|
||
| size_t format_len = os_utf8_to_wcs(format, 0, wformat, sizeof(wformat) / sizeof(wchar_t)); | ||
| if (!format_len) { | ||
| dst[0] = '\0'; | ||
| return 0; | ||
| } | ||
|
|
||
| size_t wlen = wcsftime(wdst, sizeof(wdst) / sizeof(wchar_t), wformat, tm); | ||
| if (!wlen) { | ||
| dst[0] = '\0'; | ||
| return 0; | ||
| } | ||
|
|
||
| return os_wcs_to_utf8(wdst, wlen, dst, dst_size); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both When called on POSIX, And That's because there's not a single "2-to-1" ratio between UTF-16 and UTF-8: All characters in the Basic Multilingual Plane (BMP) are encoded in 2 bytes in UTF-16, but can take anywhere between 1 to 3 bytes in UTF-8. Characters beyond that always take up 4 bytes in UTF-16 and UTF-8. So while a 256-byte UTF-16 buffer can potentially hold just 128 bytes worth of ASCII data (see above), even a 128-byte UTF-16 buffer can potentially require more than 128 bytes to properly encode all code points in UTF-8 and would thus exceed the output buffer size provided by |
||
| #else | ||
| return strftime(dst, dst_size, format, tm); | ||
| #endif | ||
| } | ||
|
|
||
| char *os_generate_formatted_filename(const char *extension, bool space, const char *format) | ||
| { | ||
| time_t now = time(0); | ||
|
|
@@ -727,9 +762,9 @@ char *os_generate_formatted_filename(const char *extension, bool space, const ch | |
|
|
||
| if (astrcmp_n(cmp, spec[i][0], len) == 0) { | ||
| if (strlen(spec[i][1])) | ||
| strftime(convert, sizeof(convert), spec[i][1], cur_time); | ||
| os_strftime_utf8(convert, sizeof(convert), spec[i][1], cur_time); | ||
| else | ||
| strftime(convert, sizeof(convert), spec[i][0], cur_time); | ||
| os_strftime_utf8(convert, sizeof(convert), spec[i][0], cur_time); | ||
|
|
||
| dstr_copy(&c, convert); | ||
| if (c.len && valid_string(c.array)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this function's implementation is platform-specific, it should be implemented that way in
platform-nix.candplatform-windows.c.