Skip to content

workloads/cln: patch coverage build to break deadlock - #258

Open
morehouse wants to merge 2 commits into
masterfrom
cln_coverage_deadlock
Open

morehouse wants to merge 2 commits into
masterfrom
cln_coverage_deadlock

Conversation

@morehouse

Copy link
Copy Markdown
Collaborator

It turns out that removing the SIGKILL from lightningd/subd.c can cause CLN to deadlock on shutdown, which holds up coverage reporting. Add the SIGKILL back in, but first attempt to gracefully shutdown by closing the subdaemon socket and waiting 5s for it. The socket close handles most of the deadlock cases while preserving coverage data. The fallback SIGKILL causes some coverage data to be lost but prevents coverage runs from deadlocking forever.

@erickcestari

Copy link
Copy Markdown
Contributor

It turns out that removing the SIGKILL from lightningd/subd.c can cause CLN to deadlock on shutdown, which holds up coverage reporting. Add the SIGKILL back in, but first attempt to gracefully shutdown by closing the subdaemon socket and waiting 5s for it. The socket close handles most of the deadlock cases while preserving coverage data. The fallback SIGKILL causes some coverage data to be lost but prevents coverage runs from deadlocking forever.

I couldn't replicate it locally. Do you have an input that triggers the deadlock case?

@morehouse

Copy link
Copy Markdown
Collaborator Author

I believe this was one of the inputs.
deadlock_input.tar.gz

