If a plan has a limit, the coordinator will eventually set Coordinator::returned_all_results_ once the limit has been hit. At this point, it should start to cancel fragment instances that are still running. This happens usually either through an explicit cancel RPC, or returning a non-OK status to the heartbeat ReportExecStatus() RPC. In the limit case, neither happen - the query status is not set to !ok() (because the query succeeded!), so there's no 'bad' status to propagate to the fragment instance.
In many cases this doesn't matter because the cancellation propagates from the top down: the root instance will get closed and go away, and then any senders to that instance will notice and cancel themselves, and so on. But there are plan shapes that mean a lot of CPU time is wasted after the query should have finished, e.g.:
This convoluted query illustrates the idea: table l is the left union child, and gets evaluated first. It produces more than two rows, so the limit gets hit. The right child, in the meantime, is evaluating the cross join before the aggregation, which is very cpu heavy. When the limit is hit, the query hangs (from the client's perspective), waiting for the right child to produce no results.
The fix for this is easy: fragment instances should learn about query termination from ReportExecStatus() RPCs. If results_returned_ is true, the coordinator should return a non-OK status, causing instance tear-down next time the instance checks its cancellation state.