mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 02:08:22 -10:00
menu: Fix EXC_BREAKPOINT when typing in OSK on macOS (in-place word_wrap)
ozone_draw_osk and the OSK caret rendering in xmb/materialui all
called word_wrap() with the same buffer as source and destination:
(ozone->word_wrap)(cursor_message, sizeof(cursor_message),
cursor_message, strlen(cursor_message), ...);
word_wrap()/word_wrap_wideglyph() copy via strlcpy(), whose buffers
must not overlap. On macOS, compat/strl.h force-defines HAVE_STRL, so
the system strlcpy is used; with _FORTIFY_SOURCE this resolves to
__strlcpy_chk, which aborts on any overlap (including exact aliasing)
via __chk_fail_overlap. As a result, typing a single character into
any OSK field (e.g. the IP Address setting under SMB Network
Settings) hits word_wrap's short-string early return
`strlcpy(s, src, len)` with s == src and raises EXC_BREAKPOINT.
Crash frame: len=2048, src_len=1, line_width=84, s == src == "1";
video_thread_loop -> ozone_draw_osk -> word_wrap -> __strlcpy_chk ->
__chk_fail_overlap. Linux/Windows never crashed only because the
strlcpy_retro__ fallback tolerates exact aliasing by accident.
In addition, in-place use of word_wrap_wideglyph() silently corrupts
CJK OSK text on all platforms: the lastwideglyph wrap branch
overwrites a source byte with '\n' before rewinding src onto it.
In-place wrapping of the kana string
E3 81 82 .. E3 81 93 (10 glyphs, line_width 4, wideglyph_width 200)
produces glyph 1 + 24 bare newlines + glyph 10 instead of one glyph
per line.
Fix: stage the OSK text in a separate cursor_src buffer at all three
call sites and wrap into the destination, matching the tmpstr pattern
already used for ozone's sidebar metadata. Document the
non-overlapping-buffers requirement for word_wrap()/
word_wrap_wideglyph() in stdstring.{c,h}. All other in-tree callers
(rgui, gfx_widgets, message boxes, sublabels) already use distinct
buffers.
Sanitizer sweep (ASan+UBSan+LSan, gcc, system strlcpy via
-DHAVE_STRL on glibc 2.39):
- fixed caller pattern yields "1" for the crashing input, clean
- separate-buffer output verified byte-exact against the previous
in-place output for ASCII inputs across line widths 5/10/30/84
(no rendering change for existing users)
- libretro-common word_wrap_wideglyph overflow regression tests pass
- ozone.c, xmb.c, materialui.c, stdstring.c compile clean
TSan: not applicable (single-threaded string path, no concurrency
change).
This commit is contained in:
1 parent
703133d989
commit
bd79aee4a7
5 files changed
+50
-20
No files matched your search
@@ -203,6 +203,10 @@ char *string_trim_whitespace(char *const s);
|
||||
* regular Latin characters - i.e. it will not wrap
|
||||
* correctly any text containing so-called 'wide' Unicode
|
||||
* characters (e.g. CJK languages, emojis, etc.).
|
||||
* @dst and @src must NOT overlap: the copy is performed
|
||||
* with strlcpy(), which has undefined behaviour for
|
||||
* overlapping buffers (and aborts via __chk_fail_overlap
|
||||
* with fortified libc on macOS).
|
||||
**/
|
||||
size_t word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len,
|
||||
int line_width, int wideglyph_width, unsigned max_lines);
|
||||
@@ -240,6 +244,10 @@ size_t word_wrap(char *dst, size_t dst_size, const char *src, size_t src_len,
|
||||
* if @src string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* @wideglyph_width value.
|
||||
* @dst and @src must NOT overlap: the copy is performed
|
||||
* with strlcpy(), which has undefined behaviour for
|
||||
* overlapping buffers (and aborts via __chk_fail_overlap
|
||||
* with fortified libc on macOS).
|
||||
**/
|
||||
size_t word_wrap_wideglyph(
|
||||
char *dst, size_t dst_size,
|
||||
|
||||
@@ -207,6 +207,10 @@ char *string_trim_whitespace(char *const s)
|
||||
* regular Latin characters - i.e. it will not wrap
|
||||
* correctly any text containing so-called 'wide' Unicode
|
||||
* characters (e.g. CJK languages, emojis, etc.).
|
||||
* @s and @src must NOT overlap: the copy is performed
|
||||
* with strlcpy(), which has undefined behaviour for
|
||||
* overlapping buffers (and aborts via __chk_fail_overlap
|
||||
* with fortified libc on macOS).
|
||||
**/
|
||||
size_t word_wrap(
|
||||
char *s, size_t len,
|
||||
@@ -317,6 +321,10 @@ size_t word_wrap(
|
||||
* if @src string contains 'wide' Unicode characters whose
|
||||
* on-screen pixel width deviates greatly from the set
|
||||
* @wideglyph_width value.
|
||||
* @s and @src must NOT overlap: the copy is performed
|
||||
* with strlcpy(), which has undefined behaviour for
|
||||
* overlapping buffers (and aborts via __chk_fail_overlap
|
||||
* with fortified libc on macOS).
|
||||
**/
|
||||
size_t word_wrap_wideglyph(char *s, size_t len,
|
||||
const char *src, size_t src_len, int line_width,
|
||||
|
||||
@@ -2929,24 +2929,28 @@ static void materialui_render_messagebox(
|
||||
if (input && line->buffer
|
||||
&& ((menu_driver_get_current_time() / 500000) & 1))
|
||||
{
|
||||
char cursor_src[MENU_LABEL_MAX_LENGTH];
|
||||
char cursor_msg[MENU_LABEL_MAX_LENGTH];
|
||||
size_t len = (size_t)(input - msg + 1);
|
||||
size_t ptr = line->ptr;
|
||||
|
||||
if (ptr > line->size)
|
||||
ptr = line->size;
|
||||
if (len < sizeof(cursor_msg))
|
||||
if (len < sizeof(cursor_src))
|
||||
{
|
||||
if (ptr >= sizeof(cursor_msg) - len)
|
||||
ptr = sizeof(cursor_msg) - len - 1;
|
||||
if (ptr >= sizeof(cursor_src) - len)
|
||||
ptr = sizeof(cursor_src) - len - 1;
|
||||
|
||||
memcpy(cursor_msg, msg, len);
|
||||
memcpy(cursor_msg + len, line->buffer, ptr);
|
||||
cursor_msg[len + ptr] = '\0';
|
||||
/* word_wrap()/word_wrap_wideglyph() require
|
||||
* non-overlapping source and destination
|
||||
* buffers, so stage the source separately */
|
||||
memcpy(cursor_src, msg, len);
|
||||
memcpy(cursor_src + len, line->buffer, ptr);
|
||||
cursor_src[len + ptr] = '\0';
|
||||
|
||||
(mui->word_wrap)(
|
||||
cursor_msg, sizeof(cursor_msg),
|
||||
cursor_msg, strlen(cursor_msg),
|
||||
cursor_src, len + ptr,
|
||||
usable_width / (int)mui->font_data.list.glyph_width,
|
||||
mui->font_data.list.wideglyph_width, 0);
|
||||
|
||||
|
||||
+12
-6
@@ -7370,20 +7370,26 @@ OZONE_NOINLINE static void ozone_draw_osk(
|
||||
|
||||
if (!draw_placeholder && input_st->keyboard_line.buffer)
|
||||
{
|
||||
char cursor_src[2048];
|
||||
char cursor_message[2048];
|
||||
size_t ptr = input_st->keyboard_line.ptr;
|
||||
|
||||
if (ptr > input_st->keyboard_line.size)
|
||||
ptr = input_st->keyboard_line.size;
|
||||
if (ptr >= sizeof(cursor_message))
|
||||
ptr = sizeof(cursor_message) - 1;
|
||||
if (ptr >= sizeof(cursor_src))
|
||||
ptr = sizeof(cursor_src) - 1;
|
||||
|
||||
memcpy(cursor_message, input_st->keyboard_line.buffer, ptr);
|
||||
cursor_message[ptr] = '\0';
|
||||
/* word_wrap()/word_wrap_wideglyph() require
|
||||
* non-overlapping source and destination buffers
|
||||
* (in-place use aborts under fortified strlcpy on
|
||||
* macOS and corrupts wide glyphs in the wideglyph
|
||||
* variant), so stage the source separately */
|
||||
memcpy(cursor_src, input_st->keyboard_line.buffer, ptr);
|
||||
cursor_src[ptr] = '\0';
|
||||
(ozone->word_wrap)(cursor_message,
|
||||
sizeof(cursor_message),
|
||||
cursor_message,
|
||||
strlen(cursor_message),
|
||||
cursor_src,
|
||||
ptr,
|
||||
(video_width - (margin * 2) - (padding * 2)) / ozone->fonts.entries_label.glyph_width,
|
||||
ozone->fonts.entries_label.wideglyph_width,
|
||||
0);
|
||||
|
||||
+11
-7
@@ -1355,24 +1355,28 @@ XMB_NOINLINE static void xmb_render_messagebox_internal(
|
||||
if (input && line->buffer
|
||||
&& ((menu_driver_get_current_time() / 500000) & 1))
|
||||
{
|
||||
char cursor_src[MENU_LABEL_MAX_LENGTH];
|
||||
char cursor_message[MENU_LABEL_MAX_LENGTH];
|
||||
size_t len = (size_t)(input - message + 1);
|
||||
size_t ptr = line->ptr;
|
||||
|
||||
if (ptr > line->size)
|
||||
ptr = line->size;
|
||||
if (len < sizeof(cursor_message))
|
||||
if (len < sizeof(cursor_src))
|
||||
{
|
||||
if (ptr >= sizeof(cursor_message) - len)
|
||||
ptr = sizeof(cursor_message) - len - 1;
|
||||
if (ptr >= sizeof(cursor_src) - len)
|
||||
ptr = sizeof(cursor_src) - len - 1;
|
||||
|
||||
memcpy(cursor_message, message, len);
|
||||
memcpy(cursor_message + len, line->buffer, ptr);
|
||||
cursor_message[len + ptr] = '\0';
|
||||
/* word_wrap()/word_wrap_wideglyph() require
|
||||
* non-overlapping source and destination
|
||||
* buffers, so stage the source separately */
|
||||
memcpy(cursor_src, message, len);
|
||||
memcpy(cursor_src + len, line->buffer, ptr);
|
||||
cursor_src[len + ptr] = '\0';
|
||||
|
||||
(xmb->word_wrap)(
|
||||
cursor_message, sizeof(cursor_message),
|
||||
cursor_message, strlen(cursor_message),
|
||||
cursor_src, len + ptr,
|
||||
usable_width / (xmb->font_size * 0.85f),
|
||||
xmb->wideglyph_width, 0);
|
||||
|
||||
|
||||
Reference in new issue
Block a user