Concurrent access is always dangerous, of course, but I have trouble seeing this being the case with this set unless something is modifying dangling pointers, which I think ought to result in less consistent crashes. The set ought to be populated by DF and then only consumed downstream, even if that happens to be in multiple threads (and I don't think that's happening).
Improper cache management can lead to issues where different threads won't get access to "the same" data, but I don't know how C(++) handles that, although my single change to "code" written in Java involved changing a variable to make it available to multiple threads (the weird default in this padded cell scripting language is to cut babies to pieces by razor sharp optimization corners in that department).
I've never dealt with exceptions in C(++) either, although I've got plenty of experience with it from a high level language (and thus those programs almost never crashed in the C fashion, but with error logs about unhandled exceptions, allowing us to at least know where to start looking).
It looked like the code blew up in std::distance rather than in our code, so instrumenting that code would presumably require replacing it with an instrumented version. I don't know if try/catch would work, although it might, in which case I'd expect it to be possible to provide some output of whatever data we can think of (while trying to avoid poking it where it blows up again).
I don't think more of dump of the same code would help, but rather outputs from instrumented code that either tries to catch the exception and output info about the state and/or output corresponding data at each visit to the code (it should be a little over once per world tile when it doesn't crash, but probably the first or one of the first times when it does. It seemed to be an UP movement, though, not a MUP, so it's a single step rather than a 10 tile step).
I think I bought my current computer a little less than 1½ years ago, and it's a fairly high end one (AMD Ryzen 9 3950X).
It's true we've learned a bit about what the problem isn't caused by.
I don't see much that can race in this scenario. You've got DF's main thread that's basically waiting for input (and hands it over to other threads), the DF display thread that just run in a cycle where the writing to the memory it uses has to be synchronized with it (doing it from the wrong parts of the script in Lua illustrates what can happen if you do it incorrectly). You've got the EA main thread that essentially takes the role of DF's main thread.
Assuming the program blows up in the EA thread (do we know it's there, rather than the thread being at that location when things blow up elsewhere?) it looks like the data received is somehow corrupt, which is uncomfortable as it's provided by DF rather than our code (I'd rather get egg on my face from screwing up than being able to say it wasn't my fault when the former means I can actually fix it, but the latter doesn't).
Edit:
I hooked up the debugger and looked at the key codes sent to the interpose code, and it turned out to be the keys the "keyboard" sent most of the time. The exception was when changing from one block of world tiles to the next one, in which case the "keyboard" key input was followed by a SETUP_LOCAL_Y_MUP followed by a set consisting of both a SETUP_LOCAL_Y_MUP and a SETUP_LOCAL_Y_MDOWN (in a 17*17 world). It doesn't really matter which of these codes you get, though, as they only serve to trigger updates of the overlay (although this indicates we have a superfluous update at every transition).
Edit 2: I just saw the PPS:
That's indeed during the initiation phase where the focus is moved to the first world tile. It can be noted that it's somewhat "lazy" in that it just keeps moving the cursor until it hits the corner, ignoring whether you're "overshooting" or not, as DF cuts it back to the world boundaries (which it has to do when dealing with players).
The end of the loop is likely just the address the call should return to, which is to be expected based on what I saw from the assembly: the addresses pointed to were the ones following calls, rather than the addresses from which the calls were made.
And I see exactly the same call sequence when I use a debugger for the first CURSOR_UPLEFT_FAST key (and, I assume, the following ones). I get a set of size one with that key as its single element.