• Bug#1109272: python3-dbusmock: Possible race condition when setting up

    From Simon McVittie@21:1/5 to All on Mon Jul 14 15:10:01 2025
    Package: python3-dbusmock
    Version: 0.34.3-1
    Severity: important
    Tags: unreproducible
    Control: affects -1 + src:mutter
    User: [email protected]
    Usertags: flaky

    After my recent upload of mutter 48.4-2 to unstable, its autopkgtest
    failed on ppc64el. At least one of the failures (monitor-unit.test) had
    this logged, and the test subsequently timed out:

    https://ci.debian.net/packages/m/mutter/testing/ppc64el/62167381/ https://ci.debian.net/data/autopkgtest/testing/ppc64el/m/mutter/62167381/artifacts.tar.gz
    Starting mocked services...
    Traceback (most recent call last):
    File "/usr/libexec/installed-tests/mutter-16/mutter-installed-dbus-session.py", line 22, in <module>
    result = meta_run(MutterDBusRunner,
    extra_env=extra_env,
    setup_argparse=add_extra_args,
    handle_argparse=handle_extra_args)
    File "/usr/share/mutter-16/tests/mutter_dbusrunner.py", line 325, in meta_run
    return run_test_mocked(klass, rest,
    launch=args.launch,
    bind_sockets=True,
    extra_env=extra_env)
    File "/usr/share/mutter-16/tests/mutter_dbusrunner.py", line 271, in run_test_mocked
    klass.setUpClass(launch=launch,
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
    bind_sockets=bind_sockets)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/usr/share/mutter-16/tests/mutter_dbusrunner.py", line 67, in setUpClass
    (klass.mocks_manager, klass.mock_obj) = klass.start_from_local_template(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
    'meta-mocks-manager', {'templates-dir': klass.__get_templates_dir()})
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/usr/share/mutter-16/tests/mutter_dbusrunner.py", line 119, in start_from_local_template
    return klass.start_from_template(template, params, system_bus=system_bus)
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/usr/share/mutter-16/tests/mutter_dbusrunner.py", line 108, in start_from_template
    klass.spawn_server_template(template,
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
    params,
    ^^^^^^^
    get_subprocess_stdout(),
    ^^^^^^^^^^^^^^^^^^^^^^^^
    system_bus=system_bus)
    ^^^^^^^^^^^^^^^^^^^^^^
    File "/usr/lib/python3/dist-packages/dbusmock/testcase.py", line 438, in spawn_server_template
    server = SpawnedMock.spawn_with_template(template, parameters, bustype, stdout, stderr=None)
    File "/usr/lib/python3/dist-packages/dbusmock/testcase.py", line 614, in spawn_with_template
    server = SpawnedMock.spawn_for_name(module.BUS_NAME, module.MAIN_OBJ, interface_name, bustype, stdout, stderr)
    File "/usr/lib/python3/dist-packages/dbusmock/testcase.py", line 557, in spawn_for_name
    bus = bustype.get_connection()
    File "/usr/lib/python3/dist-packages/dbusmock/testcase.py", line 58, in get_connection
    return dbus.bus.BusConnection(val)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^
    File "/usr/lib/python3/dist-packages/dbus/bus.py", line 120, in __new__
    bus = cls._new_for_bus(address_or_type, mainloop=mainloop) dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoServer: Failed to connect to socket /tmp/dbusmock_data_nh20oy7q/system_bus.socket: Connection refused

    I think this indicates that dbusmock started a dbus-daemon to be the
    mock system bus, but then attempted to connect to the socket before the dbus-daemon was listening on that socket. There is a (very) short window
    of time between bind() and listen() during which connection attempts
    will fail.

    If my theory is correct, this will be difficult to reproduce on-demand:
    you would have to be very unlucky with how processes get scheduled.

    dbus-daemon provides two mechanisms to avoid this race condition window:

    1. If you run it with --print-address=FD and/or --print-pid=FD, and then
    read until EOF on the given file descriptor(s) (often 1 for stdout, but
    may be any writeable fd), then the read will block until after the
    dbus-daemon has called listen(), closing the race condition. This is how dbus-run-session(1) is implemented.

    2. Or, if you run it with --fork, it will "daemonize" and the foreground process will not exit until the background process has called listen().
    This is how dbus-launch(1) is implemented.

    I would recommend (1.), blocking on a read() until --print-address or --print-pid has produced its result (you can ignore the printed address
    or pid if you don't need it). For example test/gvfs-test in
    gvfs_1.57.2-2 and tests/test-dconf.py in dconf_0.40.0-5 both do this, in
    a way that should be compatible with dbusmock's use of subprocess.Popen.

    Thanks,
    smcv

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)