fix: non-blocking notify, NUL-separated process args, NULL string guards, watch TOCTOU, app-exec return
ober
1a866ad24c058b4e5f0dcc05858e658b2ac683bb
--- a/jerboa_qt_lifecycle.cpp +++ b/jerboa_qt_lifecycle.cpp @@ -45,25 +45,17 @@ extern "C" void jerboa_qt_watch_destroyed(void *object, long callback_id) { } auto *qobject = static_cast<QObject *>(object); - bool install_connection = false; - { - std::lock_guard<std::mutex> guard(watch_mutex); - auto &state = watched_objects[qobject]; - auto &ids = state.ids; - install_connection = ids.empty(); - if (std::find(ids.begin(), ids.end(), callback_id) == ids.end()) { - ids.push_back(callback_id); - } + std::lock_guard<std::mutex> guard(watch_mutex); + auto &state = watched_objects[qobject]; + auto &ids = state.ids; + const bool install_connection = ids.empty(); + if (std::find(ids.begin(), ids.end(), callback_id) == ids.end()) { + ids.push_back(callback_id); } if (install_connection) { - auto connection = QObject::connect( + state.connection = QObject::connect( qobject, &QObject::destroyed, [qobject]() { enqueue_destroyed_ids(qobject); }); - std::lock_guard<std::mutex> guard(watch_mutex); - auto found = watched_objects.find(qobject); - if (found != watched_objects.end()) { - found->second.connection = connection; - } } } @@ -129,8 +121,10 @@ extern "C" void jerboa_qt_notify_destroying_tree(void *object) { } else { Sdeactivate_thread(); const bool invoked = QMetaObject::invokeMethod( - root, collect, Qt::BlockingQueuedConnection); - (void)Sactivate_thread(); + root, collect, Qt::QueuedConnection); + if (Sactivate_thread() != 0) { + return; + } if (!invoked) { return; } --- a/jerboa_qt_shim.c +++ b/jerboa_qt_shim.c @@ -381,3 +381,19 @@ void jerboa_qt_scintilla_on_modified(void* s, long callback_id) { qt_scintilla_on_modified(s, chez_void_callback, callback_id); } #endif + +/* NULL-safe string getters: the Chez FFI string return type cannot represent + * NULL. These wrappers substitute an empty string so the Scheme side never + * receives a dangling pointer. */ +const char *jerboa_qt_label_text(void *label) { + const char *s = qt_label_text(label); + return s ? s : ""; +} +const char *jerboa_qt_push_button_text(void *button) { + const char *s = qt_push_button_text(button); + return s ? s : ""; +} +const char *jerboa_qt_line_edit_text(void *edit) { + const char *s = qt_line_edit_text(edit); + return s ? s : ""; +} --- a/lib/jerboa-qt/ffi.sls +++ b/lib/jerboa-qt/ffi.sls @@ -1399,7 +1399,7 @@ void) (define-qt-ffi ffi-qt-label-text - "qt_label_text" + "jerboa_qt_label_text" (void*) string) (define-qt-ffi @@ -1429,7 +1429,7 @@ void) (define-qt-ffi ffi-qt-push-button-text - "qt_push_button_text" + "jerboa_qt_push_button_text" (void*) string) (define-qt-ffi @@ -1454,7 +1454,7 @@ void) (define-qt-ffi ffi-qt-line-edit-text - "qt_line_edit_text" + "jerboa_qt_line_edit_text" (void*) string) (define-qt-ffi --- a/lib/jerboa-qt/qt.sls +++ b/lib/jerboa-qt/qt.sls @@ -790,16 +790,18 @@ (require-qt-owner! 'qt-pump-events!) (ffi-qt-app-process-events app) (qt-dispatch-events!)) - (def (qt-app-exec! app . args) (require-qt-owner! 'qt-app-exec!) - (ffi-qt-app-exec app) - (let ([tick (if (null? args) #f (car args))]) - (let loop () - (when (= (ffi-qt-app-is-running) 1) - (ffi-qt-app-process-events app) - (qt-dispatch-events!) - (when tick (tick)) - (sleep-ms 10) - (loop)))) + (def (qt-app-exec! app . args) + (require-qt-owner! 'qt-app-exec!) + (let ([tick (if (null? args) #f (car args))] + [rc (ffi-qt-app-exec app)]) + (when (= rc 0) + (let loop () + (when (= (ffi-qt-app-is-running) 1) + (ffi-qt-app-process-events app) + (qt-dispatch-events!) + (when tick (tick)) + (sleep-ms 10) + (loop))))) (qt-dispatch-events!)) (def (qt-app-quit! app) (require-qt-owner! 'qt-app-quit!) @@ -2605,7 +2607,7 @@ [(parent) (ffi-qt-process-create (or parent 0))])) (def (qt-process-start! proc program args) (let ([args-str (if (list? args) - (string-join args "\n") + (string-join args (string #\nul)) args)]) (ffi-qt-process-start proc program args-str))) (def (qt-process-write! proc data) --- a/src/jerboa-qt/ffi.ss +++ b/src/jerboa-qt/ffi.ss @@ -1123,7 +1123,7 @@ (define-qt-ffi ffi-qt-label-set-text "qt_label_set_text" (void* string) void) - (define-qt-ffi ffi-qt-label-text "qt_label_text" (void*) string) + (define-qt-ffi ffi-qt-label-text "jerboa_qt_label_text" (void*) string) (define-qt-ffi ffi-qt-label-set-alignment "qt_label_set_alignment" (void* int) void) @@ -1139,7 +1139,7 @@ (define-qt-ffi ffi-qt-push-button-set-text "qt_push_button_set_text" (void* string) void) - (define-qt-ffi ffi-qt-push-button-text "qt_push_button_text" (void*) string) + (define-qt-ffi ffi-qt-push-button-text "jerboa_qt_push_button_text" (void*) string) (define-qt-ffi ffi-qt-push-button-on-clicked "jerboa_qt_push_button_on_clicked" (void* long) void) @@ -1153,7 +1153,7 @@ (define-qt-ffi ffi-qt-line-edit-set-text "qt_line_edit_set_text" (void* string) void) - (define-qt-ffi ffi-qt-line-edit-text "qt_line_edit_text" (void*) string) + (define-qt-ffi ffi-qt-line-edit-text "jerboa_qt_line_edit_text" (void*) string) (define-qt-ffi ffi-qt-line-edit-set-placeholder "qt_line_edit_set_placeholder" (void* string) void) --- a/src/jerboa-qt/qt.ss +++ b/src/jerboa-qt/qt.ss @@ -974,18 +974,16 @@ (def (qt-app-exec! app . args) (require-qt-owner! 'qt-app-exec!) - ;; qt_application_exec is a no-op in the external shim. On Linux the Qt - ;; loop runs on its native pthread; this owner-thread poll drains copied - ;; callbacks without ever attaching that pthread to Chez. - (ffi-qt-app-exec app) - (let ((tick (if (null? args) #f (car args)))) - (let loop () - (when (= (ffi-qt-app-is-running) 1) - (ffi-qt-app-process-events app) - (qt-dispatch-events!) - (when tick (tick)) - (sleep-ms 10) - (loop)))) + (let ((tick (if (null? args) #f (car args))) + (rc (ffi-qt-app-exec app))) + (when (= rc 0) + (let loop () + (when (= (ffi-qt-app-is-running) 1) + (ffi-qt-app-process-events app) + (qt-dispatch-events!) + (when tick (tick)) + (sleep-ms 10) + (loop))))) (qt-dispatch-events!)) (def (qt-app-quit! app) @@ -2745,7 +2743,7 @@ [(parent) (ffi-qt-process-create (or parent 0))])) (def (qt-process-start! proc program args) - (let ([args-str (if (list? args) (string-join args "\n") args)]) + (let ([args-str (if (list? args) (string-join args (string #\nul)) args)]) (ffi-qt-process-start proc program args-str))) (def (qt-process-write! proc data) (ffi-qt-process-write proc data)) (def (qt-process-close-write! proc) (ffi-qt-process-close-write proc))