AppImageCrafters / AppRun

AppDir runtime components
MIT License
26 stars 10 forks source link

wchar file operation segfaults on i386 #37

Open richardg867 opened 2 years ago

richardg867 commented 2 years ago

I've recently introduced AppImage packaging with appimage-builder to 86Box/86Box. While the setup I came up with works perfectly on x86_64, armhf and aarch64, I run into a consistent segfault related to widechar file operations (used to load our 86box.cfg config file) on i386, reproduced on glibc 2.31 both provided by the system and the AppImage, and both Debug and Release versions of AppRun:

#
# 86Box v3.2 logfile, created 2022/02/13 16:35:56
#
# VM: 86Box
#
# Emulator path: /tmp/.mount_test.ABJfmNC/usr/local/bin/
# Userfiles path: /home/richard/86Box/
# ROM path: /home/richard/86Box/roms/
# Configuration file: /home/richard/86Box/86box.cfg
#

APPRUN_HOOK_DEBUG: fopen "/home/richard/86Box/86box.cfg"
APPRUN_HOOK_DEBUG: fopen "/home/richard/86Box/86box.cfg"

Thread 1 "86Box" received signal SIGSEGV, Segmentation fault.
0xf62d6e0a in _IO_getwline_info (fp=0x58eb9f30, buf=0xffff9d10 L"", n=1023, delim=10, extract_delim=1, eof=0x0) at iogetwline.c:58
58      iogetwline.c: No such file or directory.
(gdb) bt
#0  0xf62d6e0a in _IO_getwline_info (fp=0x58eb9f30, buf=0xffff9d10 L"", n=1023, delim=10, extract_delim=1, eof=0x0) at iogetwline.c:58
#1  0xf62d6f5a in _IO_getwline (fp=0x58eb9f30, buf=0xffff9d10 L"", n=1023, delim=10, extract_delim=1) at iogetwline.c:35
#2  0xf62d69f1 in fgetws (buf=buf@entry=0xffff9d10 L"", n=n@entry=1024, fp=fp@entry=0x58eb9f30) at iofgetws.c:53
#3  0x565f97c7 in config_read (fn=<optimized out>) at /86Box/src/config.c:331
#4  0x565ff725 in config_load () at /86Box/src/config.c:2063
#5  0x565f7eff in pc_init (argc=<optimized out>, argv=<optimized out>) at /86Box/src/86box.c:720
#6  0x569c89a4 in main (argc=<optimized out>, argv=0xffffc4f4) at /86Box/src/qt/qt_main.cpp:160

The relevant config_read function is here (line 303). Some debugging with gdb reveals that the segfault is caused by an access to fp->_wide_data, because the FILE structure returned by fopen is somehow bogus (I've had it end up in the middle of a Qt5-related string on other occasions):

(gdb) p *fp
$1 = {_flags = -72539000, _IO_read_ptr = 0x0, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0, _IO_write_end = 0x0,
  _IO_buf_base = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0,
  _chain = 0xf644ac80 <_IO_2_1_stderr_>, _fileno = 8, _flags2 = 128, _old_offset = -1, _cur_column = 0, _vtable_offset = -72 '\270', _shortbuf = "",
  _lock = 0x58eef4e8, _offset = 4131698336, _codecvt = 0x0, _wide_data = 0x0, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0, _mode = -1,
  _unused2 = '\000' <repeats 39 times>}
(gdb) p *fp->_wide_data
Cannot access memory at address 0x0
(gdb) dump memory dump.bin fp fp+1
(gdb) shell xxd dump.bin
00000000: 8824 adfb 0000 0000 0000 0000 0000 0000  .$..............
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 80ac 44f6 0800 0000 8000 0000  ......D.........
00000040: ffff ffff 0000 b800 e8f4 ee58 a0b6 44f6  ...........X..D.
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 ffff ffff 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 

If the config reading code is skipped (by 86box.cfg being absent), the config saving code (line 417) doesn't segfault, but the resulting 86box.cfg file ends up empty, because all calls to fwprintf return -1 (with no errno set) due to the bogus FILE. While debugging this issue, I recall inserting a standard non-wchar fprintf into that code, and that did work but fwprintf did not.

All this weird file behavior doesn't happen when the 86Box binary is executed without AppRun, either directly or through a manually-generated AppImage, or with AppRun on non-i386 architectures.

azubieta commented 2 years ago

AppRun uses libapprun_hooks to intercept libc functions that takes a file path as argument to allow patching it at runtime. The bug should be there. I'm currently involved on a big fix of AppRun so it would take me a week or two to get to this issue. But if you want to jump into debugging and patching the code I could assist you.

richardg867 commented 2 years ago

Small update: this was apparently fixed in our project by defining _FILE_OFFSET_BITS=64, _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE on i386 builds. I'm keeping this issue open, though, as it could theoretically affect anything not compiled with 64-bit file support.