diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 0fe5472a56ed..f08ca058af35 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -766,7 +766,7 @@ static u64 route_score(struct amount_msat fee, */ score = (capacity_bias(global_gossmap, c, dir, total) + 1) * msat.millisatoshis; /* Raw: Weird math */ - if (score > 0xFFFFFFFF) + if (score != score || score > 0xFFFFFFFF) return 0xFFFFFFFF; /* Cast unnecessary, but be explicit! */ diff --git a/plugins/test/run-route-calc.c b/plugins/test/run-route-calc.c index a0bdda823448..9879cdbf408c 100644 --- a/plugins/test/run-route-calc.c +++ b/plugins/test/run-route-calc.c @@ -619,6 +619,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();