The real problem is being able to branch in to the middle of a JIT module. It should only be possible to call the beginning of a top-level function.
One way to ensure that (though it may come with unacceptable overhead) would be to initially mark the pages containing the JIT code as non-executable, and check the exact address causing the exception whenever something branches to it. The page would need to be marked non-executable again when the JIT code is finished, and there would be a small window of opportunity while the code is executing.
The other option, of course, is to run JIT code in one or more dedicated, high-priority user-mode threads rather than calling it directly from kernel mode. Naturally, this would add the overhead of two context switches to each JIT call, which may also be unacceptable.