🐛 Describe the bug
torchaudio.functional.forced_align (CPU) crashes the whole process with SIGSEGV when frames * (2 * target_length + 1) exceeds 2^31. This is reachable with realistic inputs: character-level CTC alignment (WhisperX-style) of a ~20-minute chunk of dense speech at 50 fps crosses the limit (we hit it in production aligning a German podcast).
Repro
# needs ~2.2 GB RAM; crashes the interpreter (run in a subprocess)
import torch, torchaudio
T, L, C = 70_000, 15_500, 30 # T*(2L+1) = 2.17e9 > 2^31
g = torch.Generator().manual_seed(0)
emission = torch.log_softmax(torch.rand((T, C), generator=g), dim=-1)
targets = torch.randint(1, C, (1, L), generator=g, dtype=torch.int32)
torchaudio.functional.forced_align(emission.unsqueeze(0), targets, blank=0)
# -> SIGSEGV / SIGBUS
Observed behaviour (torchaudio 2.11.0, macOS arm64):
T * (2L+1) |
targets dtype |
result |
| 2.17e9 (> 2^31) |
int32 |
💥 SIGSEGV |
| 2.17e9 (> 2^31) |
int64 |
💥 SIGSEGV (no dtype workaround) |
| 2.10e9 (< 2^31) |
int32 |
✅ correct result, ~8 s |
Root cause
src/libtorchaudio/forced_align/cpu/compute.cpp, the backPtr_a init loop uses a 32-bit loop index against a 64-bit trip count:
auto backPtr_a = new int8_t[T * S]; // T, S are int64_t -- allocation is fine
for (int i = 0; i < T * S; i++) { // int i overflows at 2^31 (UB)
backPtr_a[i] = -1;
}
Once i wraps to INT_MIN, the store lands at backPtr_a - 2^31 bytes. The macOS crash report confirms this exactly: fault address = buffer base − 0x80000000, as a byte write inside forced_align_impl<float, int>:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000892e94000
x0/x19 (buffer): 0x0000000912e94000 <- base - fault = 0x80000000
0 libtorchaudio.abi3.so forced_align_impl<float, int>(...) + 316
1 libtorchaudio.abi3.so torchaudio::alignment::cpu::compute(...) + 1384
This also explains why int64 targets crash identically — the dispatch only changes target_t, not the init loop. The alphas_a init loop two lines above uses the same int i < 2 * S idiom (not practically reachable today, but the same latent pattern).
The bug is platform-independent (plain C++ signed-overflow UB); it is present in current main (c0cbdb9) and at least back to the 2.8/2.11 releases.
Fix
One-liner: make the loop indices int64_t. PR incoming.
Versions
- torchaudio 2.11.0 (also inspected current main, c0cbdb9)
- torch 2.13.0
- Python 3.12.8, macOS 26.5 (arm64, M1 Max) — but the overflow is platform-independent
🐛 Describe the bug
torchaudio.functional.forced_align(CPU) crashes the whole process with SIGSEGV whenframes * (2 * target_length + 1)exceeds 2^31. This is reachable with realistic inputs: character-level CTC alignment (WhisperX-style) of a ~20-minute chunk of dense speech at 50 fps crosses the limit (we hit it in production aligning a German podcast).Repro
Observed behaviour (torchaudio 2.11.0, macOS arm64):
T * (2L+1)Root cause
src/libtorchaudio/forced_align/cpu/compute.cpp, thebackPtr_ainit loop uses a 32-bit loop index against a 64-bit trip count:Once
iwraps toINT_MIN, the store lands atbackPtr_a - 2^31bytes. The macOS crash report confirms this exactly: fault address = buffer base −0x80000000, as a byte write insideforced_align_impl<float, int>:This also explains why int64 targets crash identically — the dispatch only changes
target_t, not the init loop. Thealphas_ainit loop two lines above uses the sameint i < 2 * Sidiom (not practically reachable today, but the same latent pattern).The bug is platform-independent (plain C++ signed-overflow UB); it is present in current
main(c0cbdb9) and at least back to the 2.8/2.11 releases.Fix
One-liner: make the loop indices
int64_t. PR incoming.Versions