v0 = LoadFeatures(0x1fcbca28db)
v1 = LoadChainHashFromContext()
v2 = LoadPrivateKey(0x65b9bc2dd1298051a31fce6c41faf1ab1033bccd1ddd94d16644e25617ddf55a)
v3 = LoadPrivateKey(0x7afb6b1e2c6031809b78d6517ebd11bf453c39f9b0fecedbb2561a573b8e8ebc)
v4 = LoadPrivateKey(0x9e44b795a988406bcf4bfe1ab51612e6b113a1d6a0b2ecacfb45d01b77819d6a)
v5 = LoadFeatures(0xf1e432f655317b3d8f2f3e1cfda5)
v6 = LoadPrivateKey(0xc9427a888f179dedd540e070f6f20b7167676babe4627629865b27b8cddf4e24)
v7 = LoadPrivateKey(0xd8e5a86f62426caa69eb73781b39e35d5f1f7388b69f8ff480ce452c490629f7)
v8 = LoadPrivateKey(0xe37f5aa9e943910f0fa40b40f1547b870fabef19660388ea04e95ea6b3ed4527)
v9 = LoadPrivateKey(0x582e68d2ea417b35b616672a1b87a1fc1c100dde28e17f143be722a0e2734b75)
v10 = DerivePoint(v8)
v11 = DerivePoint(v9)
v12 = LoadAmount(14180287)
v13 = LoadFeeratePerKw(1404)
v14 = CreateFundingTransaction(v10, v11, v12, v13)
BroadcastTransaction(v14)
v16 = LookupShortChannelId(v14)
MineBlocks(9)
v18 = BuildChannelAnnouncement(v5, v1, v16, v6, v7, v8, v9)
SendMessage(v18)
v20 = LoadFeatures(0xfda928)
v21 = LoadPrivateKey(0x853c9eba189079db0745cd280fc9550cee293386008f06423ab4337ae15d7e13)
v22 = LoadPrivateKey(0x910ec73a6c1a3e373c02b1ef5f6470aea86d6ad8805dbb093ede4212d70bcbe2)
v23 = LoadTimestamp(737373712)
v24 = LoadBytes(0x9530037002959c0e2f4e7d14c2cc30e915cce35437e1b3689407a1701f6fd9c42ad64e67cddc237a0eacc9bdb343bf93df002fd01e2076acaac168bf7d0adf9f93d7086175c6f3b28ec90e891c79bb5df3df00194497090ababa32438e4ebd81204a11cb0b233ec3c5085049d2b583b1c447a66e633cb16b5bb90a99553d75e11863e1ff708a289b44f2)
v25 = BuildNodeAnnouncement{rgb=0xcf155a, alias=0x5c5e044304483134021d7679453b2913552414335d27406554333c5c5f343261}(v22, v20, v23, v24)
SendMessage(v25)
v27 = LoadPrivateKey(0xcae4b9b92c22b9eaa0bea83f3bb31dbea5401ce6fa62b6d7756fadbbadc56cfc)
v28 = LoadPrivateKey(0x506a64a5c8bc3dbae9aad127b29c9af8b7d7ad1388e270ea6b6f9f3be9498fc0)
v29 = LoadPrivateKey(0x15b4fb63c8f6b5452701c59c8a7959882b7d6a4eb42122461c21a7846da22e56)
v30 = DerivePoint(v28)
v31 = DerivePoint(v29)
v32 = LoadAmount(13956843)
v33 = LoadFeeratePerKw(1906)
v34 = CreateFundingTransaction(v30, v31, v32, v33)
BroadcastTransaction(v34)
MineBlocks(15)
v37 = LookupShortChannelId(v34)
v38 = BuildChannelAnnouncement(v20, v1, v37, v22, v27, v28, v29)
v39 = LoadPrivateKey(0x20f107377feacbe9d8041135f57b671d069c2ec216abdd40d412d71d6199eb76)
v40 = DerivePoint(v39)
v41 = LoadPrivateKey(0x058e20345ae1a5b02c9abd4be7105e63efcd304289ef9466a274be41eb219407)
v42 = LoadPrivateKey(0x5878425382f785b52f89e90ce37cb216293a5c440615aa6a317dc6e672156171)
v43 = DerivePoint(v42)
v44 = LoadPrivateKey(0x5c5fd10da7088ccd85a32c690efb9aea5cdd205e484d7a02bbdd84b6ca7b059a)
v45 = DerivePoint(v44)
v46 = LoadPrivateKey(0xdcca3e2651c7caf0875179271ffe12837ecb9da44e39e7a18bef9329d0530c68)
v47 = DerivePoint(v46)
v48 = LoadPrivateKey(0x162445726b21a646b720f3773a2e4d50fdcca8b88d01e62d3deb00d7d2ead038)
v49 = DerivePoint(v48)
v50 = LoadChannelId(0x4b8836559044977d4f5222151b6776d95d561b0f11747d18e3cf1c73227be2c5)
v51 = LoadAmount(9090555)
v52 = LoadAmount(3638055541)
v53 = LoadAmount(398)
v54 = LoadAmount(5960636960)
v55 = LoadAmount(28839)
v56 = LoadAmount(1001677149)
v57 = LoadFeeratePerKw(22359)
v58 = LoadU16(1737)
v59 = LoadU16(11)
v60 = LoadU8(0)
v61 = LoadShutdownScript(Empty)
v62 = LoadChannelType(StaticRemoteKeyScidAlias)
v63 = BuildOpenChannel(v1, v50, v51, v52, v53, v54, v55, v56, v13, v58, v59, v40, v30, v43, v45, v47, v49, v60, v61, v62)
v64 = SendOpenChannel(v63)
v65 = RecvAcceptChannel(v64)
v66 = ExtractFundingPubkey(v65)
v67 = CreateFundingTransaction(v40, v66, v51, v57)
BroadcastTransaction(v67)
v69 = SendFundingCreated(v67, v39, v50)
v70 = RecvFundingSigned(v69)
MineBlocks(6)
v72 = LoadPrivateKey(0x6b26023d8bc770e2f5ce01782cf556784cf44c8d46e847bce3956379fb50b055)
v73 = DerivePoint(v72)
v74 = LoadShortChannelId(7012180x8825230x41674)
SendChannelReady{include_alias=false}(v70, v73, v74)
RecvChannelReady()
v77 = ExtractChannelType(v65)
v78 = LoadTimestamp(1448265637)
v79 = ExtractUpfrontShutdownScript(v65)
v80 = BuildNodeAnnouncement{rgb=0x17df3b, alias=0x25755460127a0b382e4a0d00470743437c0e044c6a3b194750420e683a5d6470}(v22, v77, v78, v79)
SendMessage(v80)
SendMessage(v38)
v83 = LoadPrivateKey(0x7a80149a9a9a9a9a9a0d98cee0e49c3e5ce584d4c1ac58f189338d1a7aece0fd)
v84 = LoadPrivateKey(0x6dbdd098ec7384609522af655758f686e70457771d09694763c678d56e39dbef)
v85 = DerivePoint(v84)
v86 = LoadAmount(6428373)
v87 = LoadFeeratePerKw(2809)
v88 = CreateFundingTransaction(v85, v30, v86, v87)
MineBlocks(4)
v90 = LookupShortChannelId(v88)
MineBlocks(6)
v92 = BuildChannelAnnouncement(v20, v1, v90, v21, v83, v84, v84)
SendMessage(v80)
v94 = LoadPrivateKey(0x6f6516312e7deefc60c379d2def7960f8864497b6fe9109be93f97c297ad936f)
v95 = DerivePoint(v4)
v96 = DerivePoint(v94)
v97 = LoadAmount(3368273)
v98 = LoadFeeratePerKw(5670)
v99 = CreateFundingTransaction(v95, v96, v97, v98)
BroadcastTransaction(v99)
BroadcastTransaction(v88)
v102 = LookupShortChannelId(v99)
v103 = LoadTimestamp(2246159973)
v104 = LoadBytes(0xa278ac5009fd9703899009e2ec71ae6674e5c3e2f8f5e5d28ee71b7c68ed11adf43d92ff0dae6f5ddd4180b0f2f8fc3005079f6fa548a1c833b9c1376b33a0430c53ae632afe2adde4decdf51fb9b3371634503260a617cdff64c1e4d61f67adc70c7269dac66f4774c42115d78546d72eb97e408ea1216437b26ea91ddcbad7aa268314868e1e12aae9adb6a57b9878f6e9897f1a4e55ecf6f5bb3635f4d0d807f221b7f193fc657929cb83a6a5261f7d2fa362d2c56bd7ce20ad0d2060eb459ae9e47d4daa2e42e492ba23750fdd2660a1f06963e07d20633d855e1708782cb4483410c1150cf9e79ea10207481398623a5bc5af35ce88e6)
v105 = BuildNodeAnnouncement{rgb=0x469868, alias=0x616b6f523639400c080444562776403d74100e264f05130942362c142a04274f}(v94, v20, v103, v104)
SendMessage(v105)
v107 = BuildChannelAnnouncement(v0, v1, v102, v2, v3, v4, v94)

