plugins/test: Add a test to trigger the bug
What changed, and why it matters
This commit adds a test case that reproduces a bug in Core Lightning's payment routing code. When a payment amount is larger than a channel's capacity, the router should simply report 'no path found.' Instead, due to a precision issue with how channel capacity limits are stored, the router can compute an invalid 'NaN' (not-a-number) score and then try to convert that NaN into an ordinary unsigned integer. That conversion triggers a runtime/undefined-behavior error. The commit only adds the test; the actual fix is in a separate commit not shown here.
Locate and review the companion fix commit for route_score()/route() and the fp16_t capacity handling. Ensure the fix correctly discards channels whose effective capacity is below the payment amount, and that NaN scores cannot propagate into integer types. Run the new test under UBSan/ASan to confirm the bug is fixed.
Security signals we found
Undefined behavior triggered by NaN-to-unsigned-integer cast
Type precision loss in fp16_t capacity representation
Routing logic fails to discard insufficient-capacity channels
Regression test added for a known bug
Evidence from the diff
The change in plugins/test/run-route-calc.c introduces a regression test for a ‘NaN-cast’ bug in route_score(). The scenario creates a single channel with capacity AMOUNT_SAT(7875) and attempts to route AMOUNT_MSAT(7876357) across it. Because fp16_t cannot represent the capacity precisely, the channel is not discarded as it should be, route_score() produces NaN, and casting that NaN to u64 raises a UBSan error: ‘nan is outside the range of representable values of type unsigned long’. The test expects route() to return NULL with errmsg set to ‘No path found’. This commit is test-only and does not contain the fix.
Changed components
plugins/test/run-route-calc.croute() / route_score() routing functions (referenced, not patched)fp16_t capacity representation (referenced, not patched)Inspect captured patch +37 / −0
diff --git a/plugins/test/run-route-calc.c b/plugins/test/run-route-calc.c
index caca1ff6..25bb02d0 100644
--- a/plugins/test/run-route-calc.c
+++ b/plugins/test/run-route-calc.c
@@ -628,6 +628,43 @@ int main(int argc, char *argv[])
path[0].dir,
gossmap_find_chan(gossmap, &path[0].scid));
assert(dij[0].score == score);
+
+ /*
+ * Test for a 'NaN-cast' bug in route_score().
+ *
+ * This test reproduces a bug that occurs when attempting to
+ * route a payment whose amount exceeds the capacity of the channel
+ * it's routed through. The expected behavior is for route() to
+ * return NULL and set errmsg to "No path found".
+ *
+ * However, due to the imprecision of the htlc_max type (fp16_t), the
+ * channel is not correctly discarded. This causes the route's score
+ * to be calculated as NaN, and when this NaN is subsequently cast to
+ * a u64, it results in a runtime error.
+ *
+ * The expected UBSan error is:
+ * runtime error: nan is outside the range of representable values of type 'unsigned long'
+ */
+ add_connection(store_fd, 'X', 'Y',
+ /* base fee */ 40333,
+ /* prop fee */ 57981,
+ /* delay */ 138,
+ /* capacity */ AMOUNT_SAT(7875));
+
+ node_id('X', &src);
+ node_id('Y', &dst);
+
+ gossmap_refresh(global_gossmap);
+
+ r = route(tmpctx, gossmap,
+ gossmap_find_node(gossmap, &src),
+ gossmap_find_node(gossmap, &dst),
+ /* amount */ AMOUNT_MSAT(7876357),
+ /* Final delay */ 2655,
+ /* riskfactor */ 57.00,
+ /* Max hops */ ROUTING_MAX_HOPS,
+ /* payment */ p,
+ &errmsg);
}
common_shutdown();
Why this scored 56/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.