[5.2][Jolla Phone 2026] WireGuard VPN crashes other network connections

REPRODUCIBILITY: Always
OS VERSION: 5.2
HARDWARE: Jolla Phone 2026
UI LANGUAGE: English UK
REGRESSION:

DESCRIPTION:

New Jolla Phone 2026, running SFOS 5.2.0.13 (problem exists in 5.2.0.11 also). As soon as I add my (personal) WireGuard VPN configuration via the Settings app, both the mobile data and wifi connection configurations become impossible to interact with. The toggles for mobile data and wifi in the top menu no longer function.

Furthermore, the WLAN configuration page shows “WLAN adapter not available” and the Mobile network configuration page shows internet → disabled. Both WLAN and mobile data do still work, but their configuration pages are no longer usable.

Furthermore furthermore, the WireGuard settings page shows “connected” even if I tap to disconnect the VPN, and it is impossible to remove the VPN config unless I remove the SIM and prevent wifi access first.

There is nothing unusual about my WireGuard setup. It’s a perfectly simple /32 assigned to my phone, used for email etc. I am not attempting to route all traffic, just traffic to my own VPN hosts, although I do not believe this is relevant to the problem.

I have reproduced this behavior on a friend’s phone, and he investigated a couple of steps further and reports that connmand (via connman.service) is core dumping.

Unfortunately this new phone is unusable to me until this bug is fixed. :frowning:

PRECONDITIONS:

STEPS TO REPRODUCE:

  1. Add WireGuard config to phone

EXPECTED RESULT:

WireGuard connection should come up, and WLAN and mobile data connections should remain usable and interactible via the UI.

ACTUAL RESULT:

WireGuard connection does come up, but WLAN and mobile data connections show as “off” in UI, and cannot be toggled on. Furthermore WLAN and mobile data configuration screens in Settings app become unusable.

MODIFICATIONS:

None.

ADDITIONAL INFORMATION:

My (obfuscated) WireGuard config file:

[Interface]
PrivateKey = myprivatekey=
Address = 10.8.2.24/32
[Peer]
PublicKey = serverpublickey=
Endpoint = servername:11945
AllowedIPs = 10.8.2.0/24

Output from my friend’s investigation:

[root@JollaPhone2026 defaultuser]# systemctl status connman.service | cat
● connman.service - Connection service
Loaded: loaded (/usr/lib/systemd/system/connman.service; disabled; vendor preset: enabled)
Drop-In: /usr/lib/systemd/system/connman.service.d
└─01-prevent-start.conf, 01-require-home-mount-to-be-present.conf
Active: failed (Result: core-dump) since Fri 2026-07-10 22:00:46 EEST;
2h 18min ago
Process: 26614 ExecStart=/usr/sbin/connmand -n -W nl80211 --nobacktrace
--noplugin=wifi $SYSCONF_ARGS $CONNMAN_ARGS (code=dumped, signal=SEGV)
Main PID: 26614 (code=dumped, signal=SEGV)

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.
[root@JollaPhone2026 defaultuser]#
5 Likes

Little tidbit to add here:

If I install wireguard-tools via pkcon and bring up the WireGuard connection manually using wg-quick, everything seems fine (including setting the proper routes, which has been a problem with the official interface). So the problem here is somewhere in connman or how Sailfish interfaces with it, not with the underlying WireGuard implementation.

5 Likes

I had similar issues. I could connect to WLAN or the SIM card, but not both. This was also causing extra battery usage & I had a lot of trouble with inbound connections for wg0 even when it was working.

Same device here (Jolla Phone jp2601, SFOS 5.2.0.15) — I dug into this and can confirm the connmand SIGSEGV, characterise when it happens, and give a recovery that doesn’t need a reboot.

Confirmed crash. systemctl status connman after the VPN goes up:

Active: failed (Result: core-dump) … Main PID: … (code=dumped, signal=SEGV)

So it is genuinely connmand core-dumping, as reported above — not WireGuard itself (kernel WG / wg-quick are fine).

It’s connman’s WireGuard state-handling that crashes, not the endpoint. I initially thought the trigger was an unroutable endpoint (my mobile carrier is IPv6-only, and a dual-stack endpoint hostname resolves to the IPv4 A-record, which is unroutable there — a separate issue). But I also tested a cleanly-establishing IPv6 endpoint (AAAA-only hostname) that completes a real handshake and passes traffic — and connmand still SIGSEGVs around the connect/disconnect activity. So the crash is in connman’s handling of the WG VPN connection state, independent of whether the tunnel can route.

