chardev: socket chardev aborts on async connect failure with `reconnect-ms`
**Note:** This issue report was drafted with assistance from Cline (an AI coding assistant) using the Qwen3.6-27B model. The bug report, root cause analysis, and potential fix below should be reviewed and verified independently. [api_conversation_history.json](/uploads/dd1aa52d84ef1ef183116780f2c6fa4c/api_conversation_history.json) --- ## Bug Description When using a socket chardev in client mode with the `reconnect-ms` option, QEMU aborts (SIGABRT, exit code 134) if the initial connection attempt fails. This makes it impossible to start QEMU when the remote socket is not yet available — which was the primary use case for the `reconnect-ms` option. ## Reproduction ```bash qemu-system-x86_64 \ -nodefaults -nographic \ -chardev socket,id=test,host=127.0.0.1,port=19999,reconnect-ms=10000 \ -serial chardev:test \ -monitor stdio ``` **Result:** QEMU prints the monitor prompt, then immediately aborts with exit code 134 (SIGABRT). **Expected:** QEMU should start successfully, log the connection error, and periodically retry the connection every `reconnect-ms` milliseconds. ## Affected Versions v11.0.0 and current master (commit 405c32d2b1). The regression was introduced in commit 5c102ac9f1 (2026-03-06). ## Potential Fix Removing the `yank_unregister_function()` call from the error path in `qemu_chr_socket_connected()` (chardev/char-socket.c) resolves the crash. After this change, QEMU starts successfully, logs the error message, and continues running with the reconnect timer active. ```diff --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -1128,11 +1128,7 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) if (qio_task_propagate_error(task, &err)) { tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); - if (s->registered_yank) { - yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } check_report_connect_error(chr, err); goto cleanup; } ``` **Note:** This fix has been locally tested and verified, but a full code review is recommended before merging. ## Root Cause Analysis The crash occurs in `qemu_chr_socket_connected()` in `chardev/char-socket.c`. When the async connect task fails, the error handler calls `yank_unregister_function()` for the `QIOChannelSocket`: ```c if (qio_task_propagate_error(task, &err)) { tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); if (s->registered_yank) { yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), char_socket_yank_iochannel, QIO_CHANNEL(sioc)); } check_report_connect_error(chr, err); goto cleanup; } ``` However, `yank_register_function()` for this `sioc` is only called inside `tcp_chr_new_client()`, which is invoked only on a **successful** connection. On the failure path, `tcp_chr_new_client()` is never reached, so the yank function was never registered for this `sioc`. The `yank_unregister_function()` implementation in `util/yank.c` calls `abort()` when the function+opaque pair is not found in the registered list, which triggers the crash. ### Regression History - **2021-01-13** (commit `8ee4480692`): yank feature added to socket chardev. The yank function was registered in `tcp_chr_connect_client_async()` and unregistered in `qemu_chr_socket_connected()`'s error path — both were balanced. - **2026-03-06** (commit `5c102ac9f1`, "chardev: Consolidate yank registration"): Moved `yank_register_function()` from `tcp_chr_connect_client_async()` into `tcp_chr_new_client()` (only called on success). The corresponding `yank_unregister_function()` call in the error path was not removed, breaking the balance. The `if (s->registered_yank)` guard is insufficient: it checks whether the yank _instance_ was registered (set during chardev open), but not whether the yank _function_ was registered for this specific `sioc` (which only happens in `tcp_chr_new_client()` on success). For comparison, `tcp_chr_free_connection()` (the normal disconnect cleanup path) correctly guards the unregister call with an additional `s->sioc` check, which is NULL on the error path. ## GDB Backtrace (debug build) ``` #3 yank_unregister_function (util/yank.c:150) #4 qemu_chr_socket_connected (chardev/char-socket.c:1132) #5 qio_task_complete (io/task.c:200) #6 qio_task_thread_result (io/task.c:114) ```
issue