It's something to do with software rendering somewhere. Paused execution and got a backtrace after the freeze in gdb and I get this.
#0 R_DrawColumn_8 () at r_draw8.c:95#1 0x00005555556aa567 in R_DrawMaskedColumn ( column=column@entry=0x55555b528df8) at r_things.c:640#2 0x00005555556a2630 in R_DrawRepeatMaskedColumn (col=0x55555b528df8) at r_segs.c:710#3 0x00005555556a3cda in R_RenderThickSideRange (ds=<optimized out>, x1=<optimized out>, x2=1919, pfloor=0x55555b04345c) at r_segs.c:1220#4 0x00005555556ab8c1 in R_DrawMasked () at r_things.c:2253#5 0x000055555569edca in R_RenderPlayerView (player=0x555556b142a0 <players>) at r_main.c:1344#6 0x0000555555592fdf in D_Display () at d_main.c:397#7 0x0000555555593875 in D_Display () at d_main.c:598#8 D_SRB2Loop () at d_main.c:646#9 0x0000555555592a9d in main (argc=<optimized out>, argv=<optimized out>) at sdl/i_main.c:240
Hm okay, R_RenderThickSideRange is being called so it's an FOF wall, and R_DrawRepeatMaskedColumn is called by it only for single patch textures WITH holes. R_DrawRepeatMaskedColumn calls R_DrawMaskedColumn in a do while loop:
do { R_DrawMaskedColumn(col); sprtopscreen += dc_texheight*spryscale; } while (sprtopscreen < sprbotscreen);
If the freeze is occuring there, something must be preventing sprtopscreen from ever being >= sprbotscreen?