Why it looks like it “works but breaks everything else”. After connmand dies, the kernel keeps the WG interface, addresses and routes, so traffic keeps flowing — the VPN appears connected and you can even reach the remote LAN. But the connman control plane is dead, which is exactly the reported symptom set: WiFi/mobile-data toggles frozen, a false “mobile data disabled”, and DBus calls failing with net.connman was not provided by any .service files.

It crash-loops while the provider is configured. systemctl restart connman brings it back, but it immediately chokes on the still-configured WG connection and SEGVs again (failed). So a reboot alone doesn’t help if the tunnel is set to reconnect.

Recovery without a reboot (the WG provider daemon connman-vpn stays alive and can remove it):

# find the connection path:
dbus-send --system --print-reply --dest=net.connman.vpn / \
    net.connman.vpn.Manager.GetConnections
# remove it:
dbus-send --system --print-reply --dest=net.connman.vpn / \
    net.connman.vpn.Manager.Remove objpath:/net/connman/vpn/connection/<id>
# then clear the failed state and restart the core daemon:
systemctl reset-failed connman && systemctl restart connman

After that connmand stays up and WiFi/mobile come back. (The Settings UI “Forget” refuses while the VPN is connected, and net.connman.vpn.Connection.Disconnect returns PermissionDenied from a non-session context — Manager.Remove is what works.)

Net: on this device a WireGuard VPN is effectively unusable — it takes connmand down (SIGSEGV) on connect, and only “works” via the orphaned kernel routes while the control plane is dead. The real fix is for connman to handle the WG connection state without dereferencing whatever it’s dereferencing on SEGV.

Environment: Jolla Phone jp2601, HW 1.0.0.13, SoC MT6858, kernel 6.12.38-4k, SFOS 5.2.0.15. Happy to capture a coredump/backtrace of connmand if a dev wants one (note: coredumpctl isn’t present by default, but the core is dumped per systemctl status).

2 Likes

I cannot reproduce this, so if you can, first, as root user (devel-su) install debuginfo for ConnMan:
pkcon install connman-debuginfo
then run ConnMan with gdb:
gdb -p $(pidof connmand)
and reproduce the issue, the printed backtrace should contain enough info when the crash happens.

1 Like

Got it, @jlaakkonen — backtrace below, and I think it pins the root cause. gdb attached to the running connmand with connman-debuginfo installed, so the connman frames have full symbols.

Short version: NULL-pointer dereference in get_gateway_cb(), caused by an address-family mismatch. The VPN’s gateway_config is stored in the ipv4_gateway slot even though the endpoint is IPv6, while ipv6_gateway stays NULL — and the RTNL callback then dereferences the IPv6 one.

The crash

Thread 1 "connmand" received signal SIGSEGV, Segmentation fault.
0x000000000045afd4 in get_gateway_cb (gateway=<optimized out>, index=16,
                                      user_data=0x55ae160) at src/connection.c:171
171		config->vpn_phy_index = index;
        config = 0x0

si_addr confirms the offset — vpn_phy_index is at +0x20 in struct gateway_config:

si_signo = 11, si_code = 1 (SEGV_MAPERR)
_sigfault = { si_addr = 0x20 }

and at the instruction level, a store through a NULL base register:

=> 0x45afd4 <get_gateway_cb+208>:	str	w24, [x1, #32]
x1  = 0x0      (config)
x24 = 0x10     (index = 16)

Backtrace

#0  get_gateway_cb (gateway=<optimized out>, index=16, user_data=0x55ae160) at src/connection.c:171
        config = 0x0
        data = 0x55a5270
        params = 0x55ae160
#1  get_route_cb (answer=0x7fc18c62e8, user_data=0x5579970) at src/inet.c:2901
        index = 16
        addr = 0x7fc18c6188 "fe80::1"
#2  inet_rtnl_recv (chan=0x55a54f0, rtnl_data=0x55ae960) at src/inet.c:2727
#3  inet_rtnl_event (chan=0x55a54f0, cond=G_IO_IN, user_data=0x55ae960) at src/inet.c:2746
#4  0x…294 in ?? () from /usr/lib64/libglib-2.0.so.0
#5  0x…bdc in ?? () from /usr/lib64/libglib-2.0.so.0
#6  g_main_loop_run () from /usr/lib64/libglib-2.0.so.0
#7  main (argc=<optimized out>, argv=<optimized out>) at src/main.c:461

Frame #1 is data->callback(addr, index, data->user_data) with

$13 = { callback = 0x45af04 <get_gateway_cb>, user_data = 0x55ae160 }

The interesting part — the state at the crash

p *data  ->  (struct gateway_data *) 0x55a5270
{
  index4 = 44,
  index6 = 0,
  service = 0x55a4bc0,
  ipv4_gateway = 0x558be60,      <-- non-NULL
  ipv6_gateway = 0x0,            <-- NULL  ... and this is the one used
  default_checked = false
}

and the ipv4_gateway it did populate contains an IPv6 address:

p *data->ipv4_gateway
{
  active = false,
  gateway = 0x559daf0 "<IPV6-ENDPOINT>",
  vpn = true,
  vpn_ip = 0x55ac1c0 "<IPV6-ENDPOINT>",
  vpn_phy_index = -1,
  vpn_phy_ip = 0x0
}

p *params
{
  vpn_gateway = 0x55bc610 "<IPV6-ENDPOINT>",
  vpn_index = 44
}

The lookup and the family-selection contradict each other

disassemble /s get_gateway_cb on the live process (offsets confirm the fault: ipv4_gateway is at +0x10, ipv6_gateway at +0x18, vpn_phy_index at +0x20 = si_addr):

155		data = find_vpn_gateway(params->vpn_index, params->vpn_gateway);
   …
123			if (data->ipv4_gateway && data->index4 == index &&
124					g_str_equal(data->ipv4_gateway->gateway, gateway))
   …
162		family = connman_inet_check_ipaddress(params->vpn_gateway);
164		if (family == AF_INET)
165			config = data->ipv4_gateway;
166		else if (family == AF_INET6)
167			config = data->ipv6_gateway;
   0x45afc8 <+196>:	ldr	x1, [x19, #24]      <- reads ipv6_gateway (+0x18) = NULL
168		else
169			goto out;
171		config->vpn_phy_index = index;
   0x45afd4 <+208>:	str	w24, [x1, #32]      <- NULL + 0x20  => SIGSEGV

So on this connection:

  • find_vpn_gateway() found the entry by matching data->index4 == params->vpn_index (44) and g_str_equal(data->ipv4_gateway->gateway, params->vpn_gateway) — i.e. it identified it through the IPv4 slot;
  • line 162 then classifies that same string with connman_inet_check_ipaddress(), which returns AF_INET6 (it is an IPv6 literal);
  • so line 167 selects data->ipv6_gateway, which was never populated → NULL → line 171 writes through it.

The lookup and the family-selection therefore disagree about which slot describes this connection. Either the gateway shouldn’t have been stored in ipv4_gateway in the first place, or line 162 shouldn’t re-derive the family independently of the slot the lookup just matched — you’ll know which. Separately, line 171 has no NULL check, so any such disagreement is a crash rather than a logged error.

(For clarity about what’s evidence vs inference: the struct dumps, the disassembly and the offsets are observed; family itself was <optimized out> in both crash runs, so the AF_INET6 path is inferred from connman_inet_check_ipaddress() on an IPv6 literal plus the fact that the faulting instruction loads from +0x18, which is ipv6_gateway.)

Reproduction

Reproduced on demand, twice with gdb attached — identical backtrace both times (same frames, same config = 0x0, same addr = "fe80::1", index = 16). The struct dumps above are from the second run, where I’d added them to the capture:

  1. WireGuard VPN provider whose endpoint resolves to an IPv6 address and is reachable on the current transport. Mine: an AAAA-only hostname over an IPv6-only mobile carrier, so the tunnel genuinely establishes and passes traffic.
  2. Settings → VPN → connect.
  3. UI cycles “waiting for network” → “idle” → “connected” and settles on “connected” — connmand SEGVs during that churn.

Correction to my earlier post in this thread: I previously wrote that the crash was independent of whether the tunnel could route. That was wrong, and the distinction turns out to matter. With the same provider on an IPv4-only WiFi — where the AAAA-only endpoint is unroutable — it does not crash; connman just loops

connmand: Connect reply: No route to host (net.connman.Error.Failed)

which I watched repeat every ~2 s for ~17 s with no back-off. The crash needs the tunnel to actually come up so the gateway/route lookup happens. Sorry for the noise — I’d conflated two different failure modes.

Two side effects worth knowing

  • Crash loop. After the SEGV, systemd restarts connmand, the new instance hits the same residual state and SEGVs immediately, ending in Start request repeated too quicklyfailed. (Worth knowing if you ask anyone for logs: the journal here is Storage=volatile, so the crashed instance’s own lines rotate away within minutes.)
  • Leaked interfaces. Each crash leaves the WireGuard netdev behind — after a few cycles I had wg0wg3 all sitting DOWN — because connmand dies before cleaning up. The kernel keeps the addresses and routes, which is exactly why the VPN can look like it’s “working” while the connman control plane is dead (frozen WiFi/mobile toggles, net.connman was not provided by any .service files).

Recovery without a reboot, for anyone else hitting this — connman-vpn survives, so it can remove the provider:

dbus-send --system --print-reply --dest=net.connman.vpn / \
    net.connman.vpn.Manager.Remove objpath:/net/connman/vpn/connection/<id>
systemctl reset-failed connman && systemctl restart connman
ip link del wg0   # repeat for any leftovers

Environment: Jolla Phone jp2601, HW 1.0.0.13, SoC MT6858, kernel 6.12.38-4k, SFOS 5.2.0.15, connman-1.40+git23-1.40.1.jolla.aarch64.

I’ve got the repro scripted and the trap re-armable in a minute, so if you want anything else captured — a watchpoint on ipv6_gateway, the state at allocation time, or a patched build tested — just say and I’ll run it.

2 Likes

Thank you for all the details. I’ll see if the root cause is easy to find, or if a quick fix for now would suffice and leave a task to solve the issue later.

2 Likes

I found at least something that can be related to this but since I cannot reproduce the issue with my setup I’m not yet 100% sure if it is the fix for the root cause. But you can add my community OBS repository for WG fixes and try it out. That has a fix for the WG import issue you reported, and this attempt to fix the crash in ConnMan.

Step-by-step instructions:

  1. ssh as root to device or use devel-su in terminal
  2. Install zypper for convenience pkcon install zypper
  3. Add the repo:
    ssu ar wg-fixes https://repo.sailfishos.org/obs/home:/jlaakkonen:/wireguard_fixes/aarch64/
  4. Refresh and if you’re brave, upgrade all from the repo: zypper ref wg-fixes && zypper dup --from wg-fixes

These packages are to be updated:
connman connman-plugin-vpn-l2tp connman-plugin-vpn-openconnect connman-plugin-vpn-openfortivpn connman-plugin-vpn-openvpn connman-plugin-vpn-pptp connman-plugin-vpn-vpnc connman-plugin-vpn-wireguard connman-vpn-scripts nemo-qml-plugin-connectivity

So if you just want to test the import fix (and not to install zypper), install nemo-qml-plugin-connectivity: pkcon install nemo-qml-plugin-connectivity.

After installing you need to either restart the phone, or “do you feel lucky, well do you, root?” systemctl restart connman && systemctl-user restart lipstick.

1 Like

Excellent! thanks I’ll try it and report back but you’ll have to give me some time as holidays may “get in the way” :slight_smile: I’ll let you know how it goes!

3 Likes

I added some ConnMan fixes for the route adding to the same OBS repo as well since you reported them in an another bug post. I just put all the changes into the same branch so all changes are getting built for testing. The previous instructions apply.

Apparently with WireGuard the endpoint should not be used as the route gateway. It becomes unreachable when adding the route and kernel refuses to do so.

When you, or anyone else having interest in improving WireGuard on Sailfish OS, have time try those out and let me know how they work.

1 Like

Hi, I also had issue with wire guard connection and troubleshooted for hours! Here was my situation and solution:
My WireGuard VPN (FritzBox → Jolla phone) worked on WLAN but not on cellular. The myFritz hostname resolves to both IPv6 and IPv4. Sailfish’s WireGuard plugin picks the IPv6 address first and never falls back — and over mobile data the IPv6 path was unreachable. So the tunnel never established, and since the VPN’s default route sent all traffic into the dead tunnel, nothing worked. (Works still fine on my old phone and iPad).
If I imported a wireguard config file where the endpoint clearly showed to the IPv4 address it worked (instead of MYDOMAIN@.myfirtz.net:PORT)

Solution:
(1) Enabled Developer Mode → terminal as root (devel-su).
(2) Installed diagnostics tooling: zypper install wireguard-tools.
(3) Confirmed the cause with wg show wg0 (IPv6 endpoint, handshake but data dropped).
(4) Added IPv4 preference to /etc/gai.conf:
label ::ffff:0:0/96 4
precedence ::ffff:0:0/96 100
(5)Rebooted the phone — this cleared the plugin’s cached IPv6 resolution (that was the key step; without the reboot, the old IPv6 result was reused).
(6)Verified on cellular: wg show wg0 shows an IPv4 endpoint, ping and DNS work.

Not sure if anybody else has the same issue.