@erickcestari

Copy link
Copy Markdown
Contributor

I couldn't replicate it locally. I've run it 200 times and didn't deadlock.

It turns out that removing the SIGKILL from lightningd/subd.c can cause
CLN to deadlock on shutdown, which holds up coverage reporting.  We can
resolve most of the deadlock cases by closing the subdaemon socket and
allowing the subdaemon to gracefully shut down.
Fields are dropped in declaration order, so the first field is dropped
before the second.  This change ensures our connection to the peer is
always closed before we attempt to shut down the target.

This only matters for local mode -- when fuzzing in Nyx the snapshot is
always restored before the scenario object is dropped anway.  In local
mode, it avoids a shutdown deadlock for CLN where our open connection
prevents CLN from shutting down cleanly until connectd's keepalive ping
fails and causes CLN to drop the connection itself.
@morehouse
morehouse force-pushed the cln_coverage_deadlock branch from ed09485 to 6e62dce Compare September 18, 2026 17:43
@morehouse

Copy link
Copy Markdown
Collaborator Author

Thanks for pushing on this -- it made look harder at what was actually happening, and I think I've come up with a better approach now that avoids SIGKILL entirely like we originally did.

I couldn't replicate it locally. I've run it 200 times and didn't deadlock.

Yeah, sorry. That input reproduces the issue against CLN's master branch, but not the v26.06.6 we have pinned. I have better repro inputs below that work on v26.06.6.

There's actually two different deadlocks I ran into:

Subdaemon socket deadlock

A CLN subdaemon blocks waiting for input from it's connection to lightningd. lightningd blocks waiting for the subdaemon to shut down.

Fixed in the first commit by closing the subdaemon socket to allow a graceful shutdown.

Repro input:
deadlock_input1.tar.gz

Peer connection socket deadlock

A CLN subdaemon blocks waiting for input from its peer connection. lightningd blocks waiting for the subdaemon to shut down.

Fixed in the second commit by closing our peer connections with the target before tearing the target down.

Repro input:
deadlock_input2.tar.gz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants