While analyzing and fixing
MDEV-18879 in 10.3, it occurred to me that the function row_ins_foreign_fill_virtual that was introduced in MariaDB 10.2.2 via merging MySQL 5.7.9, could be suffering from a similar bug.
The update->old_vrow is constructed with ROW_COPY_POINTERS, pointing to a buffer pool page frame. That page frame can become stale and invalid as soon as row_ins_foreign_check_on_constraint() invokes mtr_t::commit(). Most of the time, the pointer target is not overwritten by anything, and everything appears to work correctly. Buffer pool page replacement is highly unlikely, and any pessimistic operation that would overwrite the old location of the record is only slightly more likely.
In order to be on the safe side, I believe that the ROW_COPY_DATA mode should be used. This will slightly increase the memory usage.